summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig16
-rw-r--r--drivers/leds/Makefile5
-rw-r--r--drivers/leds/led-core.c22
-rw-r--r--drivers/leds/leds-aw200xx.c2
-rw-r--r--drivers/leds/leds-lp8860.c2
-rw-r--r--drivers/leds/leds-max77705.c275
-rw-r--r--drivers/leds/leds-mlxcpld.c1
-rw-r--r--drivers/leds/leds-nic78bx.c16
-rw-r--r--drivers/leds/leds-pca955x.c359
-rw-r--r--drivers/leds/leds-st1202.c42
-rw-r--r--drivers/leds/rgb/leds-pwm-multicolor.c5
-rw-r--r--drivers/leds/rgb/leds-qcom-lpg.c23
-rw-r--r--drivers/leds/simatic/Kconfig (renamed from drivers/leds/simple/Kconfig)0
-rw-r--r--drivers/leds/simatic/Makefile (renamed from drivers/leds/simple/Makefile)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c (renamed from drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-core.c (renamed from drivers/leds/simple/simatic-ipc-leds-gpio-core.c)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c (renamed from drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c (renamed from drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio.h (renamed from drivers/leds/simple/simatic-ipc-leds-gpio.h)0
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds.c (renamed from drivers/leds/simple/simatic-ipc-leds.c)0
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c16
-rw-r--r--drivers/leds/trigger/ledtrig-pattern.c4
22 files changed, 621 insertions, 167 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 2b27d043921c..a104cbb0a001 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -778,6 +778,14 @@ config LEDS_MAX77650
help
LEDs driver for MAX77650 family of PMICs from Maxim Integrated.
+config LEDS_MAX77705
+ tristate "LED support for Maxim MAX77705 PMIC"
+ depends on MFD_MAX77705
+ depends on LEDS_CLASS
+ depends on LEDS_CLASS_MULTICOLOR
+ help
+ LED driver for MAX77705 PMIC from Maxim Integrated.
+
config LEDS_MAX8997
tristate "LED support for MAX8997 PMIC"
depends on LEDS_CLASS && MFD_MAX8997
@@ -924,7 +932,8 @@ config LEDS_USER
config LEDS_NIC78BX
tristate "LED support for NI PXI NIC78bx devices"
depends on LEDS_CLASS
- depends on X86 && ACPI
+ depends on HAS_IOPORT
+ depends on X86 || COMPILE_TEST
help
This option enables support for the User1 and User2 LEDs on NI
PXI NIC78bx devices.
@@ -971,6 +980,7 @@ config LEDS_ST1202
depends on I2C
depends on OF
select LEDS_TRIGGERS
+ select LEDS_TRIGGER_PATTERN
help
Say Y to enable support for LEDs connected to LED1202
LED driver chips accessed via the I2C bus.
@@ -1014,7 +1024,7 @@ source "drivers/leds/rgb/Kconfig"
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
-comment "Simple LED drivers"
-source "drivers/leds/simple/Kconfig"
+comment "Simatic LED drivers"
+source "drivers/leds/simatic/Kconfig"
endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6ad52e219ec6..2f170d69dcbf 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
+obj-$(CONFIG_LEDS_MAX77705) += leds-max77705.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
@@ -121,5 +122,5 @@ obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
# LED Blink
obj-y += blink/
-# Simple LED drivers
-obj-y += simple/
+# Simatic LED drivers
+obj-y += simatic/
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index f6c46d2e5276..e3d8ddcff567 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -159,8 +159,19 @@ static void set_brightness_delayed(struct work_struct *ws)
* before this work item runs once. To make sure this works properly
* handle LED_SET_BRIGHTNESS_OFF first.
*/
- if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags))
+ if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags)) {
set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
+ /*
+ * The consecutives led_set_brightness(LED_OFF),
+ * led_set_brightness(LED_FULL) could have been executed out of
+ * order (LED_FULL first), if the work_flags has been set
+ * between LED_SET_BRIGHTNESS_OFF and LED_SET_BRIGHTNESS of this
+ * work. To avoid ending with the LED turned off, turn the LED
+ * on again.
+ */
+ if (led_cdev->delayed_set_value != LED_OFF)
+ set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
+ }
if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
@@ -331,10 +342,13 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
* change is done immediately afterwards (before the work runs),
* it uses a separate work_flag.
*/
- if (value) {
- led_cdev->delayed_set_value = value;
+ led_cdev->delayed_set_value = value;
+ /* Ensure delayed_set_value is seen before work_flags modification */
+ smp_mb__before_atomic();
+
+ if (value)
set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
- } else {
+ else {
clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c
index 08cca128458c..fe223d363a5d 100644
--- a/drivers/leds/leds-aw200xx.c
+++ b/drivers/leds/leds-aw200xx.c
@@ -379,7 +379,7 @@ static void aw200xx_enable(const struct aw200xx *const chip)
static void aw200xx_disable(const struct aw200xx *const chip)
{
- return gpiod_set_value_cansleep(chip->hwen, 0);
+ gpiod_set_value_cansleep(chip->hwen, 0);
}
static int aw200xx_probe_get_display_rows(struct device *dev,
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c
index 06196d851ade..995f2adf8569 100644
--- a/drivers/leds/leds-lp8860.c
+++ b/drivers/leds/leds-lp8860.c
@@ -331,7 +331,6 @@ static const struct regmap_config lp8860_regmap_config = {
.max_register = LP8860_EEPROM_UNLOCK,
.reg_defaults = lp8860_reg_defs,
.num_reg_defaults = ARRAY_SIZE(lp8860_reg_defs),
- .cache_type = REGCACHE_NONE,
};
static const struct reg_default lp8860_eeprom_defs[] = {
@@ -369,7 +368,6 @@ static const struct regmap_config lp8860_eeprom_regmap_config = {
.max_register = LP8860_EEPROM_REG_24,
.reg_defaults = lp8860_eeprom_defs,
.num_reg_defaults = ARRAY_SIZE(lp8860_eeprom_defs),
- .cache_type = REGCACHE_NONE,
};
static int lp8860_probe(struct i2c_client *client)
diff --git a/drivers/leds/leds-max77705.c b/drivers/leds/leds-max77705.c
new file mode 100644
index 000000000000..933cb4f19be9
--- /dev/null
+++ b/drivers/leds/leds-max77705.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on leds-max77650 driver
+ *
+ * LED driver for MAXIM 77705 PMIC.
+ * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org>
+ */
+
+#include <linux/i2c.h>
+#include <linux/led-class-multicolor.h>
+#include <linux/leds.h>
+#include <linux/mfd/max77705-private.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MAX77705_LED_NUM_LEDS 4
+#define MAX77705_LED_EN_MASK GENMASK(1, 0)
+#define MAX77705_LED_MAX_BRIGHTNESS 0xff
+#define MAX77705_LED_EN_SHIFT(reg) (reg * MAX77705_RGBLED_EN_WIDTH)
+#define MAX77705_LED_REG_BRIGHTNESS(reg) (reg + MAX77705_RGBLED_REG_LED0BRT)
+
+struct max77705_led {
+ struct led_classdev cdev;
+ struct led_classdev_mc mcdev;
+ struct regmap *regmap;
+
+ struct mc_subled *subled_info;
+};
+
+static const struct regmap_config max77705_leds_regmap_config = {
+ .reg_base = MAX77705_RGBLED_REG_BASE,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77705_LED_REG_END,
+};
+
+static int max77705_rgb_blink(struct led_classdev *cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct max77705_led *led = container_of(cdev, struct max77705_led, cdev);
+ int value, on_value, off_value;
+
+ if (*delay_on < MAX77705_RGB_DELAY_100_STEP)
+ on_value = 0;
+ else if (*delay_on < MAX77705_RGB_DELAY_100_STEP_LIM)
+ on_value = *delay_on / MAX77705_RGB_DELAY_100_STEP - 1;
+ else if (*delay_on < MAX77705_RGB_DELAY_250_STEP_LIM)
+ on_value = (*delay_on - MAX77705_RGB_DELAY_100_STEP_LIM) /
+ MAX77705_RGB_DELAY_250_STEP +
+ MAX77705_RGB_DELAY_100_STEP_COUNT;
+ else
+ on_value = 15;
+
+ on_value <<= 4;
+
+ if (*delay_off < 1)
+ off_value = 0;
+ else if (*delay_off < MAX77705_RGB_DELAY_500_STEP)
+ off_value = 1;
+ else if (*delay_off < MAX77705_RGB_DELAY_500_STEP_LIM)
+ off_value = *delay_off / MAX77705_RGB_DELAY_500_STEP;
+ else if (*delay_off < MAX77705_RGB_DELAY_1000_STEP_LIM)
+ off_value = (*delay_off - MAX77705_RGB_DELAY_1000_STEP_LIM) /
+ MAX77705_RGB_DELAY_1000_STEP +
+ MAX77705_RGB_DELAY_500_STEP_COUNT;
+ else if (*delay_off < MAX77705_RGB_DELAY_2000_STEP_LIM)
+ off_value = (*delay_off - MAX77705_RGB_DELAY_2000_STEP_LIM) /
+ MAX77705_RGB_DELAY_2000_STEP +
+ MAX77705_RGB_DELAY_1000_STEP_COUNT;
+ else
+ off_value = 15;
+
+ value = on_value | off_value;
+ return regmap_write(led->regmap, MAX77705_RGBLED_REG_LEDBLNK, value);
+}
+
+static int max77705_led_brightness_set(struct regmap *regmap, struct mc_subled *subled,
+ int num_colors)
+{
+ int ret;
+
+ for (int i = 0; i < num_colors; i++) {
+ unsigned int channel, brightness;
+
+ channel = subled[i].channel;
+ brightness = subled[i].brightness;
+
+ if (brightness == LED_OFF) {
+ /* Flash OFF */
+ ret = regmap_update_bits(regmap,
+ MAX77705_RGBLED_REG_LEDEN,
+ MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel), 0);
+ } else {
+ /* Set current */
+ ret = regmap_write(regmap, MAX77705_LED_REG_BRIGHTNESS(channel),
+ brightness);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(regmap,
+ MAX77705_RGBLED_REG_LEDEN,
+ LED_ON << MAX77705_LED_EN_SHIFT(channel),
+ MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel));
+ }
+ }
+
+ return ret;
+}
+
+static int max77705_led_brightness_set_single(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct max77705_led *led = container_of(cdev, struct max77705_led, cdev);
+
+ led->subled_info->brightness = brightness;
+
+ return max77705_led_brightness_set(led->regmap, led->subled_info, 1);
+}
+
+static int max77705_led_brightness_set_multi(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct led_classdev_mc *mcdev = lcdev_to_mccdev(cdev);
+ struct max77705_led *led = container_of(mcdev, struct max77705_led, mcdev);
+
+ led_mc_calc_color_components(mcdev, brightness);
+
+ return max77705_led_brightness_set(led->regmap, led->mcdev.subled_info, mcdev->num_colors);
+}
+
+static int max77705_parse_subled(struct device *dev, struct fwnode_handle *np,
+ struct mc_subled *info)
+{
+ u32 color = LED_COLOR_ID_GREEN;
+ u32 reg;
+ int ret;
+
+ ret = fwnode_property_read_u32(np, "reg", &reg);
+ if (ret || !reg || reg >= MAX77705_LED_NUM_LEDS)
+ return dev_err_probe(dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np);
+
+ info->channel = reg;
+
+ ret = fwnode_property_read_u32(np, "color", &color);
+ if (ret < 0 && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "failed to parse \"color\" of %pOF\n", np);
+
+ info->color_index = color;
+
+ return 0;
+}
+
+static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fwnode_handle *np)
+{
+ int ret, i = 0;
+ unsigned int color, reg;
+ struct max77705_led *led;
+ struct led_classdev *cdev;
+ struct mc_subled *info;
+ struct fwnode_handle *child;
+ struct led_init_data init_data = {};
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32(np, "color", &color);
+ if (ret < 0 && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "failed to parse \"color\" of %pOF\n", np);
+
+ led->regmap = regmap;
+ init_data.fwnode = np;
+
+ if (color == LED_COLOR_ID_RGB) {
+ int num_channels = of_get_available_child_count(to_of_node(np));
+
+ ret = fwnode_property_read_u32(np, "reg", &reg);
+ if (ret || reg >= MAX77705_LED_NUM_LEDS)
+ ret = -EINVAL;
+
+ info = devm_kcalloc(dev, num_channels, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ cdev = &led->mcdev.led_cdev;
+ cdev->max_brightness = MAX77705_LED_MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = max77705_led_brightness_set_multi;
+ cdev->blink_set = max77705_rgb_blink;
+
+ fwnode_for_each_available_child_node(np, child) {
+ ret = max77705_parse_subled(dev, child, &info[i]);
+ if (ret < 0)
+ return ret;
+
+ info[i].intensity = 0;
+ i++;
+ }
+
+ led->mcdev.subled_info = info;
+ led->mcdev.num_colors = num_channels;
+ led->cdev = *cdev;
+
+ ret = devm_led_classdev_multicolor_register_ext(dev, &led->mcdev, &init_data);
+ if (ret)
+ return ret;
+
+ ret = max77705_led_brightness_set_multi(&led->cdev, LED_OFF);
+ if (ret)
+ return ret;
+ } else {
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ max77705_parse_subled(dev, np, info);
+
+ led->subled_info = info;
+ led->cdev.brightness_set_blocking = max77705_led_brightness_set_single;
+ led->cdev.blink_set = max77705_rgb_blink;
+ led->cdev.max_brightness = MAX77705_LED_MAX_BRIGHTNESS;
+
+ ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
+ if (ret)
+ return ret;
+
+ ret = max77705_led_brightness_set_single(&led->cdev, LED_OFF);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77705_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct i2c_client *i2c = to_i2c_client(pdev->dev.parent);
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &max77705_leds_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register LEDs regmap\n");
+
+ device_for_each_child_node_scoped(dev, child) {
+ ret = max77705_add_led(dev, regmap, child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id max77705_led_of_match[] = {
+ { .compatible = "maxim,max77705-rgb" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max77705_led_of_match);
+
+static struct platform_driver max77705_led_driver = {
+ .driver = {
+ .name = "max77705-led",
+ .of_match_table = max77705_led_of_match,
+ },
+ .probe = max77705_led_probe,
+};
+module_platform_driver(max77705_led_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77705 LED driver");
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c
index 718f55096e90..f25f68789281 100644
--- a/drivers/leds/leds-mlxcpld.c
+++ b/drivers/leds/leds-mlxcpld.c
@@ -32,7 +32,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
diff --git a/drivers/leds/leds-nic78bx.c b/drivers/leds/leds-nic78bx.c
index 282d9e4cf116..f3161266b8ad 100644
--- a/drivers/leds/leds-nic78bx.c
+++ b/drivers/leds/leds-nic78bx.c
@@ -3,11 +3,19 @@
* Copyright (C) 2016 National Instruments Corp.
*/
-#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
+#include <linux/types.h>
#define NIC78BX_USER1_LED_MASK 0x3
#define NIC78BX_USER1_GREEN_LED BIT(0)
@@ -181,8 +189,8 @@ static int nic78bx_probe(struct platform_device *pdev)
}
static const struct acpi_device_id led_device_ids[] = {
- {"NIC78B3", 0},
- {"", 0},
+ { "NIC78B3" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, led_device_ids);
@@ -190,7 +198,7 @@ static struct platform_driver led_driver = {
.probe = nic78bx_probe,
.driver = {
.name = KBUILD_MODNAME,
- .acpi_match_table = ACPI_PTR(led_device_ids),
+ .acpi_match_table = led_device_ids,
},
};
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 94a9f8a54b35..e9cfde9fe4b1 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -62,6 +62,8 @@
#define PCA955X_GPIO_HIGH LED_OFF
#define PCA955X_GPIO_LOW LED_FULL
+#define PCA955X_BLINK_DEFAULT_MS 1000
+
enum pca955x_type {
pca9550,
pca9551,
@@ -74,6 +76,7 @@ struct pca955x_chipdef {
int bits;
u8 slv_addr; /* 7-bit slave address mask */
int slv_addr_shift; /* Number of bits to ignore */
+ int blink_div; /* PSC divider */
};
static const struct pca955x_chipdef pca955x_chipdefs[] = {
@@ -81,26 +84,31 @@ static const struct pca955x_chipdef pca955x_chipdefs[] = {
.bits = 2,
.slv_addr = /* 110000x */ 0x60,
.slv_addr_shift = 1,
+ .blink_div = 44,
},
[pca9551] = {
.bits = 8,
.slv_addr = /* 1100xxx */ 0x60,
.slv_addr_shift = 3,
+ .blink_div = 38,
},
[pca9552] = {
.bits = 16,
.slv_addr = /* 1100xxx */ 0x60,
.slv_addr_shift = 3,
+ .blink_div = 44,
},
[ibm_pca9552] = {
.bits = 16,
.slv_addr = /* 0110xxx */ 0x30,
.slv_addr_shift = 3,
+ .blink_div = 44,
},
[pca9553] = {
.bits = 4,
.slv_addr = /* 110001x */ 0x62,
.slv_addr_shift = 1,
+ .blink_div = 44,
},
};
@@ -109,7 +117,9 @@ struct pca955x {
struct pca955x_led *leds;
const struct pca955x_chipdef *chipdef;
struct i2c_client *client;
+ unsigned long active_blink;
unsigned long active_pins;
+ unsigned long blink_period;
#ifdef CONFIG_LEDS_PCA955X_GPIO
struct gpio_chip gpio;
#endif
@@ -124,17 +134,25 @@ struct pca955x_led {
struct fwnode_handle *fwnode;
};
+#define led_to_pca955x(l) container_of(l, struct pca955x_led, led_cdev)
+
struct pca955x_platform_data {
struct pca955x_led *leds;
int num_leds;
};
/* 8 bits per input register */
-static inline int pca95xx_num_input_regs(int bits)
+static inline int pca955x_num_input_regs(int bits)
{
return (bits + 7) / 8;
}
+/* 4 bits per LED selector register */
+static inline int pca955x_num_led_regs(int bits)
+{
+ return (bits + 3) / 4;
+}
+
/*
* Return an LED selector register value based on an existing one, with
* the appropriate 2-bit state value set for the given LED number (0-3).
@@ -145,20 +163,25 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
((state & 0x3) << (led_num << 1));
}
+static inline int pca955x_ledstate(u8 ls, int led_num)
+{
+ return (ls >> (led_num << 1)) & 0x3;
+}
+
/*
* Write to frequency prescaler register, used to program the
- * period of the PWM output. period = (PSCx + 1) / 38
+ * period of the PWM output. period = (PSCx + 1) / coeff
+ * Where for pca9551 chips coeff = 38 and for all other chips coeff = 44
*/
-static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_psc(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + (2 * n);
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
+ val, ret);
return ret;
}
@@ -169,16 +192,15 @@ static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
*
* Duty cycle is (256 - PWMx) / 256
*/
-static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_pwm(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
+ val, ret);
return ret;
}
@@ -186,16 +208,15 @@ static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
* Write to LED selector register, which determines the source that
* drives the LED output.
*/
-static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)
+static int pca955x_write_ls(struct pca955x *pca955x, int n, u8 val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n;
int ret;
- ret = i2c_smbus_write_byte_data(client, cmd, val);
+ ret = i2c_smbus_write_byte_data(pca955x->client, cmd, val);
if (ret < 0)
- dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
- __func__, n, val, ret);
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n,
+ val, ret);
return ret;
}
@@ -203,32 +224,43 @@ static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)
* Read the LED selector register, which determines the source that
* drives the LED output.
*/
-static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)
+static int pca955x_read_ls(struct pca955x *pca955x, int n, u8 *val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 4 + n;
int ret;
- ret = i2c_smbus_read_byte_data(client, cmd);
+ ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
if (ret < 0) {
- dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
- __func__, n, ret);
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret);
return ret;
}
*val = (u8)ret;
return 0;
}
-static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val)
+static int pca955x_read_pwm(struct pca955x *pca955x, int n, u8 *val)
{
- struct pca955x *pca955x = i2c_get_clientdata(client);
- u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
+ u8 cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);
int ret;
- ret = i2c_smbus_read_byte_data(client, cmd);
+ ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
if (ret < 0) {
- dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
- __func__, n, ret);
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret);
+ return ret;
+ }
+ *val = (u8)ret;
+ return 0;
+}
+
+static int pca955x_read_psc(struct pca955x *pca955x, int n, u8 *val)
+{
+ int ret;
+ u8 cmd;
+
+ cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + (2 * n);
+ ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
+ if (ret < 0) {
+ dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret);
return ret;
}
*val = (u8)ret;
@@ -237,30 +269,25 @@ static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val)
static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
{
- struct pca955x_led *pca955x_led = container_of(led_cdev,
- struct pca955x_led,
- led_cdev);
+ struct pca955x_led *pca955x_led = led_to_pca955x(led_cdev);
struct pca955x *pca955x = pca955x_led->pca955x;
u8 ls, pwm;
int ret;
- ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls);
+ ret = pca955x_read_ls(pca955x, pca955x_led->led_num / 4, &ls);
if (ret)
return ret;
- ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3;
- switch (ls) {
+ switch (pca955x_ledstate(ls, pca955x_led->led_num % 4)) {
case PCA955X_LS_LED_ON:
+ case PCA955X_LS_BLINK0:
ret = LED_FULL;
break;
case PCA955X_LS_LED_OFF:
ret = LED_OFF;
break;
- case PCA955X_LS_BLINK0:
- ret = LED_HALF;
- break;
case PCA955X_LS_BLINK1:
- ret = pca955x_read_pwm(pca955x->client, 1, &pwm);
+ ret = pca955x_read_pwm(pca955x, 1, &pwm);
if (ret)
return ret;
ret = 255 - pwm;
@@ -273,51 +300,150 @@ static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
static int pca955x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct pca955x_led *pca955x_led;
- struct pca955x *pca955x;
+ struct pca955x_led *pca955x_led = led_to_pca955x(led_cdev);
+ struct pca955x *pca955x = pca955x_led->pca955x;
+ int reg = pca955x_led->led_num / 4;
+ int bit = pca955x_led->led_num % 4;
u8 ls;
- int chip_ls; /* which LSx to use (0-3 potentially) */
- int ls_led; /* which set of bits within LSx to use (0-3) */
int ret;
- pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);
- pca955x = pca955x_led->pca955x;
-
- chip_ls = pca955x_led->led_num / 4;
- ls_led = pca955x_led->led_num % 4;
-
mutex_lock(&pca955x->lock);
- ret = pca955x_read_ls(pca955x->client, chip_ls, &ls);
+ ret = pca955x_read_ls(pca955x, reg, &ls);
if (ret)
goto out;
- switch (value) {
- case LED_FULL:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
- break;
- case LED_OFF:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF);
- break;
- case LED_HALF:
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0);
- break;
- default:
- /*
- * Use PWM1 for all other values. This has the unwanted
- * side effect of making all LEDs on the chip share the
- * same brightness level if set to a value other than
- * OFF, HALF, or FULL. But, this is probably better than
- * just turning off for all other values.
- */
- ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);
- if (ret)
+ if (test_bit(pca955x_led->led_num, &pca955x->active_blink)) {
+ if (value == LED_OFF) {
+ clear_bit(pca955x_led->led_num, &pca955x->active_blink);
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
+ } else {
+ /* No variable brightness for blinking LEDs */
goto out;
- ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
- break;
+ }
+ } else {
+ switch (value) {
+ case LED_FULL:
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_ON);
+ break;
+ case LED_OFF:
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
+ break;
+ default:
+ /*
+ * Use PWM1 for all other values. This has the unwanted
+ * side effect of making all LEDs on the chip share the
+ * same brightness level if set to a value other than
+ * OFF or FULL. But, this is probably better than just
+ * turning off for all other values.
+ */
+ ret = pca955x_write_pwm(pca955x, 1, 255 - value);
+ if (ret)
+ goto out;
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK1);
+ break;
+ }
}
- ret = pca955x_write_ls(pca955x->client, chip_ls, ls);
+ ret = pca955x_write_ls(pca955x, reg, ls);
+
+out:
+ mutex_unlock(&pca955x->lock);
+
+ return ret;
+}
+
+static u8 pca955x_period_to_psc(struct pca955x *pca955x, unsigned long period)
+{
+ /* psc register value = (blink period * coeff) - 1 */
+ period *= pca955x->chipdef->blink_div;
+ period /= MSEC_PER_SEC;
+ period -= 1;
+
+ return period;
+}
+
+static unsigned long pca955x_psc_to_period(struct pca955x *pca955x, u8 psc)
+{
+ unsigned long period = psc;
+
+ /* blink period = (psc register value + 1) / coeff */
+ period += 1;
+ period *= MSEC_PER_SEC;
+ period /= pca955x->chipdef->blink_div;
+
+ return period;
+}
+
+static int pca955x_led_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct pca955x_led *pca955x_led = led_to_pca955x(led_cdev);
+ struct pca955x *pca955x = pca955x_led->pca955x;
+ unsigned long period = *delay_on + *delay_off;
+ int ret = 0;
+
+ mutex_lock(&pca955x->lock);
+
+ if (period) {
+ if (*delay_on != *delay_off) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (period < pca955x_psc_to_period(pca955x, 0) ||
+ period > pca955x_psc_to_period(pca955x, 0xff)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ } else {
+ period = pca955x->active_blink ? pca955x->blink_period :
+ PCA955X_BLINK_DEFAULT_MS;
+ }
+
+ if (!pca955x->active_blink ||
+ pca955x->active_blink == BIT(pca955x_led->led_num) ||
+ pca955x->blink_period == period) {
+ u8 psc = pca955x_period_to_psc(pca955x, period);
+
+ if (!test_and_set_bit(pca955x_led->led_num,
+ &pca955x->active_blink)) {
+ u8 ls;
+ int reg = pca955x_led->led_num / 4;
+ int bit = pca955x_led->led_num % 4;
+
+ ret = pca955x_read_ls(pca955x, reg, &ls);
+ if (ret)
+ goto out;
+
+ ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK0);
+ ret = pca955x_write_ls(pca955x, reg, ls);
+ if (ret)
+ goto out;
+
+ /*
+ * Force 50% duty cycle to maintain the specified
+ * blink rate.
+ */
+ ret = pca955x_write_pwm(pca955x, 0, 128);
+ if (ret)
+ goto out;
+ }
+
+ if (pca955x->blink_period != period) {
+ pca955x->blink_period = period;
+ ret = pca955x_write_psc(pca955x, 0, psc);
+ if (ret)
+ goto out;
+ }
+
+ period = pca955x_psc_to_period(pca955x, psc);
+ period /= 2;
+ *delay_on = period;
+ *delay_off = period;
+ } else {
+ ret = -EBUSY;
+ }
out:
mutex_unlock(&pca955x->lock);
@@ -455,10 +581,13 @@ static int pca955x_probe(struct i2c_client *client)
struct led_classdev *led;
struct led_init_data init_data;
struct i2c_adapter *adapter;
- int i, err;
+ int i, bit, err, nls, reg;
+ u8 ls1[4];
+ u8 ls2[4];
struct pca955x_platform_data *pdata;
+ u8 psc0;
+ bool keep_psc0 = false;
bool set_default_label = false;
- bool keep_pwm = false;
char default_label[8];
chip = i2c_get_match_data(client);
@@ -509,10 +638,22 @@ static int pca955x_probe(struct i2c_client *client)
mutex_init(&pca955x->lock);
pca955x->client = client;
pca955x->chipdef = chip;
+ pca955x->blink_period = PCA955X_BLINK_DEFAULT_MS;
init_data.devname_mandatory = false;
init_data.devicename = "pca955x";
+ nls = pca955x_num_led_regs(chip->bits);
+ /* Use auto-increment feature to read all the LED selectors at once. */
+ err = i2c_smbus_read_i2c_block_data(client,
+ 0x10 | (pca955x_num_input_regs(chip->bits) + 4), nls,
+ ls1);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < nls; i++)
+ ls2[i] = ls1[i];
+
for (i = 0; i < chip->bits; i++) {
pca955x_led = &pca955x->leds[i];
pca955x_led->led_num = i;
@@ -524,18 +665,20 @@ static int pca955x_probe(struct i2c_client *client)
case PCA955X_TYPE_GPIO:
break;
case PCA955X_TYPE_LED:
+ bit = i % 4;
+ reg = i / 4;
led = &pca955x_led->led_cdev;
led->brightness_set_blocking = pca955x_led_set;
led->brightness_get = pca955x_led_get;
-
- if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF) {
- err = pca955x_led_set(led, LED_OFF);
- if (err)
- return err;
- } else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON) {
- err = pca955x_led_set(led, LED_FULL);
- if (err)
- return err;
+ led->blink_set = pca955x_led_blink;
+
+ if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF)
+ ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_OFF);
+ else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON)
+ ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_ON);
+ else if (pca955x_ledstate(ls2[reg], bit) == PCA955X_LS_BLINK0) {
+ keep_psc0 = true;
+ set_bit(i, &pca955x->active_blink);
}
init_data.fwnode = pdata->leds[i].fwnode;
@@ -564,39 +707,31 @@ static int pca955x_probe(struct i2c_client *client)
return err;
set_bit(i, &pca955x->active_pins);
-
- /*
- * For default-state == "keep", let the core update the
- * brightness from the hardware, then check the
- * brightness to see if it's using PWM1. If so, PWM1
- * should not be written below.
- */
- if (pdata->leds[i].default_state == LEDS_DEFSTATE_KEEP) {
- if (led->brightness != LED_FULL &&
- led->brightness != LED_OFF &&
- led->brightness != LED_HALF)
- keep_pwm = true;
- }
}
}
- /* PWM0 is used for half brightness or 50% duty cycle */
- err = pca955x_write_pwm(client, 0, 255 - LED_HALF);
- if (err)
- return err;
+ for (i = 0; i < nls; i++) {
+ if (ls1[i] != ls2[i]) {
+ err = pca955x_write_ls(pca955x, i, ls2[i]);
+ if (err)
+ return err;
+ }
+ }
- if (!keep_pwm) {
- /* PWM1 is used for variable brightness, default to OFF */
- err = pca955x_write_pwm(client, 1, 0);
- if (err)
- return err;
+ if (keep_psc0) {
+ err = pca955x_read_psc(pca955x, 0, &psc0);
+ } else {
+ psc0 = pca955x_period_to_psc(pca955x, pca955x->blink_period);
+ err = pca955x_write_psc(pca955x, 0, psc0);
}
- /* Set to fast frequency so we do not see flashing */
- err = pca955x_write_psc(client, 0, 0);
if (err)
return err;
- err = pca955x_write_psc(client, 1, 0);
+
+ pca955x->blink_period = pca955x_psc_to_period(pca955x, psc0);
+
+ /* Set PWM1 to fast frequency so we do not see flashing */
+ err = pca955x_write_psc(pca955x, 1, 0);
if (err)
return err;
diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index b691c4886993..4e5dd76d714d 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -99,9 +99,9 @@ static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
value_h = (u8)(value >> 8);
/*
- * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh),
- * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh)
- * and y is the pattern number in hexadecimal (y = 00h .. 07h)
+ * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh),
+ * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh)
+ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
*/
ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + (led_num * 2) + 0x18 * pattern),
value_l);
@@ -189,9 +189,8 @@ static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active
static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
{
struct st1202_led *led = cdev_to_st1202_led(ldev);
- struct st1202_chip *chip = led->chip;
- return st1202_channel_set(chip, led->led_num, value == LED_OFF ? false : true);
+ return st1202_channel_set(led->chip, led->led_num, !!value);
}
static int st1202_led_pattern_clear(struct led_classdev *ldev)
@@ -261,8 +260,6 @@ static int st1202_dt_init(struct st1202_chip *chip)
int err, reg;
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
- struct led_init_data init_data = {};
-
err = of_property_read_u32(child, "reg", &reg);
if (err)
return dev_err_probe(dev, err, "Invalid register\n");
@@ -276,15 +273,6 @@ static int st1202_dt_init(struct st1202_chip *chip)
led->led_cdev.pattern_set = st1202_led_pattern_set;
led->led_cdev.pattern_clear = st1202_led_pattern_clear;
led->led_cdev.default_trigger = "pattern";
-
- init_data.fwnode = led->fwnode;
- init_data.devicename = "st1202";
- init_data.default_label = ":";
-
- err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
- if (err < 0)
- return dev_err_probe(dev, err, "Failed to register LED class device\n");
-
led->led_cdev.brightness_set = st1202_brightness_set;
led->led_cdev.brightness_get = st1202_brightness_get;
}
@@ -299,8 +287,8 @@ static int st1202_setup(struct st1202_chip *chip)
guard(mutex)(&chip->lock);
/*
- * Once the supply voltage is applied, the LED1202 executes some internal checks,
- * afterwords it stops the oscillator and puts the internal LDO in quiescent mode.
+ * Once the supply voltage is applied, the LED1202 executes some internal checks.
+ * Afterwards, it stops the oscillator and puts the internal LDO in quiescent mode.
* To start the device, EN bit must be set inside the “Device Enable” register at
* address 01h. As soon as EN is set, the LED1202 loads the adjustment parameters
* from the internal non-volatile memory and performs an auto-calibration procedure
@@ -356,18 +344,21 @@ static int st1202_probe(struct i2c_client *client)
if (!chip)
return -ENOMEM;
- devm_mutex_init(&client->dev, &chip->lock);
+ ret = devm_mutex_init(&client->dev, &chip->lock);
+ if (ret < 0)
+ return ret;
chip->client = client;
- ret = st1202_dt_init(chip);
+ ret = st1202_setup(chip);
if (ret < 0)
return ret;
- ret = st1202_setup(chip);
+ ret = st1202_dt_init(chip);
if (ret < 0)
return ret;
for (int i = 0; i < ST1202_MAX_LEDS; i++) {
+ struct led_init_data init_data = {};
led = &chip->leds[i];
led->chip = chip;
led->led_num = i;
@@ -384,6 +375,15 @@ static int st1202_probe(struct i2c_client *client)
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to clear LED pattern\n");
+
+ init_data.fwnode = led->fwnode;
+ init_data.devicename = "st1202";
+ init_data.default_label = ":";
+
+ ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to register LED class device\n");
}
return 0;
diff --git a/drivers/leds/rgb/leds-pwm-multicolor.c b/drivers/leds/rgb/leds-pwm-multicolor.c
index f80a06cc31f8..1c7705bafdfc 100644
--- a/drivers/leds/rgb/leds-pwm-multicolor.c
+++ b/drivers/leds/rgb/leds-pwm-multicolor.c
@@ -141,8 +141,11 @@ static int led_pwm_mc_probe(struct platform_device *pdev)
/* init the multicolor's LED class device */
cdev = &priv->mc_cdev.led_cdev;
- fwnode_property_read_u32(mcnode, "max-brightness",
+ ret = fwnode_property_read_u32(mcnode, "max-brightness",
&cdev->max_brightness);
+ if (ret)
+ goto release_mcnode;
+
cdev->flags = LED_CORE_SUSPENDRESUME;
cdev->brightness_set_blocking = led_pwm_mc_set;
diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c
index f3c9ef2bfa57..4f2a178e3d26 100644
--- a/drivers/leds/rgb/leds-qcom-lpg.c
+++ b/drivers/leds/rgb/leds-qcom-lpg.c
@@ -24,6 +24,7 @@
#define LPG_PATTERN_CONFIG_REG 0x40
#define LPG_SIZE_CLK_REG 0x41
#define PWM_CLK_SELECT_MASK GENMASK(1, 0)
+#define PWM_SIZE_SELECT_MASK BIT(2)
#define PWM_CLK_SELECT_HI_RES_MASK GENMASK(2, 0)
#define PWM_SIZE_HI_RES_MASK GENMASK(6, 4)
#define LPG_PREDIV_CLK_REG 0x42
@@ -412,8 +413,8 @@ static int lpg_lut_sync(struct lpg *lpg, unsigned int mask)
static const unsigned int lpg_clk_rates[] = {0, 1024, 32768, 19200000};
static const unsigned int lpg_clk_rates_hi_res[] = {0, 1024, 32768, 19200000, 76800000};
static const unsigned int lpg_pre_divs[] = {1, 3, 5, 6};
-static const unsigned int lpg_pwm_resolution[] = {9};
-static const unsigned int lpg_pwm_resolution_hi_res[] = {8, 9, 10, 11, 12, 13, 14, 15};
+static const unsigned int lpg_pwm_resolution[] = {6, 9};
+static const unsigned int lpg_pwm_resolution_hi_res[] = {8, 9, 10, 11, 12, 13, 14, 15};
static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period)
{
@@ -436,12 +437,12 @@ static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period)
* period = --------------------------
* refclk
*
- * Resolution = 2^9 bits for PWM or
+ * Resolution = 2^{6 or 9} bits for PWM or
* 2^{8, 9, 10, 11, 12, 13, 14, 15} bits for high resolution PWM
* pre_div = {1, 3, 5, 6} and
* M = [0..7].
*
- * This allows for periods between 27uS and 384s for PWM channels and periods between
+ * This allows for periods between 3uS and 384s for PWM channels and periods between
* 3uS and 24576s for high resolution PWMs.
* The PWM framework wants a period of equal or lower length than requested,
* reject anything below minimum period.
@@ -461,7 +462,7 @@ static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period)
max_res = LPG_RESOLUTION_9BIT;
}
- min_period = div64_u64((u64)NSEC_PER_SEC * (1 << pwm_resolution_arr[0]),
+ min_period = div64_u64((u64)NSEC_PER_SEC * ((1 << pwm_resolution_arr[0]) - 1),
clk_rate_arr[clk_len - 1]);
if (period <= min_period)
return -EINVAL;
@@ -482,7 +483,7 @@ static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period)
*/
for (i = 0; i < pwm_resolution_count; i++) {
- resolution = 1 << pwm_resolution_arr[i];
+ resolution = (1 << pwm_resolution_arr[i]) - 1;
for (clk_sel = 1; clk_sel < clk_len; clk_sel++) {
u64 numerator = period * clk_rate_arr[clk_sel];
@@ -529,10 +530,10 @@ static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty)
unsigned int clk_rate;
if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) {
- max = LPG_RESOLUTION_15BIT - 1;
+ max = BIT(lpg_pwm_resolution_hi_res[chan->pwm_resolution_sel]) - 1;
clk_rate = lpg_clk_rates_hi_res[chan->clk_sel];
} else {
- max = LPG_RESOLUTION_9BIT - 1;
+ max = BIT(lpg_pwm_resolution[chan->pwm_resolution_sel]) - 1;
clk_rate = lpg_clk_rates[chan->clk_sel];
}
@@ -558,7 +559,7 @@ static void lpg_apply_freq(struct lpg_channel *chan)
val |= GENMASK(5, 4);
break;
case LPG_SUBTYPE_PWM:
- val |= BIT(2);
+ val |= FIELD_PREP(PWM_SIZE_SELECT_MASK, chan->pwm_resolution_sel);
break;
case LPG_SUBTYPE_HI_RES_PWM:
val |= FIELD_PREP(PWM_SIZE_HI_RES_MASK, chan->pwm_resolution_sel);
@@ -1276,7 +1277,7 @@ static int lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
resolution = lpg_pwm_resolution_hi_res[FIELD_GET(PWM_SIZE_HI_RES_MASK, val)];
} else {
refclk = lpg_clk_rates[FIELD_GET(PWM_CLK_SELECT_MASK, val)];
- resolution = 9;
+ resolution = lpg_pwm_resolution[FIELD_GET(PWM_SIZE_SELECT_MASK, val)];
}
if (refclk) {
@@ -1291,7 +1292,7 @@ static int lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
if (ret)
return ret;
- state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * (1 << resolution) *
+ state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * ((1 << resolution) - 1) *
pre_div * (1 << m), refclk);
state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk);
} else {
diff --git a/drivers/leds/simple/Kconfig b/drivers/leds/simatic/Kconfig
index e616cc6d6051..e616cc6d6051 100644
--- a/drivers/leds/simple/Kconfig
+++ b/drivers/leds/simatic/Kconfig
diff --git a/drivers/leds/simple/Makefile b/drivers/leds/simatic/Makefile
index 783578f11bb0..783578f11bb0 100644
--- a/drivers/leds/simple/Makefile
+++ b/drivers/leds/simatic/Makefile
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c
index c98c370687c2..c98c370687c2 100644
--- a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c
+++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-core.c
index 9bc5f361a06b..9bc5f361a06b 100644
--- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c
+++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-core.c
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c
index 7f7cff275448..7f7cff275448 100644
--- a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c
+++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c
index bc23d701bcb7..bc23d701bcb7 100644
--- a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c
+++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.h b/drivers/leds/simatic/simatic-ipc-leds-gpio.h
index 6b2519809cee..6b2519809cee 100644
--- a/drivers/leds/simple/simatic-ipc-leds-gpio.h
+++ b/drivers/leds/simatic/simatic-ipc-leds-gpio.h
diff --git a/drivers/leds/simple/simatic-ipc-leds.c b/drivers/leds/simatic/simatic-ipc-leds.c
index 348679f0d1b7..348679f0d1b7 100644
--- a/drivers/leds/simple/simatic-ipc-leds.c
+++ b/drivers/leds/simatic/simatic-ipc-leds.c
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index c15efe3e5078..4e048e08c4fd 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -68,6 +68,7 @@ struct led_netdev_data {
unsigned int last_activity;
unsigned long mode;
+ unsigned long blink_delay;
int link_speed;
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes);
u8 duplex;
@@ -86,6 +87,10 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
/* Already validated, hw control is possible with the requested mode */
if (trigger_data->hw_control) {
led_cdev->hw_control_set(led_cdev, trigger_data->mode);
+ if (led_cdev->blink_set) {
+ led_cdev->blink_set(led_cdev, &trigger_data->blink_delay,
+ &trigger_data->blink_delay);
+ }
return;
}
@@ -454,10 +459,11 @@ static ssize_t interval_store(struct device *dev,
size_t size)
{
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+ struct led_classdev *led_cdev = trigger_data->led_cdev;
unsigned long value;
int ret;
- if (trigger_data->hw_control)
+ if (trigger_data->hw_control && !led_cdev->blink_set)
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
@@ -466,9 +472,13 @@ static ssize_t interval_store(struct device *dev,
/* impose some basic bounds on the timer interval */
if (value >= 5 && value <= 10000) {
- cancel_delayed_work_sync(&trigger_data->work);
+ if (trigger_data->hw_control) {
+ trigger_data->blink_delay = value;
+ } else {
+ cancel_delayed_work_sync(&trigger_data->work);
- atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
+ }
set_baseline_state(trigger_data); /* resets timer */
}
diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c
index aad48c2540fc..a594bd5e2233 100644
--- a/drivers/leds/trigger/ledtrig-pattern.c
+++ b/drivers/leds/trigger/ledtrig-pattern.c
@@ -483,8 +483,8 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
data->led_cdev = led_cdev;
led_set_trigger_data(led_cdev, data);
timer_setup(&data->timer, pattern_trig_timer_function, 0);
- hrtimer_init(&data->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- data->hrtimer.function = pattern_trig_hrtimer_function;
+ hrtimer_setup(&data->hrtimer, pattern_trig_hrtimer_function, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
led_cdev->activated = true;
if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {