Commit 5f9cc570 authored by Linus Torvalds's avatar Linus Torvalds

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
parents cd7b34fe 1591caf2
...@@ -18,6 +18,9 @@ LED sub-node properties: ...@@ -18,6 +18,9 @@ LED sub-node properties:
see Documentation/devicetree/bindings/leds/common.txt see Documentation/devicetree/bindings/leds/common.txt
- retain-state-suspended: (optional) The suspend state can be retained.Such - retain-state-suspended: (optional) The suspend state can be retained.Such
as charge-led gpio. 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) - panic-indicator : (optional)
see Documentation/devicetree/bindings/leds/common.txt see Documentation/devicetree/bindings/leds/common.txt
......
* 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 ...@@ -386,6 +386,17 @@ config LEDS_PCA955X
LED driver chips accessed via the I2C bus. Supported LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553. 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 config LEDS_PCA963X
tristate "LED support for PCA963x I2C chip" tristate "LED support for PCA963x I2C chip"
depends on LEDS_CLASS depends on LEDS_CLASS
......
...@@ -314,8 +314,10 @@ static void aat1290_led_validate_mm_current(struct aat1290_led *led, ...@@ -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, static int init_mm_current_scale(struct aat1290_led *led,
struct aat1290_led_config_data *cfg) struct aat1290_led_config_data *cfg)
{ {
int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, static const int max_mm_current_percent[] = {
63, 71, 79, 89, 100 }; 20, 22, 25, 28, 32, 36, 40, 45, 50, 56,
63, 71, 79, 89, 100
};
int i, max_mm_current = int i, max_mm_current =
AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
......
...@@ -298,7 +298,7 @@ static struct attribute *blinkm_attrs[] = { ...@@ -298,7 +298,7 @@ static struct attribute *blinkm_attrs[] = {
NULL, NULL,
}; };
static struct attribute_group blinkm_group = { static const struct attribute_group blinkm_group = {
.name = "blinkm", .name = "blinkm",
.attrs = blinkm_attrs, .attrs = blinkm_attrs,
}; };
......
...@@ -134,6 +134,8 @@ static int create_gpio_led(const struct gpio_led *template, ...@@ -134,6 +134,8 @@ static int create_gpio_led(const struct gpio_led *template,
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
if (template->panic_indicator) if (template->panic_indicator)
led_dat->cdev.flags |= LED_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); ret = gpiod_direction_output(led_dat->gpiod, state);
if (ret < 0) if (ret < 0)
...@@ -205,6 +207,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) ...@@ -205,6 +207,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
if (fwnode_property_present(child, "retain-state-suspended")) if (fwnode_property_present(child, "retain-state-suspended"))
led.retain_state_suspended = 1; 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")) if (fwnode_property_present(child, "panic-indicator"))
led.panic_indicator = 1; led.panic_indicator = 1;
...@@ -267,7 +271,8 @@ static void gpio_led_shutdown(struct platform_device *pdev) ...@@ -267,7 +271,8 @@ static void gpio_led_shutdown(struct platform_device *pdev)
for (i = 0; i < priv->num_leds; i++) { for (i = 0; i < priv->num_leds; i++) {
struct gpio_led_data *led = &priv->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, ...@@ -348,8 +348,8 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
ret = of_property_read_u32(child, "reg", &reg); ret = of_property_read_u32(child, "reg", &reg);
if (ret || reg < 1 || reg > led_data->priv->cdef->channels) { if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
dev_err(dev, dev_err(dev,
"Child node %s does not have a valid reg property\n", "Child node %pOF does not have a valid reg property\n",
child->full_name); child);
return -EINVAL; return -EINVAL;
} }
led_data->channel = reg; led_data->channel = reg;
......
...@@ -626,7 +626,7 @@ static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, ...@@ -626,7 +626,7 @@ static umode_t lm3533_led_attr_is_visible(struct kobject *kobj,
return mode; 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, .is_visible = lm3533_led_attr_is_visible,
.attrs = lm3533_led_attributes .attrs = lm3533_led_attributes
}; };
......
...@@ -134,13 +134,13 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) ...@@ -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) static void lp5521_load_engine(struct lp55xx_chip *chip)
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = { static const u8 mask[] = {
[LP55XX_ENGINE_1] = LP5521_MODE_R_M, [LP55XX_ENGINE_1] = LP5521_MODE_R_M,
[LP55XX_ENGINE_2] = LP5521_MODE_G_M, [LP55XX_ENGINE_2] = LP5521_MODE_G_M,
[LP55XX_ENGINE_3] = LP5521_MODE_B_M, [LP55XX_ENGINE_3] = LP5521_MODE_B_M,
}; };
u8 val[] = { static const u8 val[] = {
[LP55XX_ENGINE_1] = LP5521_LOAD_R, [LP55XX_ENGINE_1] = LP5521_LOAD_R,
[LP55XX_ENGINE_2] = LP5521_LOAD_G, [LP55XX_ENGINE_2] = LP5521_LOAD_G,
[LP55XX_ENGINE_3] = LP5521_LOAD_B, [LP55XX_ENGINE_3] = LP5521_LOAD_B,
...@@ -160,7 +160,7 @@ static void lp5521_stop_all_engines(struct lp55xx_chip *chip) ...@@ -160,7 +160,7 @@ static void lp5521_stop_all_engines(struct lp55xx_chip *chip)
static void lp5521_stop_engine(struct lp55xx_chip *chip) static void lp5521_stop_engine(struct lp55xx_chip *chip)
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = { static const u8 mask[] = {
[LP55XX_ENGINE_1] = LP5521_MODE_R_M, [LP55XX_ENGINE_1] = LP5521_MODE_R_M,
[LP55XX_ENGINE_2] = LP5521_MODE_G_M, [LP55XX_ENGINE_2] = LP5521_MODE_G_M,
[LP55XX_ENGINE_3] = LP5521_MODE_B_M, [LP55XX_ENGINE_3] = LP5521_MODE_B_M,
...@@ -226,7 +226,7 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip, ...@@ -226,7 +226,7 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
u8 addr[] = { static const u8 addr[] = {
[LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM,
[LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM,
[LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM,
......
...@@ -116,7 +116,7 @@ static inline void lp5562_wait_enable_done(void) ...@@ -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) 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_R_CURRENT,
LP5562_REG_G_CURRENT, LP5562_REG_G_CURRENT,
LP5562_REG_B_CURRENT, LP5562_REG_B_CURRENT,
...@@ -130,13 +130,13 @@ static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_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) static void lp5562_load_engine(struct lp55xx_chip *chip)
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = { static const u8 mask[] = {
[LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M, [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
[LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M, [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
[LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M, [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
}; };
u8 val[] = { static const u8 val[] = {
[LP55XX_ENGINE_1] = LP5562_LOAD_ENG1, [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
[LP55XX_ENGINE_2] = LP5562_LOAD_ENG2, [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
[LP55XX_ENGINE_3] = LP5562_LOAD_ENG3, [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
...@@ -211,7 +211,7 @@ static int lp5562_update_firmware(struct lp55xx_chip *chip, ...@@ -211,7 +211,7 @@ static int lp5562_update_firmware(struct lp55xx_chip *chip,
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 pattern[LP5562_PROGRAM_LENGTH] = {0}; u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
u8 addr[] = { static const u8 addr[] = {
[LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1, [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
[LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2, [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
[LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3, [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
...@@ -314,7 +314,7 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip) ...@@ -314,7 +314,7 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip)
static int lp5562_led_brightness(struct lp55xx_led *led) static int lp5562_led_brightness(struct lp55xx_led *led)
{ {
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
u8 addr[] = { static const u8 addr[] = {
LP5562_REG_R_PWM, LP5562_REG_R_PWM,
LP5562_REG_G_PWM, LP5562_REG_G_PWM,
LP5562_REG_B_PWM, LP5562_REG_B_PWM,
......
...@@ -118,19 +118,19 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip) ...@@ -118,19 +118,19 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip)
static void lp8501_load_engine(struct lp55xx_chip *chip) static void lp8501_load_engine(struct lp55xx_chip *chip)
{ {
enum lp55xx_engine_index idx = chip->engine_idx; enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = { static const u8 mask[] = {
[LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
[LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
[LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
}; };
u8 val[] = { static const u8 val[] = {
[LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
[LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
[LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
}; };
u8 page_sel[] = { static const u8 page_sel[] = {
[LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
[LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
[LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
......
...@@ -41,14 +41,19 @@ ...@@ -41,14 +41,19 @@
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/leds.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.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/slab.h>
#include <linux/string.h>
#include <dt-bindings/leds/leds-pca955x.h>
/* LED select registers determine the source that drives LED outputs */ /* LED select registers determine the source that drives LED outputs */
#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ #define PCA955X_LS_LED_ON 0x0 /* Output LOW */
...@@ -115,6 +120,9 @@ struct pca955x { ...@@ -115,6 +120,9 @@ struct pca955x {
struct pca955x_led *leds; struct pca955x_led *leds;
struct pca955x_chipdef *chipdef; struct pca955x_chipdef *chipdef;
struct i2c_client *client; struct i2c_client *client;
#ifdef CONFIG_LEDS_PCA955X_GPIO
struct gpio_chip gpio;
#endif
}; };
struct pca955x_led { struct pca955x_led {
...@@ -122,6 +130,13 @@ struct pca955x_led { ...@@ -122,6 +130,13 @@ struct pca955x_led {
struct led_classdev led_cdev; struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */ int led_num; /* 0 .. 15 potentially */
char name[32]; 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 */ /* 8 bits per input register */
...@@ -150,13 +165,18 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) ...@@ -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 * 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) / 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); 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, pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
val); 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) ...@@ -166,38 +186,56 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
* *
* Duty cycle is (256 - PWMx) / 256 * 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); 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, pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
val); 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 * Write to LED selector register, which determines the source that
* drives the LED output. * 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); 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, pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
val); 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 * Read the LED selector register, which determines the source that
* drives the LED output. * 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); 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); 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, static int pca955x_led_set(struct led_classdev *led_cdev,
...@@ -208,6 +246,7 @@ 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; u8 ls;
int chip_ls; /* which LSx to use (0-3 potentially) */ int chip_ls; /* which LSx to use (0-3 potentially) */
int ls_led; /* which set of bits within LSx to use (0-3) */ 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_led = container_of(led_cdev, struct pca955x_led, led_cdev);
pca955x = pca955x_led->pca955x; pca955x = pca955x_led->pca955x;
...@@ -217,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev, ...@@ -217,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
mutex_lock(&pca955x->lock); 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) { switch (value) {
case LED_FULL: case LED_FULL:
...@@ -237,18 +278,159 @@ static int pca955x_led_set(struct led_classdev *led_cdev, ...@@ -237,18 +278,159 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
* OFF, HALF, or FULL. But, this is probably better than * OFF, HALF, or FULL. But, this is probably better than
* just turning off for all other values. * just turning off for all other values.
*/ */
pca955x_write_pwm(pca955x->client, 1, ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);
255 - value); if (ret)
goto out;
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
break; break;
} }
pca955x_write_ls(pca955x->client, chip_ls, ls); ret = pca955x_write_ls(pca955x->client, chip_ls, ls);
out:
mutex_unlock(&pca955x->lock); mutex_unlock(&pca955x->lock);
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; 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, &reg);
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", &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, static int pca955x_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
...@@ -257,8 +439,9 @@ static int pca955x_probe(struct i2c_client *client, ...@@ -257,8 +439,9 @@ static int pca955x_probe(struct i2c_client *client,
struct pca955x_led *pca955x_led; struct pca955x_led *pca955x_led;
struct pca955x_chipdef *chip; struct pca955x_chipdef *chip;
struct i2c_adapter *adapter; struct i2c_adapter *adapter;
struct led_platform_data *pdata;
int i, err; int i, err;
struct pca955x_platform_data *pdata;
int ngpios = 0;
if (id) { if (id) {
chip = &pca955x_chipdefs[id->driver_data]; chip = &pca955x_chipdefs[id->driver_data];
...@@ -272,6 +455,11 @@ static int pca955x_probe(struct i2c_client *client, ...@@ -272,6 +455,11 @@ static int pca955x_probe(struct i2c_client *client,
} }
adapter = to_i2c_adapter(client->dev.parent); adapter = to_i2c_adapter(client->dev.parent);
pdata = dev_get_platdata(&client->dev); 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 */ /* Make sure the slave address / chip type combo given is possible */
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
...@@ -288,13 +476,11 @@ static int pca955x_probe(struct i2c_client *client, ...@@ -288,13 +476,11 @@ static int pca955x_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO; return -EIO;
if (pdata) { if (pdata->num_leds != chip->bits) {
if (pdata->num_leds != chip->bits) { dev_err(&client->dev,
dev_err(&client->dev, "board info claims %d LEDs" "board info claims %d LEDs on a %d-bit chip\n",
" on a %d-bit chip\n", pdata->num_leds, chip->bits);
pdata->num_leds, chip->bits); return -ENODEV;
return -ENODEV;
}
} }
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL); pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
...@@ -316,60 +502,92 @@ static int pca955x_probe(struct i2c_client *client, ...@@ -316,60 +502,92 @@ static int pca955x_probe(struct i2c_client *client,
pca955x_led = &pca955x->leds[i]; pca955x_led = &pca955x->leds[i];
pca955x_led->led_num = i; pca955x_led->led_num = i;
pca955x_led->pca955x = pca955x; 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) if (pdata->leds[i].default_trigger)
pca955x_led->led_cdev.default_trigger = pca955x_led->led_cdev.default_trigger =
pdata->leds[i].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.name = pca955x_led->name;
pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set; pca955x_led->led_cdev.brightness_set_blocking =
pca955x_led_set;
err = led_classdev_register(&client->dev, err = devm_led_classdev_register(&client->dev,
&pca955x_led->led_cdev); &pca955x_led->led_cdev);
if (err < 0) if (err)
goto exit; return err;
}
/* Turn off LEDs */ /* Turn off LED */
for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF);
pca955x_write_ls(client, i, 0x55); if (err)
return err;
}
}
/* PWM0 is used for half brightness or 50% duty cycle */ /* 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 */ /* 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 */ /* Set to fast frequency so we do not see flashing */
pca955x_write_psc(client, 0, 0); err = pca955x_write_psc(client, 0, 0);
pca955x_write_psc(client, 1, 0); if (err)
return err;
return 0; err = pca955x_write_psc(client, 1, 0);
if (err)
exit: return err;
while (i--)
led_classdev_unregister(&pca955x->leds[i].led_cdev); #ifdef CONFIG_LEDS_PCA955X_GPIO
if (ngpios) {
return err; pca955x->gpio.label = "gpio-pca955x";
} pca955x->gpio.direction_input = pca955x_gpio_direction_input;
pca955x->gpio.direction_output = pca955x_gpio_direction_output;
static int pca955x_remove(struct i2c_client *client) pca955x->gpio.set = pca955x_gpio_set_value;
{ pca955x->gpio.get = pca955x_gpio_get_value;
struct pca955x *pca955x = i2c_get_clientdata(client); pca955x->gpio.request = pca955x_gpio_request_pin;
int i; pca955x->gpio.can_sleep = 1;
pca955x->gpio.base = -1;
for (i = 0; i < pca955x->chipdef->bits; i++) pca955x->gpio.ngpio = ngpios;
led_classdev_unregister(&pca955x->leds[i].led_cdev); pca955x->gpio.parent = &client->dev;
pca955x->gpio.owner = THIS_MODULE;
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; return 0;
} }
...@@ -378,9 +596,9 @@ static struct i2c_driver pca955x_driver = { ...@@ -378,9 +596,9 @@ static struct i2c_driver pca955x_driver = {
.driver = { .driver = {
.name = "leds-pca955x", .name = "leds-pca955x",
.acpi_match_table = ACPI_PTR(pca955x_acpi_ids), .acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
.of_match_table = of_match_ptr(of_pca955x_match),
}, },
.probe = pca955x_probe, .probe = pca955x_probe,
.remove = pca955x_remove,
.id_table = pca955x_id, .id_table = pca955x_id,
}; };
......
...@@ -224,12 +224,8 @@ static int powernv_led_create(struct device *dev, ...@@ -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->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
powernv_led->loc_code, powernv_led->loc_code,
led_type_desc); led_type_desc);
if (!powernv_led->cdev.name) { if (!powernv_led->cdev.name)
dev_err(dev,
"%s: Memory allocation failed for classdev name\n",
__func__);
return -ENOMEM; return -ENOMEM;
}
powernv_led->cdev.brightness_set_blocking = powernv_brightness_set; powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
powernv_led->cdev.brightness_get = powernv_brightness_get; powernv_led->cdev.brightness_get = powernv_brightness_get;
......
...@@ -230,12 +230,15 @@ tlc591xx_probe(struct i2c_client *client, ...@@ -230,12 +230,15 @@ tlc591xx_probe(struct i2c_client *client,
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
err = of_property_read_u32(child, "reg", &reg); err = of_property_read_u32(child, "reg", &reg);
if (err) if (err) {
of_node_put(child);
return err; return err;
if (reg < 0 || reg >= tlc591xx->max_leds) }
return -EINVAL; if (reg < 0 || reg >= tlc591xx->max_leds ||
if (priv->leds[reg].active) priv->leds[reg].active) {
of_node_put(child);
return -EINVAL; return -EINVAL;
}
priv->leds[reg].active = true; priv->leds[reg].active = true;
priv->leds[reg].ldev.name = priv->leds[reg].ldev.name =
of_get_property(child, "label", NULL) ? : child->name; of_get_property(child, "label", NULL) ? : child->name;
......
/*
* 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 { ...@@ -49,6 +49,7 @@ struct led_classdev {
#define LED_HW_PLUGGABLE (1 << 19) #define LED_HW_PLUGGABLE (1 << 19)
#define LED_PANIC_INDICATOR (1 << 20) #define LED_PANIC_INDICATOR (1 << 20)
#define LED_BRIGHT_HW_CHANGED (1 << 21) #define LED_BRIGHT_HW_CHANGED (1 << 21)
#define LED_RETAIN_AT_SHUTDOWN (1 << 22)
/* set_brightness_work / blink_timer flags, atomic, private. */ /* set_brightness_work / blink_timer flags, atomic, private. */
unsigned long work_flags; unsigned long work_flags;
...@@ -392,6 +393,7 @@ struct gpio_led { ...@@ -392,6 +393,7 @@ struct gpio_led {
unsigned retain_state_suspended : 1; unsigned retain_state_suspended : 1;
unsigned panic_indicator : 1; unsigned panic_indicator : 1;
unsigned default_state : 2; unsigned default_state : 2;
unsigned retain_state_shutdown : 1;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment