LED updates for 4.14
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJZsZswAAoJEL1qUBy3i3wm2cwQANp9Hufui0BYxD7X9bB3sBvX LJ2lbwQlZ93gmQo176fvE22wYw90wmf4CG12pdx8CB4TIBOuaEl1EUptaIxHjBkP +MOmiGxT0YZgUDmSo+sCL+LHEDWU8gcs8MI5ZkAxgALUb0kYiQySH5UlCJJOJIJv ww1A4K1bo6VONli+y8ZDa3eKgkArg1/pXYl2VejNMSkr88fyzf35fOKD44lMUGos 5KFVWdPMnz1OuVbh199x9vAPZygb4PNzJL1Tf2ntklBfU3mX+582e6uvIPqh4Tuy D5ZianpBNtA0IBV4G0UE+n/9rOy9oAr34+Tq9Gv0BRLlQXsC8FMEM5xcCDd1kFCI L6FeiGc1YnEtJpUVEhcWzmpww2EBBRKIB9lJK5c8VUlWf/yZME72Al5R8c7uffcE hc78eUVwM2fHNmBhmCmK49flC67/LMzHMiaEQ/lSGtGnd8ratclshFuooiPzsJN/ MSTX2B3yxWvBjwmZMNixY6WasduKKJs4K5xHnHJzyLt0mvClLiwgOvi1Dks0t+Ko imE6cdU3LCUeJJb4I9SgHTTjp33CTqq5ZBhbFsgqjNsfj4o/6GmnWxvSZ4ywqJMW HwU4iyBKlx2AVfhzIPozuoE67+HJyRYIexsUZi4vt0Pw2qHxwp1RvH+BMQbXGLLg rJ/H9ef5BAPRkPWxe1HM =E1ff -----END PGP SIGNATURE----- Merge tag 'leds_for_4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds Pull LED updates from Jacek Anaszewski: "LED class drivers improvements: leds-pca955x: - add Device Tree support and bindings - use devm_led_classdev_register() - add GPIO support - prevent crippled LED class device name - check for I2C errors leds-gpio: - add optional retain-state-shutdown DT property - allow LED to retain state at shutdown leds-tlc591xx: - merge conditional tests - add missing of_node_put leds-powernv: - delete an error message for a failed memory allocation in powernv_led_create() leds-is31fl32xx.c - convert to using custom %pOF printf format specifier Constify attribute_group structures in: - leds-blinkm - leds-lm3533 Make several arrays static const in: - leds-aat1290 - leds-lp5521 - leds-lp5562 - leds-lp8501" * tag 'leds_for_4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: leds: pca955x: check for I2C errors leds: gpio: Allow LED to retain state at shutdown dt-bindings: leds: gpio: Add optional retain-state-shutdown property leds: powernv: Delete an error message for a failed memory allocation in powernv_led_create() leds: lp8501: make several arrays static const leds: lp5562: make several arrays static const leds: lp5521: make several arrays static const leds: aat1290: make array max_mm_current_percent static const leds: pca955x: Prevent crippled LED device name leds: lm3533: constify attribute_group structure dt-bindings: leds: add pca955x leds: pca955x: add GPIO support leds: pca955x: use devm_led_classdev_register leds: pca955x: add device tree support leds: Convert to using %pOF instead of full_name leds: blinkm: constify attribute_group structures. leds: tlc591xx: add missing of_node_put leds: tlc591xx: merge conditional tests
This commit is contained in:
commit
5f9cc57036
|
@ -18,6 +18,9 @@ LED sub-node properties:
|
|||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- retain-state-suspended: (optional) The suspend state can be retained.Such
|
||||
as charge-led gpio.
|
||||
- retain-state-shutdown: (optional) Retain the state of the LED on shutdown.
|
||||
Useful in BMC systems, for example when the BMC is rebooted while the host
|
||||
remains up.
|
||||
- panic-indicator : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
* NXP - pca955x LED driver
|
||||
|
||||
The PCA955x family of chips are I2C LED blinkers whose pins not used
|
||||
to control LEDs can be used as general purpose I/Os. The GPIO pins can
|
||||
be input or output, and output pins can also be pulse-width controlled.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of :
|
||||
"nxp,pca9550"
|
||||
"nxp,pca9551"
|
||||
"nxp,pca9552"
|
||||
"nxp,pca9553"
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 0
|
||||
- reg: I2C slave address. depends on the model.
|
||||
|
||||
Optional properties:
|
||||
- gpio-controller: allows pins to be used as GPIOs.
|
||||
- #gpio-cells: must be 2.
|
||||
- gpio-line-names: define the names of the GPIO lines
|
||||
|
||||
LED sub-node properties:
|
||||
- reg : number of LED line.
|
||||
from 0 to 1 for the pca9550
|
||||
from 0 to 7 for the pca9551
|
||||
from 0 to 15 for the pca9552
|
||||
from 0 to 3 for the pca9553
|
||||
- type: (optional) either
|
||||
PCA9532_TYPE_NONE
|
||||
PCA9532_TYPE_LED
|
||||
PCA9532_TYPE_GPIO
|
||||
see dt-bindings/leds/leds-pca955x.h (default to LED)
|
||||
- label : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- linux,default-trigger : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Examples:
|
||||
|
||||
pca9552: pca9552@60 {
|
||||
compatible = "nxp,pca9552";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x60>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
gpio-line-names = "GPIO12", "GPIO13", "GPIO14", "GPIO15";
|
||||
|
||||
gpio@12 {
|
||||
reg = <12>;
|
||||
type = <PCA955X_TYPE_GPIO>;
|
||||
};
|
||||
gpio@13 {
|
||||
reg = <13>;
|
||||
type = <PCA955X_TYPE_GPIO>;
|
||||
};
|
||||
gpio@14 {
|
||||
reg = <14>;
|
||||
type = <PCA955X_TYPE_GPIO>;
|
||||
};
|
||||
gpio@15 {
|
||||
reg = <15>;
|
||||
type = <PCA955X_TYPE_GPIO>;
|
||||
};
|
||||
|
||||
led@0 {
|
||||
label = "red:power";
|
||||
linux,default-trigger = "default-on";
|
||||
reg = <0>;
|
||||
type = <PCA955X_TYPE_LED>;
|
||||
};
|
||||
led@1 {
|
||||
label = "green:power";
|
||||
reg = <1>;
|
||||
type = <PCA955X_TYPE_LED>;
|
||||
};
|
||||
led@2 {
|
||||
label = "pca9552:yellow";
|
||||
reg = <2>;
|
||||
type = <PCA955X_TYPE_LED>;
|
||||
};
|
||||
led@3 {
|
||||
label = "pca9552:white";
|
||||
reg = <3>;
|
||||
type = <PCA955X_TYPE_LED>;
|
||||
};
|
||||
};
|
|
@ -386,6 +386,17 @@ config LEDS_PCA955X
|
|||
LED driver chips accessed via the I2C bus. Supported
|
||||
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
|
||||
|
||||
config LEDS_PCA955X_GPIO
|
||||
bool "Enable GPIO support for PCA955X"
|
||||
depends on LEDS_PCA955X
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Allow unused pins on PCA955X to be used as gpio.
|
||||
|
||||
To use a pin as gpio the pin type should be set to
|
||||
PCA955X_TYPE_GPIO in the device tree.
|
||||
|
||||
|
||||
config LEDS_PCA963X
|
||||
tristate "LED support for PCA963x I2C chip"
|
||||
depends on LEDS_CLASS
|
||||
|
|
|
@ -314,8 +314,10 @@ static void aat1290_led_validate_mm_current(struct aat1290_led *led,
|
|||
static int init_mm_current_scale(struct aat1290_led *led,
|
||||
struct aat1290_led_config_data *cfg)
|
||||
{
|
||||
int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56,
|
||||
63, 71, 79, 89, 100 };
|
||||
static const int max_mm_current_percent[] = {
|
||||
20, 22, 25, 28, 32, 36, 40, 45, 50, 56,
|
||||
63, 71, 79, 89, 100
|
||||
};
|
||||
int i, max_mm_current =
|
||||
AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
|
||||
|
||||
|
|
|
@ -298,7 +298,7 @@ static struct attribute *blinkm_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group blinkm_group = {
|
||||
static const struct attribute_group blinkm_group = {
|
||||
.name = "blinkm",
|
||||
.attrs = blinkm_attrs,
|
||||
};
|
||||
|
|
|
@ -134,6 +134,8 @@ static int create_gpio_led(const struct gpio_led *template,
|
|||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
if (template->panic_indicator)
|
||||
led_dat->cdev.flags |= LED_PANIC_INDICATOR;
|
||||
if (template->retain_state_shutdown)
|
||||
led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
|
||||
ret = gpiod_direction_output(led_dat->gpiod, state);
|
||||
if (ret < 0)
|
||||
|
@ -205,6 +207,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||
|
||||
if (fwnode_property_present(child, "retain-state-suspended"))
|
||||
led.retain_state_suspended = 1;
|
||||
if (fwnode_property_present(child, "retain-state-shutdown"))
|
||||
led.retain_state_shutdown = 1;
|
||||
if (fwnode_property_present(child, "panic-indicator"))
|
||||
led.panic_indicator = 1;
|
||||
|
||||
|
@ -267,7 +271,8 @@ static void gpio_led_shutdown(struct platform_device *pdev)
|
|||
for (i = 0; i < priv->num_leds; i++) {
|
||||
struct gpio_led_data *led = &priv->leds[i];
|
||||
|
||||
gpio_led_set(&led->cdev, LED_OFF);
|
||||
if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN))
|
||||
gpio_led_set(&led->cdev, LED_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -348,8 +348,8 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
|
|||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
|
||||
dev_err(dev,
|
||||
"Child node %s does not have a valid reg property\n",
|
||||
child->full_name);
|
||||
"Child node %pOF does not have a valid reg property\n",
|
||||
child);
|
||||
return -EINVAL;
|
||||
}
|
||||
led_data->channel = reg;
|
||||
|
|
|
@ -626,7 +626,7 @@ static umode_t lm3533_led_attr_is_visible(struct kobject *kobj,
|
|||
return mode;
|
||||
};
|
||||
|
||||
static struct attribute_group lm3533_led_attribute_group = {
|
||||
static const struct attribute_group lm3533_led_attribute_group = {
|
||||
.is_visible = lm3533_led_attr_is_visible,
|
||||
.attrs = lm3533_led_attributes
|
||||
};
|
||||
|
|
|
@ -134,13 +134,13 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current)
|
|||
static void lp5521_load_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 mask[] = {
|
||||
static const u8 mask[] = {
|
||||
[LP55XX_ENGINE_1] = LP5521_MODE_R_M,
|
||||
[LP55XX_ENGINE_2] = LP5521_MODE_G_M,
|
||||
[LP55XX_ENGINE_3] = LP5521_MODE_B_M,
|
||||
};
|
||||
|
||||
u8 val[] = {
|
||||
static const u8 val[] = {
|
||||
[LP55XX_ENGINE_1] = LP5521_LOAD_R,
|
||||
[LP55XX_ENGINE_2] = LP5521_LOAD_G,
|
||||
[LP55XX_ENGINE_3] = LP5521_LOAD_B,
|
||||
|
@ -160,7 +160,7 @@ static void lp5521_stop_all_engines(struct lp55xx_chip *chip)
|
|||
static void lp5521_stop_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 mask[] = {
|
||||
static const u8 mask[] = {
|
||||
[LP55XX_ENGINE_1] = LP5521_MODE_R_M,
|
||||
[LP55XX_ENGINE_2] = LP5521_MODE_G_M,
|
||||
[LP55XX_ENGINE_3] = LP5521_MODE_B_M,
|
||||
|
@ -226,7 +226,7 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
|
|||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
|
||||
u8 addr[] = {
|
||||
static const u8 addr[] = {
|
||||
[LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM,
|
||||
[LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM,
|
||||
[LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM,
|
||||
|
|
|
@ -116,7 +116,7 @@ static inline void lp5562_wait_enable_done(void)
|
|||
|
||||
static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
|
||||
{
|
||||
u8 addr[] = {
|
||||
static const u8 addr[] = {
|
||||
LP5562_REG_R_CURRENT,
|
||||
LP5562_REG_G_CURRENT,
|
||||
LP5562_REG_B_CURRENT,
|
||||
|
@ -130,13 +130,13 @@ static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
|
|||
static void lp5562_load_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 mask[] = {
|
||||
static const u8 mask[] = {
|
||||
[LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
|
||||
[LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
|
||||
[LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
|
||||
};
|
||||
|
||||
u8 val[] = {
|
||||
static const u8 val[] = {
|
||||
[LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
|
||||
|
@ -211,7 +211,7 @@ static int lp5562_update_firmware(struct lp55xx_chip *chip,
|
|||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
|
||||
u8 addr[] = {
|
||||
static const u8 addr[] = {
|
||||
[LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
|
||||
|
@ -314,7 +314,7 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip)
|
|||
static int lp5562_led_brightness(struct lp55xx_led *led)
|
||||
{
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
u8 addr[] = {
|
||||
static const u8 addr[] = {
|
||||
LP5562_REG_R_PWM,
|
||||
LP5562_REG_G_PWM,
|
||||
LP5562_REG_B_PWM,
|
||||
|
|
|
@ -118,19 +118,19 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip)
|
|||
static void lp8501_load_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 mask[] = {
|
||||
static const u8 mask[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
|
||||
[LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
|
||||
[LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
|
||||
};
|
||||
|
||||
u8 val[] = {
|
||||
static const u8 val[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
|
||||
};
|
||||
|
||||
u8 page_sel[] = {
|
||||
static const u8 page_sel[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
|
||||
|
|
|
@ -41,14 +41,19 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <dt-bindings/leds/leds-pca955x.h>
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
|
||||
|
@ -115,6 +120,9 @@ struct pca955x {
|
|||
struct pca955x_led *leds;
|
||||
struct pca955x_chipdef *chipdef;
|
||||
struct i2c_client *client;
|
||||
#ifdef CONFIG_LEDS_PCA955X_GPIO
|
||||
struct gpio_chip gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pca955x_led {
|
||||
|
@ -122,6 +130,13 @@ struct pca955x_led {
|
|||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 15 potentially */
|
||||
char name[32];
|
||||
u32 type;
|
||||
const char *default_trigger;
|
||||
};
|
||||
|
||||
struct pca955x_platform_data {
|
||||
struct pca955x_led *leds;
|
||||
int num_leds;
|
||||
};
|
||||
|
||||
/* 8 bits per input register */
|
||||
|
@ -150,13 +165,18 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
|
|||
* Write to frequency prescaler register, used to program the
|
||||
* period of the PWM output. period = (PSCx + 1) / 38
|
||||
*/
|
||||
static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
|
||||
static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
|
||||
{
|
||||
struct pca955x *pca955x = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
i2c_smbus_write_byte_data(client,
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
|
||||
val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
|
||||
__func__, n, val, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -166,38 +186,56 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
|
|||
*
|
||||
* Duty cycle is (256 - PWMx) / 256
|
||||
*/
|
||||
static void pca955x_write_pwm(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);
|
||||
int ret;
|
||||
|
||||
i2c_smbus_write_byte_data(client,
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
|
||||
val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
|
||||
__func__, n, val, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to LED selector register, which determines the source that
|
||||
* drives the LED output.
|
||||
*/
|
||||
static void pca955x_write_ls(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);
|
||||
int ret;
|
||||
|
||||
i2c_smbus_write_byte_data(client,
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
|
||||
val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
|
||||
__func__, n, val, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the LED selector register, which determines the source that
|
||||
* drives the LED output.
|
||||
*/
|
||||
static u8 pca955x_read_ls(struct i2c_client *client, int n)
|
||||
static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)
|
||||
{
|
||||
struct pca955x *pca955x = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return (u8) i2c_smbus_read_byte_data(client,
|
||||
ret = i2c_smbus_read_byte_data(client,
|
||||
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
|
||||
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 int pca955x_led_set(struct led_classdev *led_cdev,
|
||||
|
@ -208,6 +246,7 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
|
|||
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;
|
||||
|
@ -217,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
|
|||
|
||||
mutex_lock(&pca955x->lock);
|
||||
|
||||
ls = pca955x_read_ls(pca955x->client, chip_ls);
|
||||
ret = pca955x_read_ls(pca955x->client, chip_ls, &ls);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
switch (value) {
|
||||
case LED_FULL:
|
||||
|
@ -237,19 +278,160 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
|
|||
* OFF, HALF, or FULL. But, this is probably better than
|
||||
* just turning off for all other values.
|
||||
*/
|
||||
pca955x_write_pwm(pca955x->client, 1,
|
||||
255 - value);
|
||||
ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);
|
||||
if (ret)
|
||||
goto out;
|
||||
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
|
||||
break;
|
||||
}
|
||||
|
||||
pca955x_write_ls(pca955x->client, chip_ls, ls);
|
||||
ret = pca955x_write_ls(pca955x->client, chip_ls, ls);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pca955x->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_PCA955X_GPIO
|
||||
/*
|
||||
* Read the INPUT register, which contains the state of LEDs.
|
||||
*/
|
||||
static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(client, n);
|
||||
|
||||
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 int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct pca955x *pca955x = gpiochip_get_data(gc);
|
||||
struct pca955x_led *led = &pca955x->leds[offset];
|
||||
|
||||
if (led->type == PCA955X_TYPE_GPIO)
|
||||
return 0;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
|
||||
int val)
|
||||
{
|
||||
struct pca955x *pca955x = gpiochip_get_data(gc);
|
||||
struct pca955x_led *led = &pca955x->leds[offset];
|
||||
|
||||
if (val)
|
||||
return pca955x_led_set(&led->led_cdev, LED_FULL);
|
||||
else
|
||||
return pca955x_led_set(&led->led_cdev, LED_OFF);
|
||||
}
|
||||
|
||||
static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
|
||||
int val)
|
||||
{
|
||||
pca955x_set_value(gc, offset, val);
|
||||
}
|
||||
|
||||
static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct pca955x *pca955x = gpiochip_get_data(gc);
|
||||
struct pca955x_led *led = &pca955x->leds[offset];
|
||||
u8 reg = 0;
|
||||
|
||||
/* There is nothing we can do about errors */
|
||||
pca955x_read_input(pca955x->client, led->led_num / 8, ®);
|
||||
|
||||
return !!(reg & (1 << (led->led_num % 8)));
|
||||
}
|
||||
|
||||
static int pca955x_gpio_direction_input(struct gpio_chip *gc,
|
||||
unsigned int offset)
|
||||
{
|
||||
/* To use as input ensure pin is not driven */
|
||||
return pca955x_set_value(gc, offset, 0);
|
||||
}
|
||||
|
||||
static int pca955x_gpio_direction_output(struct gpio_chip *gc,
|
||||
unsigned int offset, int val)
|
||||
{
|
||||
return pca955x_set_value(gc, offset, val);
|
||||
}
|
||||
#endif /* CONFIG_LEDS_PCA955X_GPIO */
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static struct pca955x_platform_data *
|
||||
pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *child;
|
||||
struct pca955x_platform_data *pdata;
|
||||
int count;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
if (!count || count > chip->bits)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->leds = devm_kzalloc(&client->dev,
|
||||
sizeof(struct pca955x_led) * chip->bits,
|
||||
GFP_KERNEL);
|
||||
if (!pdata->leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
const char *name;
|
||||
u32 reg;
|
||||
int res;
|
||||
|
||||
res = of_property_read_u32(child, "reg", ®);
|
||||
if ((res != 0) || (reg >= chip->bits))
|
||||
continue;
|
||||
|
||||
if (of_property_read_string(child, "label", &name))
|
||||
name = child->name;
|
||||
|
||||
snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name),
|
||||
"%s", name);
|
||||
|
||||
pdata->leds[reg].type = PCA955X_TYPE_LED;
|
||||
of_property_read_u32(child, "type", &pdata->leds[reg].type);
|
||||
of_property_read_string(child, "linux,default-trigger",
|
||||
&pdata->leds[reg].default_trigger);
|
||||
}
|
||||
|
||||
pdata->num_leds = chip->bits;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_pca955x_match[] = {
|
||||
{ .compatible = "nxp,pca9550", .data = (void *)pca9550 },
|
||||
{ .compatible = "nxp,pca9551", .data = (void *)pca9551 },
|
||||
{ .compatible = "nxp,pca9552", .data = (void *)pca9552 },
|
||||
{ .compatible = "nxp,pca9553", .data = (void *)pca9553 },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_pca955x_match);
|
||||
#else
|
||||
static struct pca955x_platform_data *
|
||||
pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pca955x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -257,8 +439,9 @@ static int pca955x_probe(struct i2c_client *client,
|
|||
struct pca955x_led *pca955x_led;
|
||||
struct pca955x_chipdef *chip;
|
||||
struct i2c_adapter *adapter;
|
||||
struct led_platform_data *pdata;
|
||||
int i, err;
|
||||
struct pca955x_platform_data *pdata;
|
||||
int ngpios = 0;
|
||||
|
||||
if (id) {
|
||||
chip = &pca955x_chipdefs[id->driver_data];
|
||||
|
@ -272,6 +455,11 @@ static int pca955x_probe(struct i2c_client *client,
|
|||
}
|
||||
adapter = to_i2c_adapter(client->dev.parent);
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata) {
|
||||
pdata = pca955x_pdata_of_init(client, chip);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
/* Make sure the slave address / chip type combo given is possible */
|
||||
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
|
||||
|
@ -288,13 +476,11 @@ static int pca955x_probe(struct i2c_client *client,
|
|||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EIO;
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->num_leds != chip->bits) {
|
||||
dev_err(&client->dev, "board info claims %d LEDs"
|
||||
" on a %d-bit chip\n",
|
||||
pdata->num_leds, chip->bits);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pdata->num_leds != chip->bits) {
|
||||
dev_err(&client->dev,
|
||||
"board info claims %d LEDs on a %d-bit chip\n",
|
||||
pdata->num_leds, chip->bits);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
|
||||
|
@ -316,60 +502,92 @@ static int pca955x_probe(struct i2c_client *client,
|
|||
pca955x_led = &pca955x->leds[i];
|
||||
pca955x_led->led_num = i;
|
||||
pca955x_led->pca955x = pca955x;
|
||||
pca955x_led->type = pdata->leds[i].type;
|
||||
|
||||
switch (pca955x_led->type) {
|
||||
case PCA955X_TYPE_NONE:
|
||||
break;
|
||||
case PCA955X_TYPE_GPIO:
|
||||
ngpios++;
|
||||
break;
|
||||
case PCA955X_TYPE_LED:
|
||||
/*
|
||||
* Platform data can specify LED names and
|
||||
* default triggers
|
||||
*/
|
||||
if (pdata->leds[i].name[0] == '\0')
|
||||
snprintf(pdata->leds[i].name,
|
||||
sizeof(pdata->leds[i].name), "%d", i);
|
||||
|
||||
snprintf(pca955x_led->name,
|
||||
sizeof(pca955x_led->name), "pca955x:%s",
|
||||
pdata->leds[i].name);
|
||||
|
||||
/* Platform data can specify LED names and default triggers */
|
||||
if (pdata) {
|
||||
if (pdata->leds[i].name)
|
||||
snprintf(pca955x_led->name,
|
||||
sizeof(pca955x_led->name), "pca955x:%s",
|
||||
pdata->leds[i].name);
|
||||
if (pdata->leds[i].default_trigger)
|
||||
pca955x_led->led_cdev.default_trigger =
|
||||
pdata->leds[i].default_trigger;
|
||||
} else {
|
||||
snprintf(pca955x_led->name, sizeof(pca955x_led->name),
|
||||
"pca955x:%d", i);
|
||||
|
||||
pca955x_led->led_cdev.name = pca955x_led->name;
|
||||
pca955x_led->led_cdev.brightness_set_blocking =
|
||||
pca955x_led_set;
|
||||
|
||||
err = devm_led_classdev_register(&client->dev,
|
||||
&pca955x_led->led_cdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Turn off LED */
|
||||
err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
pca955x_led->led_cdev.name = pca955x_led->name;
|
||||
pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set;
|
||||
|
||||
err = led_classdev_register(&client->dev,
|
||||
&pca955x_led->led_cdev);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Turn off LEDs */
|
||||
for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++)
|
||||
pca955x_write_ls(client, i, 0x55);
|
||||
|
||||
/* PWM0 is used for half brightness or 50% duty cycle */
|
||||
pca955x_write_pwm(client, 0, 255-LED_HALF);
|
||||
err = pca955x_write_pwm(client, 0, 255 - LED_HALF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* PWM1 is used for variable brightness, default to OFF */
|
||||
pca955x_write_pwm(client, 1, 0);
|
||||
err = pca955x_write_pwm(client, 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set to fast frequency so we do not see flashing */
|
||||
pca955x_write_psc(client, 0, 0);
|
||||
pca955x_write_psc(client, 1, 0);
|
||||
err = pca955x_write_psc(client, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = pca955x_write_psc(client, 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
#ifdef CONFIG_LEDS_PCA955X_GPIO
|
||||
if (ngpios) {
|
||||
pca955x->gpio.label = "gpio-pca955x";
|
||||
pca955x->gpio.direction_input = pca955x_gpio_direction_input;
|
||||
pca955x->gpio.direction_output = pca955x_gpio_direction_output;
|
||||
pca955x->gpio.set = pca955x_gpio_set_value;
|
||||
pca955x->gpio.get = pca955x_gpio_get_value;
|
||||
pca955x->gpio.request = pca955x_gpio_request_pin;
|
||||
pca955x->gpio.can_sleep = 1;
|
||||
pca955x->gpio.base = -1;
|
||||
pca955x->gpio.ngpio = ngpios;
|
||||
pca955x->gpio.parent = &client->dev;
|
||||
pca955x->gpio.owner = THIS_MODULE;
|
||||
|
||||
exit:
|
||||
while (i--)
|
||||
led_classdev_unregister(&pca955x->leds[i].led_cdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pca955x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca955x *pca955x = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pca955x->chipdef->bits; i++)
|
||||
led_classdev_unregister(&pca955x->leds[i].led_cdev);
|
||||
err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
|
||||
pca955x);
|
||||
if (err) {
|
||||
/* Use data->gpio.dev as a flag for freeing gpiochip */
|
||||
pca955x->gpio.parent = NULL;
|
||||
dev_warn(&client->dev, "could not add gpiochip\n");
|
||||
return err;
|
||||
}
|
||||
dev_info(&client->dev, "gpios %i...%i\n",
|
||||
pca955x->gpio.base, pca955x->gpio.base +
|
||||
pca955x->gpio.ngpio - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -378,9 +596,9 @@ static struct i2c_driver pca955x_driver = {
|
|||
.driver = {
|
||||
.name = "leds-pca955x",
|
||||
.acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
|
||||
.of_match_table = of_match_ptr(of_pca955x_match),
|
||||
},
|
||||
.probe = pca955x_probe,
|
||||
.remove = pca955x_remove,
|
||||
.id_table = pca955x_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -224,12 +224,8 @@ static int powernv_led_create(struct device *dev,
|
|||
powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
|
||||
powernv_led->loc_code,
|
||||
led_type_desc);
|
||||
if (!powernv_led->cdev.name) {
|
||||
dev_err(dev,
|
||||
"%s: Memory allocation failed for classdev name\n",
|
||||
__func__);
|
||||
if (!powernv_led->cdev.name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
|
||||
powernv_led->cdev.brightness_get = powernv_brightness_get;
|
||||
|
|
|
@ -230,12 +230,15 @@ tlc591xx_probe(struct i2c_client *client,
|
|||
|
||||
for_each_child_of_node(np, child) {
|
||||
err = of_property_read_u32(child, "reg", ®);
|
||||
if (err)
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
return err;
|
||||
if (reg < 0 || reg >= tlc591xx->max_leds)
|
||||
return -EINVAL;
|
||||
if (priv->leds[reg].active)
|
||||
}
|
||||
if (reg < 0 || reg >= tlc591xx->max_leds ||
|
||||
priv->leds[reg].active) {
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->leds[reg].active = true;
|
||||
priv->leds[reg].ldev.name =
|
||||
of_get_property(child, "label", NULL) ? : child->name;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* This header provides constants for pca955x LED bindings.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_LEDS_PCA955X_H
|
||||
#define _DT_BINDINGS_LEDS_PCA955X_H
|
||||
|
||||
#define PCA955X_TYPE_NONE 0
|
||||
#define PCA955X_TYPE_LED 1
|
||||
#define PCA955X_TYPE_GPIO 2
|
||||
|
||||
#endif /* _DT_BINDINGS_LEDS_PCA955X_H */
|
|
@ -49,6 +49,7 @@ struct led_classdev {
|
|||
#define LED_HW_PLUGGABLE (1 << 19)
|
||||
#define LED_PANIC_INDICATOR (1 << 20)
|
||||
#define LED_BRIGHT_HW_CHANGED (1 << 21)
|
||||
#define LED_RETAIN_AT_SHUTDOWN (1 << 22)
|
||||
|
||||
/* set_brightness_work / blink_timer flags, atomic, private. */
|
||||
unsigned long work_flags;
|
||||
|
@ -392,6 +393,7 @@ struct gpio_led {
|
|||
unsigned retain_state_suspended : 1;
|
||||
unsigned panic_indicator : 1;
|
||||
unsigned default_state : 2;
|
||||
unsigned retain_state_shutdown : 1;
|
||||
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
|
||||
struct gpio_desc *gpiod;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue