forked from openwrt/openwrt
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kernel: add upstream patches for pca955x led driver
These patches are to support the pca955x led with OpenWRT correctly via device tree on linux 5.10. Without these, the new LED function/color/reg features can not be used. Signed-off-by: Chris Blake <chrisrblake93@gmail.com>
- Loading branch information
1 parent
2e0afef
commit 78ecaeb
Showing
5 changed files
with
582 additions
and
0 deletions.
There are no files selected for viewing
178 changes: 178 additions & 0 deletions
178
target/linux/generic/backport-5.10/840-v5.15-leds-pca955x-clean-up-code-formatting.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
From 2420ae02ce0a926819ebe18f809a57bff3edeac2 Mon Sep 17 00:00:00 2001 | ||
From: Eddie James <eajames@linux.ibm.com> | ||
Date: Fri, 16 Jul 2021 17:03:27 -0500 | ||
Subject: [PATCH] leds: pca955x: Clean up code formatting | ||
|
||
Format the code. Add some variables to help shorten lines. | ||
|
||
Signed-off-by: Eddie James <eajames@linux.ibm.com> | ||
Signed-off-by: Pavel Machek <pavel@ucw.cz> | ||
--- | ||
drivers/leds/leds-pca955x.c | 63 ++++++++++++++++++------------------- | ||
1 file changed, 30 insertions(+), 33 deletions(-) | ||
|
||
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c | ||
index 7087ca4592fc96..f0d841cb59fcc8 100644 | ||
--- a/drivers/leds/leds-pca955x.c | ||
+++ b/drivers/leds/leds-pca955x.c | ||
@@ -166,11 +166,10 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) | ||
static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) | ||
{ | ||
struct pca955x *pca955x = i2c_get_clientdata(client); | ||
+ u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n); | ||
int ret; | ||
|
||
- ret = i2c_smbus_write_byte_data(client, | ||
- pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, | ||
- val); | ||
+ ret = i2c_smbus_write_byte_data(client, cmd, val); | ||
if (ret < 0) | ||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", | ||
__func__, n, val, ret); | ||
@@ -187,11 +186,10 @@ static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) | ||
static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) | ||
{ | ||
struct pca955x *pca955x = i2c_get_clientdata(client); | ||
+ u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); | ||
int ret; | ||
|
||
- ret = i2c_smbus_write_byte_data(client, | ||
- pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, | ||
- val); | ||
+ ret = i2c_smbus_write_byte_data(client, cmd, val); | ||
if (ret < 0) | ||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", | ||
__func__, n, val, ret); | ||
@@ -205,11 +203,10 @@ static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) | ||
static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) | ||
{ | ||
struct pca955x *pca955x = i2c_get_clientdata(client); | ||
+ u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; | ||
int ret; | ||
|
||
- ret = i2c_smbus_write_byte_data(client, | ||
- pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, | ||
- val); | ||
+ ret = i2c_smbus_write_byte_data(client, cmd, val); | ||
if (ret < 0) | ||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", | ||
__func__, n, val, ret); | ||
@@ -223,10 +220,10 @@ static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) | ||
static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) | ||
{ | ||
struct pca955x *pca955x = i2c_get_clientdata(client); | ||
+ u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; | ||
int ret; | ||
|
||
- ret = i2c_smbus_read_byte_data(client, | ||
- pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); | ||
+ ret = i2c_smbus_read_byte_data(client, cmd); | ||
if (ret < 0) { | ||
dev_err(&client->dev, "%s: reg 0x%x, err %d\n", | ||
__func__, n, ret); | ||
@@ -371,6 +368,7 @@ static struct pca955x_platform_data * | ||
pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) | ||
{ | ||
struct pca955x_platform_data *pdata; | ||
+ struct pca955x_led *led; | ||
struct fwnode_handle *child; | ||
int count; | ||
|
||
@@ -401,13 +399,13 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) | ||
if ((res != 0) && is_of_node(child)) | ||
name = to_of_node(child)->name; | ||
|
||
- snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), | ||
- "%s", name); | ||
+ led = &pdata->leds[reg]; | ||
+ snprintf(led->name, sizeof(led->name), "%s", name); | ||
|
||
- pdata->leds[reg].type = PCA955X_TYPE_LED; | ||
- fwnode_property_read_u32(child, "type", &pdata->leds[reg].type); | ||
+ led->type = PCA955X_TYPE_LED; | ||
+ fwnode_property_read_u32(child, "type", &led->type); | ||
fwnode_property_read_string(child, "linux,default-trigger", | ||
- &pdata->leds[reg].default_trigger); | ||
+ &led->default_trigger); | ||
} | ||
|
||
pdata->num_leds = chip->bits; | ||
@@ -426,11 +424,12 @@ static const struct of_device_id of_pca955x_match[] = { | ||
MODULE_DEVICE_TABLE(of, of_pca955x_match); | ||
|
||
static int pca955x_probe(struct i2c_client *client, | ||
- const struct i2c_device_id *id) | ||
+ const struct i2c_device_id *id) | ||
{ | ||
struct pca955x *pca955x; | ||
struct pca955x_led *pca955x_led; | ||
struct pca955x_chipdef *chip; | ||
+ struct led_classdev *led; | ||
struct i2c_adapter *adapter; | ||
int i, err; | ||
struct pca955x_platform_data *pdata; | ||
@@ -449,13 +448,13 @@ static int pca955x_probe(struct i2c_client *client, | ||
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != | ||
chip->slv_addr) { | ||
dev_err(&client->dev, "invalid slave address %02x\n", | ||
- client->addr); | ||
+ client->addr); | ||
return -ENODEV; | ||
} | ||
|
||
dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " | ||
- "slave address 0x%02x\n", | ||
- client->name, chip->bits, client->addr); | ||
+ "slave address 0x%02x\n", client->name, chip->bits, | ||
+ client->addr); | ||
|
||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
return -EIO; | ||
@@ -471,8 +470,8 @@ static int pca955x_probe(struct i2c_client *client, | ||
if (!pca955x) | ||
return -ENOMEM; | ||
|
||
- pca955x->leds = devm_kcalloc(&client->dev, | ||
- chip->bits, sizeof(*pca955x_led), GFP_KERNEL); | ||
+ pca955x->leds = devm_kcalloc(&client->dev, chip->bits, | ||
+ sizeof(*pca955x_led), GFP_KERNEL); | ||
if (!pca955x->leds) | ||
return -ENOMEM; | ||
|
||
@@ -501,27 +500,25 @@ static int pca955x_probe(struct i2c_client *client, | ||
*/ | ||
if (pdata->leds[i].name[0] == '\0') | ||
snprintf(pdata->leds[i].name, | ||
- sizeof(pdata->leds[i].name), "%d", i); | ||
+ sizeof(pdata->leds[i].name), "%d", i); | ||
|
||
- snprintf(pca955x_led->name, | ||
- sizeof(pca955x_led->name), "pca955x:%s", | ||
- pdata->leds[i].name); | ||
+ snprintf(pca955x_led->name, sizeof(pca955x_led->name), | ||
+ "pca955x:%s", pdata->leds[i].name); | ||
|
||
+ led = &pca955x_led->led_cdev; | ||
if (pdata->leds[i].default_trigger) | ||
- pca955x_led->led_cdev.default_trigger = | ||
+ led->default_trigger = | ||
pdata->leds[i].default_trigger; | ||
|
||
- pca955x_led->led_cdev.name = pca955x_led->name; | ||
- pca955x_led->led_cdev.brightness_set_blocking = | ||
- pca955x_led_set; | ||
+ led->name = pca955x_led->name; | ||
+ led->brightness_set_blocking = pca955x_led_set; | ||
|
||
- err = devm_led_classdev_register(&client->dev, | ||
- &pca955x_led->led_cdev); | ||
+ err = devm_led_classdev_register(&client->dev, led); | ||
if (err) | ||
return err; | ||
|
||
/* Turn off LED */ | ||
- err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); | ||
+ err = pca955x_led_set(led, LED_OFF); | ||
if (err) | ||
return err; | ||
} |
83 changes: 83 additions & 0 deletions
83
target/linux/generic/backport-5.10/841-v5.15-leds-pca955x-add-brightness-get-function.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
From 7086625fde6538b2c0623eb767ad23c7ac3d7f3a Mon Sep 17 00:00:00 2001 | ||
From: Eddie James <eajames@linux.ibm.com> | ||
Date: Fri, 16 Jul 2021 17:03:28 -0500 | ||
Subject: [PATCH] leds: pca955x: Add brightness_get function | ||
|
||
Add a function to fetch the state of the hardware LED. | ||
|
||
Signed-off-by: Eddie James <eajames@linux.ibm.com> | ||
Signed-off-by: Pavel Machek <pavel@ucw.cz> | ||
--- | ||
drivers/leds/leds-pca955x.c | 52 +++++++++++++++++++++++++++++++++++++ | ||
1 file changed, 52 insertions(+) | ||
|
||
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c | ||
index f0d841cb59fcc8..e47ba7c3b7c7d8 100644 | ||
--- a/drivers/leds/leds-pca955x.c | ||
+++ b/drivers/leds/leds-pca955x.c | ||
@@ -233,6 +233,57 @@ static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) | ||
return 0; | ||
} | ||
|
||
+static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val) | ||
+{ | ||
+ struct pca955x *pca955x = i2c_get_clientdata(client); | ||
+ u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); | ||
+ int ret; | ||
+ | ||
+ ret = i2c_smbus_read_byte_data(client, cmd); | ||
+ if (ret < 0) { | ||
+ dev_err(&client->dev, "%s: reg 0x%x, err %d\n", | ||
+ __func__, n, ret); | ||
+ return ret; | ||
+ } | ||
+ *val = (u8)ret; | ||
+ return 0; | ||
+} | ||
+ | ||
+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 *pca955x = pca955x_led->pca955x; | ||
+ u8 ls, pwm; | ||
+ int ret; | ||
+ | ||
+ ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls); | ||
+ if (ret) | ||
+ return ret; | ||
+ | ||
+ ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3; | ||
+ switch (ls) { | ||
+ case PCA955X_LS_LED_ON: | ||
+ 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); | ||
+ if (ret) | ||
+ return ret; | ||
+ ret = 255 - pwm; | ||
+ break; | ||
+ } | ||
+ | ||
+ return ret; | ||
+} | ||
+ | ||
static int pca955x_led_set(struct led_classdev *led_cdev, | ||
enum led_brightness value) | ||
{ | ||
@@ -512,6 +563,7 @@ static int pca955x_probe(struct i2c_client *client, | ||
|
||
led->name = pca955x_led->name; | ||
led->brightness_set_blocking = pca955x_led_set; | ||
+ led->brightness_get = pca955x_led_get; | ||
|
||
err = devm_led_classdev_register(&client->dev, led); | ||
if (err) |
119 changes: 119 additions & 0 deletions
119
...x/generic/backport-5.10/842-v5.15-leds-pca955x-implement-the-default-state-property.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
From e46cb6d0c760a5b15e38138845fad99628fafcb8 Mon Sep 17 00:00:00 2001 | ||
From: Eddie James <eajames@linux.ibm.com> | ||
Date: Fri, 16 Jul 2021 17:03:29 -0500 | ||
Subject: [PATCH] leds: pca955x: Implement the default-state property | ||
|
||
In order to retain the LED state after a system reboot, check the | ||
documented default-state device tree property during initialization. | ||
Modify the behavior of the probe according to the property. | ||
|
||
Signed-off-by: Eddie James <eajames@linux.ibm.com> | ||
Signed-off-by: Pavel Machek <pavel@ucw.cz> | ||
--- | ||
drivers/leds/leds-pca955x.c | 54 +++++++++++++++++++++++++++++++------ | ||
1 file changed, 46 insertions(+), 8 deletions(-) | ||
|
||
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c | ||
index e47ba7c3b7c7d8..fa1d77d86ef67b 100644 | ||
--- a/drivers/leds/leds-pca955x.c | ||
+++ b/drivers/leds/leds-pca955x.c | ||
@@ -129,6 +129,7 @@ struct pca955x_led { | ||
int led_num; /* 0 .. 15 potentially */ | ||
char name[32]; | ||
u32 type; | ||
+ int default_state; | ||
const char *default_trigger; | ||
}; | ||
|
||
@@ -439,6 +440,7 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) | ||
|
||
device_for_each_child_node(&client->dev, child) { | ||
const char *name; | ||
+ const char *state; | ||
u32 reg; | ||
int res; | ||
|
||
@@ -457,6 +459,18 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) | ||
fwnode_property_read_u32(child, "type", &led->type); | ||
fwnode_property_read_string(child, "linux,default-trigger", | ||
&led->default_trigger); | ||
+ | ||
+ if (!fwnode_property_read_string(child, "default-state", | ||
+ &state)) { | ||
+ if (!strcmp(state, "keep")) | ||
+ led->default_state = LEDS_GPIO_DEFSTATE_KEEP; | ||
+ else if (!strcmp(state, "on")) | ||
+ led->default_state = LEDS_GPIO_DEFSTATE_ON; | ||
+ else | ||
+ led->default_state = LEDS_GPIO_DEFSTATE_OFF; | ||
+ } else { | ||
+ led->default_state = LEDS_GPIO_DEFSTATE_OFF; | ||
+ } | ||
} | ||
|
||
pdata->num_leds = chip->bits; | ||
@@ -485,6 +499,7 @@ static int pca955x_probe(struct i2c_client *client, | ||
int i, err; | ||
struct pca955x_platform_data *pdata; | ||
int ngpios = 0; | ||
+ bool keep_pwm = false; | ||
|
||
chip = &pca955x_chipdefs[id->driver_data]; | ||
adapter = client->adapter; | ||
@@ -565,14 +580,35 @@ static int pca955x_probe(struct i2c_client *client, | ||
led->brightness_set_blocking = pca955x_led_set; | ||
led->brightness_get = pca955x_led_get; | ||
|
||
+ if (pdata->leds[i].default_state == | ||
+ LEDS_GPIO_DEFSTATE_OFF) { | ||
+ err = pca955x_led_set(led, LED_OFF); | ||
+ if (err) | ||
+ return err; | ||
+ } else if (pdata->leds[i].default_state == | ||
+ LEDS_GPIO_DEFSTATE_ON) { | ||
+ err = pca955x_led_set(led, LED_FULL); | ||
+ if (err) | ||
+ return err; | ||
+ } | ||
+ | ||
err = devm_led_classdev_register(&client->dev, led); | ||
if (err) | ||
return err; | ||
|
||
- /* Turn off LED */ | ||
- err = pca955x_led_set(led, LED_OFF); | ||
- if (err) | ||
- return err; | ||
+ /* | ||
+ * 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_GPIO_DEFSTATE_KEEP) { | ||
+ if (led->brightness != LED_FULL && | ||
+ led->brightness != LED_OFF && | ||
+ led->brightness != LED_HALF) | ||
+ keep_pwm = true; | ||
+ } | ||
} | ||
} | ||
|
||
@@ -581,10 +617,12 @@ static int pca955x_probe(struct i2c_client *client, | ||
if (err) | ||
return err; | ||
|
||
- /* PWM1 is used for variable brightness, default to OFF */ | ||
- err = pca955x_write_pwm(client, 1, 0); | ||
- 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; | ||
+ } | ||
|
||
/* Set to fast frequency so we do not see flashing */ | ||
err = pca955x_write_psc(client, 0, 0); |
Oops, something went wrong.