Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds

Pull LED subsystem update from Bryan Wu:
 "Basically this cycle is mostly cleanup for LED subsystem"

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds:
  leds: s3c24xx: Remove hardware.h inclusion
  leds: replace list_for_each with list_for_each_entry
  leds: kirkwood: Cleanup in header files
  leds: pwm: Remove a warning on non-DT platforms
  leds: leds-pwm: fix duty time overflow.
  leds: leds-mc13783: Remove unneeded mc13xxx_{un}lock
  leds: leds-mc13783: Remove duplicate field in platform data
  drivers: leds: leds-tca6507: check CONFIG_GPIOLIB whether defined for 'gpio_base'
  leds: lp5523: Support LED MUX configuration on running a pattern
  leds: lp5521/5523: Fix multiple engine usage bug
  LEDS: tca6507 - fix up some comments.
  LEDS: tca6507: add device-tree support for GPIO configuration.
  LEDS: tca6507 - fix bugs in parsing of device-tree configuration.
This commit is contained in:
Linus Torvalds 2014-01-28 18:53:01 -08:00
commit 268943fb75
14 changed files with 226 additions and 230 deletions

View file

@ -2,6 +2,13 @@ LEDs connected to tca6507
Required properties: Required properties:
- compatible : should be : "ti,tca6507". - compatible : should be : "ti,tca6507".
- #address-cells: must be 1
- #size-cells: must be 0
- reg: typically 0x45.
Optional properties:
- gpio-controller: allows lines to be used as output-only GPIOs.
- #gpio-cells: if present, must be 0.
Each led is represented as a sub-node of the ti,tca6507 device. Each led is represented as a sub-node of the ti,tca6507 device.
@ -10,6 +17,7 @@ LED sub-node properties:
- reg : number of LED line (could be from 0 to 6) - reg : number of LED line (could be from 0 to 6)
- linux,default-trigger : (optional) - linux,default-trigger : (optional)
see Documentation/devicetree/bindings/leds/common.txt see Documentation/devicetree/bindings/leds/common.txt
- compatible: either "led" (the default) or "gpio".
Examples: Examples:
@ -19,6 +27,9 @@ tca6507@45 {
#size-cells = <0>; #size-cells = <0>;
reg = <0x45>; reg = <0x45>;
gpio-controller;
#gpio-cells = <2>;
led0: red-aux@0 { led0: red-aux@0 {
label = "red:aux"; label = "red:aux";
reg = <0x0>; reg = <0x0>;
@ -29,5 +40,10 @@ tca6507@45 {
reg = <0x5>; reg = <0x5>;
linux,default-trigger = "default-on"; linux,default-trigger = "default-on";
}; };
wifi-reset@6 {
reg = <0x6>;
compatible = "gpio";
};
}; };

View file

@ -73,6 +73,10 @@ select_engine : Select which engine is used for running program
run_engine : Start program which is loaded via the firmware interface run_engine : Start program which is loaded via the firmware interface
firmware : Load program data firmware : Load program data
In case of LP5523, one more command is required, 'enginex_leds'.
It is used for selecting LED output(s) at each engine number.
In more details, please refer to 'leds-lp5523.txt'.
For example, run blinking pattern in engine #1 of LP5521 For example, run blinking pattern in engine #1 of LP5521
echo 1 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp5521/loading echo 1 > /sys/class/firmware/lp5521/loading
@ -81,10 +85,12 @@ echo 0 > /sys/class/firmware/lp5521/loading
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
For example, run blinking pattern in engine #3 of LP55231 For example, run blinking pattern in engine #3 of LP55231
Two LEDs are configured as pattern output channels.
echo 3 > /sys/bus/i2c/devices/xxxx/select_engine echo 3 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp55231/loading echo 1 > /sys/class/firmware/lp55231/loading
echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data
echo 0 > /sys/class/firmware/lp55231/loading echo 0 > /sys/class/firmware/lp55231/loading
echo "000001100" > /sys/bus/i2c/devices/xxxx/engine3_leds
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
To start blinking patterns in engine #2 and #3 simultaneously, To start blinking patterns in engine #2 and #3 simultaneously,
@ -99,17 +105,19 @@ done
echo 1 > /sys/class/leds/red/device/run_engine echo 1 > /sys/class/leds/red/device/run_engine
Here is another example for LP5523. Here is another example for LP5523.
Full LED strings are selected by 'engine2_leds'.
echo 2 > /sys/bus/i2c/devices/xxxx/select_engine echo 2 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp5523/loading echo 1 > /sys/class/firmware/lp5523/loading
echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data
echo 0 > /sys/class/firmware/lp5523/loading echo 0 > /sys/class/firmware/lp5523/loading
echo "111111111" > /sys/bus/i2c/devices/xxxx/engine2_leds
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
As soon as 'loading' is set to 0, registered callback is called. As soon as 'loading' is set to 0, registered callback is called.
Inside the callback, the selected engine is loaded and memory is updated. Inside the callback, the selected engine is loaded and memory is updated.
To run programmed pattern, 'run_engine' attribute should be enabled. To run programmed pattern, 'run_engine' attribute should be enabled.
The pattern sqeuence of LP8501 is same as LP5523. The pattern sqeuence of LP8501 is similar to LP5523.
However pattern data is specific. However pattern data is specific.
Ex 1) Engine 1 is used Ex 1) Engine 1 is used
echo 1 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/bus/i2c/devices/xxxx/select_engine

View file

@ -236,32 +236,26 @@ static struct mc13xxx_led_platform_data moboard_led[] = {
{ {
.id = MC13783_LED_R1, .id = MC13783_LED_R1,
.name = "coreboard-led-4:red", .name = "coreboard-led-4:red",
.max_current = 2,
}, },
{ {
.id = MC13783_LED_G1, .id = MC13783_LED_G1,
.name = "coreboard-led-4:green", .name = "coreboard-led-4:green",
.max_current = 2,
}, },
{ {
.id = MC13783_LED_B1, .id = MC13783_LED_B1,
.name = "coreboard-led-4:blue", .name = "coreboard-led-4:blue",
.max_current = 2,
}, },
{ {
.id = MC13783_LED_R2, .id = MC13783_LED_R2,
.name = "coreboard-led-5:red", .name = "coreboard-led-5:red",
.max_current = 3,
}, },
{ {
.id = MC13783_LED_G2, .id = MC13783_LED_G2,
.name = "coreboard-led-5:green", .name = "coreboard-led-5:green",
.max_current = 3,
}, },
{ {
.id = MC13783_LED_B2, .id = MC13783_LED_B2,
.name = "coreboard-led-5:blue", .name = "coreboard-led-5:blue",
.max_current = 3,
}, },
}; };
@ -271,8 +265,14 @@ static struct mc13xxx_leds_platform_data moboard_leds = {
.led_control[0] = MC13783_LED_C0_ENABLE | MC13783_LED_C0_ABMODE(0), .led_control[0] = MC13783_LED_C0_ENABLE | MC13783_LED_C0_ABMODE(0),
.led_control[1] = MC13783_LED_C1_SLEWLIM, .led_control[1] = MC13783_LED_C1_SLEWLIM,
.led_control[2] = MC13783_LED_C2_SLEWLIM, .led_control[2] = MC13783_LED_C2_SLEWLIM,
.led_control[3] = MC13783_LED_C3_PERIOD(0), .led_control[3] = MC13783_LED_C3_PERIOD(0) |
.led_control[4] = MC13783_LED_C3_PERIOD(0), MC13783_LED_C3_CURRENT_R1(2) |
MC13783_LED_C3_CURRENT_G1(2) |
MC13783_LED_C3_CURRENT_B1(2),
.led_control[4] = MC13783_LED_C4_PERIOD(0) |
MC13783_LED_C4_CURRENT_R2(3) |
MC13783_LED_C4_CURRENT_G2(3) |
MC13783_LED_C4_CURRENT_B2(3),
}; };
static struct mc13xxx_buttons_platform_data moboard_buttons = { static struct mc13xxx_buttons_platform_data moboard_buttons = {

View file

@ -242,18 +242,14 @@ EXPORT_SYMBOL_GPL(led_trigger_unregister);
void led_trigger_event(struct led_trigger *trig, void led_trigger_event(struct led_trigger *trig,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct list_head *entry; struct led_classdev *led_cdev;
if (!trig) if (!trig)
return; return;
read_lock(&trig->leddev_list_lock); read_lock(&trig->leddev_list_lock);
list_for_each(entry, &trig->led_cdevs) { list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
led_set_brightness(led_cdev, brightness); led_set_brightness(led_cdev, brightness);
}
read_unlock(&trig->leddev_list_lock); read_unlock(&trig->leddev_list_lock);
} }
EXPORT_SYMBOL_GPL(led_trigger_event); EXPORT_SYMBOL_GPL(led_trigger_event);
@ -264,16 +260,13 @@ static void led_trigger_blink_setup(struct led_trigger *trig,
int oneshot, int oneshot,
int invert) int invert)
{ {
struct list_head *entry; struct led_classdev *led_cdev;
if (!trig) if (!trig)
return; return;
read_lock(&trig->leddev_list_lock); read_lock(&trig->leddev_list_lock);
list_for_each(entry, &trig->led_cdevs) { list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
if (oneshot) if (oneshot)
led_blink_set_oneshot(led_cdev, delay_on, delay_off, led_blink_set_oneshot(led_cdev, delay_on, delay_off,
invert); invert);

View file

@ -152,12 +152,26 @@ static void lp5521_load_engine(struct lp55xx_chip *chip)
lp5521_wait_opmode_done(); lp5521_wait_opmode_done();
} }
static void lp5521_stop_engine(struct lp55xx_chip *chip) static void lp5521_stop_all_engines(struct lp55xx_chip *chip)
{ {
lp55xx_write(chip, LP5521_REG_OP_MODE, 0); lp55xx_write(chip, LP5521_REG_OP_MODE, 0);
lp5521_wait_opmode_done(); lp5521_wait_opmode_done();
} }
static void lp5521_stop_engine(struct lp55xx_chip *chip)
{
enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = {
[LP55XX_ENGINE_1] = LP5521_MODE_R_M,
[LP55XX_ENGINE_2] = LP5521_MODE_G_M,
[LP55XX_ENGINE_3] = LP5521_MODE_B_M,
};
lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], 0);
lp5521_wait_opmode_done();
}
static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
{ {
int ret; int ret;
@ -564,7 +578,7 @@ static int lp5521_remove(struct i2c_client *client)
struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
lp5521_stop_engine(chip); lp5521_stop_all_engines(chip);
lp55xx_unregister_sysfs(chip); lp55xx_unregister_sysfs(chip);
lp55xx_unregister_leds(led, chip); lp55xx_unregister_leds(led, chip);
lp55xx_deinit_device(chip); lp55xx_deinit_device(chip);

View file

@ -195,12 +195,26 @@ static void lp5523_load_engine_and_select_page(struct lp55xx_chip *chip)
lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]);
} }
static void lp5523_stop_engine(struct lp55xx_chip *chip) static void lp5523_stop_all_engines(struct lp55xx_chip *chip)
{ {
lp55xx_write(chip, LP5523_REG_OP_MODE, 0); lp55xx_write(chip, LP5523_REG_OP_MODE, 0);
lp5523_wait_opmode_done(); lp5523_wait_opmode_done();
} }
static void lp5523_stop_engine(struct lp55xx_chip *chip)
{
enum lp55xx_engine_index idx = chip->engine_idx;
u8 mask[] = {
[LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M,
[LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M,
[LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M,
};
lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], 0);
lp5523_wait_opmode_done();
}
static void lp5523_turn_off_channels(struct lp55xx_chip *chip) static void lp5523_turn_off_channels(struct lp55xx_chip *chip)
{ {
int i; int i;
@ -311,7 +325,7 @@ static int lp5523_init_program_engine(struct lp55xx_chip *chip)
} }
out: out:
lp5523_stop_engine(chip); lp5523_stop_all_engines(chip);
return ret; return ret;
} }
@ -782,7 +796,7 @@ static int lp5523_remove(struct i2c_client *client)
struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
lp5523_stop_engine(chip); lp5523_stop_all_engines(chip);
lp55xx_unregister_sysfs(chip); lp55xx_unregister_sysfs(chip);
lp55xx_unregister_leds(led, chip); lp55xx_unregister_leds(led, chip);
lp55xx_deinit_device(chip); lp55xx_deinit_device(chip);

View file

@ -210,6 +210,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
{ {
struct lp55xx_chip *chip = context; struct lp55xx_chip *chip = context;
struct device *dev = &chip->cl->dev; struct device *dev = &chip->cl->dev;
enum lp55xx_engine_index idx = chip->engine_idx;
if (!fw) { if (!fw) {
dev_err(dev, "firmware request failed\n"); dev_err(dev, "firmware request failed\n");
@ -219,6 +220,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
/* handling firmware data is chip dependent */ /* handling firmware data is chip dependent */
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
chip->engines[idx - 1].mode = LP55XX_ENGINE_LOAD;
chip->fw = fw; chip->fw = fw;
if (chip->cfg->firmware_cb) if (chip->cfg->firmware_cb)
chip->cfg->firmware_cb(chip); chip->cfg->firmware_cb(chip);

View file

@ -117,9 +117,7 @@ static void mc13xxx_led_work(struct work_struct *work)
BUG(); BUG();
} }
mc13xxx_lock(led->master);
mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift); mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift);
mc13xxx_unlock(led->master);
} }
static void mc13xxx_led_set(struct led_classdev *led_cdev, static void mc13xxx_led_set(struct led_classdev *led_cdev,
@ -132,75 +130,6 @@ static void mc13xxx_led_set(struct led_classdev *led_cdev,
schedule_work(&led->work); schedule_work(&led->work);
} }
static int __init mc13xxx_led_setup(struct mc13xxx_led *led, int max_current)
{
int shift, mask, reg, ret, bank;
switch (led->id) {
case MC13783_LED_MD:
reg = MC13XXX_REG_LED_CONTROL(2);
shift = 0;
mask = 0x07;
break;
case MC13783_LED_AD:
reg = MC13XXX_REG_LED_CONTROL(2);
shift = 3;
mask = 0x07;
break;
case MC13783_LED_KP:
reg = MC13XXX_REG_LED_CONTROL(2);
shift = 6;
mask = 0x07;
break;
case MC13783_LED_R1:
case MC13783_LED_G1:
case MC13783_LED_B1:
case MC13783_LED_R2:
case MC13783_LED_G2:
case MC13783_LED_B2:
case MC13783_LED_R3:
case MC13783_LED_G3:
case MC13783_LED_B3:
bank = (led->id - MC13783_LED_R1) / 3;
reg = MC13XXX_REG_LED_CONTROL(3) + bank;
shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2;
mask = 0x03;
break;
case MC13892_LED_MD:
reg = MC13XXX_REG_LED_CONTROL(0);
shift = 9;
mask = 0x07;
break;
case MC13892_LED_AD:
reg = MC13XXX_REG_LED_CONTROL(0);
shift = 21;
mask = 0x07;
break;
case MC13892_LED_KP:
reg = MC13XXX_REG_LED_CONTROL(1);
shift = 9;
mask = 0x07;
break;
case MC13892_LED_R:
case MC13892_LED_G:
case MC13892_LED_B:
bank = (led->id - MC13892_LED_R) / 2;
reg = MC13XXX_REG_LED_CONTROL(2) + bank;
shift = ((led->id - MC13892_LED_R) - bank * 2) * 12 + 9;
mask = 0x07;
break;
default:
BUG();
}
mc13xxx_lock(led->master);
ret = mc13xxx_reg_rmw(led->master, reg, mask << shift,
max_current << shift);
mc13xxx_unlock(led->master);
return ret;
}
static int __init mc13xxx_led_probe(struct platform_device *pdev) static int __init mc13xxx_led_probe(struct platform_device *pdev)
{ {
struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
@ -233,31 +162,22 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
leds->num_leds = num_leds; leds->num_leds = num_leds;
platform_set_drvdata(pdev, leds); platform_set_drvdata(pdev, leds);
mc13xxx_lock(mcdev);
for (i = 0; i < devtype->num_regs; i++) { for (i = 0; i < devtype->num_regs; i++) {
reg = pdata->led_control[i]; reg = pdata->led_control[i];
WARN_ON(reg >= (1 << 24)); WARN_ON(reg >= (1 << 24));
ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg); ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg);
if (ret) if (ret)
break;
}
mc13xxx_unlock(mcdev);
if (ret) {
dev_err(&pdev->dev, "Unable to init LED driver\n");
return ret; return ret;
} }
for (i = 0; i < num_leds; i++) { for (i = 0; i < num_leds; i++) {
const char *name, *trig; const char *name, *trig;
char max_current;
ret = -EINVAL; ret = -EINVAL;
id = pdata->led[i].id; id = pdata->led[i].id;
name = pdata->led[i].name; name = pdata->led[i].name;
trig = pdata->led[i].default_trigger; trig = pdata->led[i].default_trigger;
max_current = pdata->led[i].max_current;
if ((id > devtype->led_max) || (id < devtype->led_min)) { if ((id > devtype->led_max) || (id < devtype->led_min)) {
dev_err(&pdev->dev, "Invalid ID %i\n", id); dev_err(&pdev->dev, "Invalid ID %i\n", id);
@ -280,11 +200,6 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
INIT_WORK(&leds->led[i].work, mc13xxx_led_work); INIT_WORK(&leds->led[i].work, mc13xxx_led_work);
ret = mc13xxx_led_setup(&leds->led[i], max_current);
if (ret) {
dev_err(&pdev->dev, "Unable to setup LED %i\n", id);
break;
}
ret = led_classdev_register(pdev->dev.parent, ret = led_classdev_register(pdev->dev.parent,
&leds->led[i].cdev); &leds->led[i].cdev);
if (ret) { if (ret) {
@ -313,10 +228,8 @@ static int mc13xxx_led_remove(struct platform_device *pdev)
cancel_work_sync(&leds->led[i].work); cancel_work_sync(&leds->led[i].work);
} }
mc13xxx_lock(mcdev);
for (i = 0; i < leds->devtype->num_regs; i++) for (i = 0; i < leds->devtype->num_regs; i++)
mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0); mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0);
mc13xxx_unlock(mcdev);
return 0; return 0;
} }

View file

@ -66,9 +66,11 @@ static void led_pwm_set(struct led_classdev *led_cdev,
struct led_pwm_data *led_dat = struct led_pwm_data *led_dat =
container_of(led_cdev, struct led_pwm_data, cdev); container_of(led_cdev, struct led_pwm_data, cdev);
unsigned int max = led_dat->cdev.max_brightness; unsigned int max = led_dat->cdev.max_brightness;
unsigned int period = led_dat->period; unsigned long long duty = led_dat->period;
led_dat->duty = brightness * period / max; duty *= brightness;
do_div(duty, max);
led_dat->duty = duty;
if (led_dat->can_sleep) if (led_dat->can_sleep)
schedule_work(&led_dat->work); schedule_work(&led_dat->work);
@ -85,11 +87,10 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
static int led_pwm_create_of(struct platform_device *pdev, static int led_pwm_create_of(struct platform_device *pdev,
struct led_pwm_priv *priv) struct led_pwm_priv *priv)
{ {
struct device_node *node = pdev->dev.of_node;
struct device_node *child; struct device_node *child;
int ret; int ret;
for_each_child_of_node(node, child) { for_each_child_of_node(pdev->dev.of_node, child) {
struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
led_dat->cdev.name = of_get_property(child, "label", led_dat->cdev.name = of_get_property(child, "label",

View file

@ -18,11 +18,10 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/leds-s3c24xx.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h> #include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h> #include <plat/gpio-cfg.h>
#include <linux/platform_data/leds-s3c24xx.h>
/* our context */ /* our context */

View file

@ -4,77 +4,87 @@
* The TCA6507 is a programmable LED controller that can drive 7 * The TCA6507 is a programmable LED controller that can drive 7
* separate lines either by holding them low, or by pulsing them * separate lines either by holding them low, or by pulsing them
* with modulated width. * with modulated width.
* The modulation can be varied in a simple pattern to produce a blink or * The modulation can be varied in a simple pattern to produce a
* double-blink. * blink or double-blink.
* *
* This driver can configure each line either as a 'GPIO' which is out-only * This driver can configure each line either as a 'GPIO' which is
* (no pull-up) or as an LED with variable brightness and hardware-assisted * out-only (pull-up resistor required) or as an LED with variable
* blinking. * brightness and hardware-assisted blinking.
* *
* Apart from OFF and ON there are three programmable brightness levels which * Apart from OFF and ON there are three programmable brightness
* can be programmed from 0 to 15 and indicate how many 500usec intervals in * levels which can be programmed from 0 to 15 and indicate how many
* each 8msec that the led is 'on'. The levels are named MASTER, BANK0 and * 500usec intervals in each 8msec that the led is 'on'. The levels
* BANK1. * are named MASTER, BANK0 and BANK1.
* *
* There are two different blink rates that can be programmed, each with * There are two different blink rates that can be programmed, each
* separate time for rise, on, fall, off and second-off. Thus if 3 or more * with separate time for rise, on, fall, off and second-off. Thus if
* different non-trivial rates are required, software must be used for the extra * 3 or more different non-trivial rates are required, software must
* rates. The two different blink rates must align with the two levels BANK0 and * be used for the extra rates. The two different blink rates must
* BANK1. * align with the two levels BANK0 and BANK1. This driver does not
* This driver does not support double-blink so 'second-off' always matches * support double-blink so 'second-off' always matches 'off'.
* 'off'.
* *
* Only 16 different times can be programmed in a roughly logarithmic scale from * Only 16 different times can be programmed in a roughly logarithmic
* 64ms to 16320ms. To be precise the possible times are: * scale from 64ms to 16320ms. To be precise the possible times are:
* 0, 64, 128, 192, 256, 384, 512, 768, * 0, 64, 128, 192, 256, 384, 512, 768,
* 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320 * 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320
* *
* Times that cannot be closely matched with these must be * Times that cannot be closely matched with these must be handled in
* handled in software. This driver allows 12.5% error in matching. * software. This driver allows 12.5% error in matching.
* *
* This driver does not allow rise/fall rates to be set explicitly. When trying * This driver does not allow rise/fall rates to be set explicitly.
* to match a given 'on' or 'off' period, an appropriate pair of 'change' and * When trying to match a given 'on' or 'off' period, an appropriate
* 'hold' times are chosen to get a close match. If the target delay is even, * pair of 'change' and 'hold' times are chosen to get a close match.
* the 'change' number will be the smaller; if odd, the 'hold' number will be * If the target delay is even, the 'change' number will be the
* the smaller. * smaller; if odd, the 'hold' number will be the smaller.
* Choosing pairs of delays with 12.5% errors allows us to match delays in the * Choosing pairs of delays with 12.5% errors allows us to match
* ranges: 56-72, 112-144, 168-216, 224-27504, 28560-36720. * delays in the ranges: 56-72, 112-144, 168-216, 224-27504,
* 26% of the achievable sums can be matched by multiple pairings. For example * 28560-36720.
* 1536 == 1536+0, 1024+512, or 768+768. This driver will always choose the * 26% of the achievable sums can be matched by multiple pairings.
* pairing with the least maximum - 768+768 in this case. Other pairings are * For example 1536 == 1536+0, 1024+512, or 768+768.
* not available. * This driver will always choose the pairing with the least
* maximum - 768+768 in this case. Other pairings are not available.
* *
* Access to the 3 levels and 2 blinks are on a first-come, first-served basis. * Access to the 3 levels and 2 blinks are on a first-come,
* Access can be shared by multiple leds if they have the same level and * first-served basis. Access can be shared by multiple leds if they
* either same blink rates, or some don't blink. * have the same level and either same blink rates, or some don't
* When a led changes, it relinquishes access and tries again, so it might * blink. When a led changes, it relinquishes access and tries again,
* lose access to hardware blink. * so it might lose access to hardware blink.
* If a blink engine cannot be allocated, software blink is used.
* If the desired brightness cannot be allocated, the closest available non-zero
* brightness is used. As 'full' is always available, the worst case would be
* to have two different blink rates at '1', with Max at '2', then other leds
* will have to choose between '2' and '16'. Hopefully this is not likely.
* *
* Each bank (BANK0 and BANK1) has two usage counts - LEDs using the brightness * If a blink engine cannot be allocated, software blink is used. If
* and LEDs using the blink. It can only be reprogrammed when the appropriate * the desired brightness cannot be allocated, the closest available
* counter is zero. The MASTER level has a single usage count. * non-zero brightness is used. As 'full' is always available, the
* worst case would be to have two different blink rates at '1', with
* Max at '2', then other leds will have to choose between '2' and
* '16'. Hopefully this is not likely.
* *
* Each Led has programmable 'on' and 'off' time as milliseconds. With each * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the
* there is a flag saying if it was explicitly requested or defaulted. * brightness and LEDs using the blink. It can only be reprogrammed
* Similarly the banks know if each time was explicit or a default. Defaults * when the appropriate counter is zero. The MASTER level has a
* are permitted to be changed freely - they are not recognised when matching. * single usage count.
*
* Each LED has programmable 'on' and 'off' time as milliseconds.
* With each there is a flag saying if it was explicitly requested or
* defaulted. Similarly the banks know if each time was explicit or a
* default. Defaults are permitted to be changed freely - they are
* not recognised when matching.
* *
* *
* An led-tca6507 device must be provided with platform data. This data * An led-tca6507 device must be provided with platform data or
* lists for each output: the name, default trigger, and whether the signal * configured via devicetree.
* is being used as a GPiO rather than an led. 'struct led_plaform_data'
* is used for this. If 'name' is NULL, the output isn't used. If 'flags'
* is TCA6507_MAKE_CPIO, the output is a GPO.
* The "struct led_platform_data" can be embedded in a
* "struct tca6507_platform_data" which adds a 'gpio_base' for the GPiOs,
* and a 'setup' callback which is called once the GPiOs are available.
* *
* The platform-data lists for each output: the name, default trigger,
* and whether the signal is being used as a GPIO rather than an LED.
* 'struct led_plaform_data' is used for this. If 'name' is NULL, the
* output isn't used. If 'flags' is TCA6507_MAKE_GPIO, the output is
* a GPO. The "struct led_platform_data" can be embedded in a "struct
* tca6507_platform_data" which adds a 'gpio_base' for the GPIOs, and
* a 'setup' callback which is called once the GPIOs are available.
*
* When configured via devicetree there is one child for each output.
* The "reg" determines the output number and "compatible" determines
* whether it is an LED or a GPIO. "linux,default-trigger" can set a
* default trigger.
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -192,17 +202,18 @@ MODULE_DEVICE_TABLE(i2c, tca6507_id);
static int choose_times(int msec, int *c1p, int *c2p) static int choose_times(int msec, int *c1p, int *c2p)
{ {
/* /*
* Choose two timecodes which add to 'msec' as near as possible. * Choose two timecodes which add to 'msec' as near as
* The first returned is the 'on' or 'off' time. The second is to be * possible. The first returned is the 'on' or 'off' time.
* used as a 'fade-on' or 'fade-off' time. If 'msec' is even, * The second is to be used as a 'fade-on' or 'fade-off' time.
* the first will not be smaller than the second. If 'msec' is odd, * If 'msec' is even, the first will not be smaller than the
* the first will not be larger than the second. * second. If 'msec' is odd, the first will not be larger
* If we cannot get a sum within 1/8 of 'msec' fail with -EINVAL, * than the second.
* otherwise return the sum that was achieved, plus 1 if the first is * If we cannot get a sum within 1/8 of 'msec' fail with
* smaller. * -EINVAL, otherwise return the sum that was achieved, plus 1
* If two possibilities are equally good (e.g. 512+0, 256+256), choose * if the first is smaller.
* the first pair so there is more change-time visible (i.e. it is * If two possibilities are equally good (e.g. 512+0,
* softer). * 256+256), choose the first pair so there is more
* change-time visible (i.e. it is softer).
*/ */
int c1, c2; int c1, c2;
int tmax = msec * 9 / 8; int tmax = msec * 9 / 8;
@ -255,8 +266,8 @@ static int choose_times(int msec, int *c1p, int *c2p)
} }
/* /*
* Update the register file with the appropriate 3-bit state for * Update the register file with the appropriate 3-bit state for the
* the given led. * given led.
*/ */
static void set_select(struct tca6507_chip *tca, int led, int val) static void set_select(struct tca6507_chip *tca, int led, int val)
{ {
@ -274,9 +285,9 @@ static void set_select(struct tca6507_chip *tca, int led, int val)
} }
} }
/* Update the register file with the appropriate 4-bit code for /* Update the register file with the appropriate 4-bit code for one
* one bank or other. This can be used for timers, for levels, or * bank or other. This can be used for timers, for levels, or for
* for initialisation. * initialization.
*/ */
static void set_code(struct tca6507_chip *tca, int reg, int bank, int new) static void set_code(struct tca6507_chip *tca, int reg, int bank, int new)
{ {
@ -309,7 +320,7 @@ static void set_level(struct tca6507_chip *tca, int bank, int level)
tca->bank[bank].level = level; tca->bank[bank].level = level;
} }
/* Record all relevant time code for a given bank */ /* Record all relevant time codes for a given bank */
static void set_times(struct tca6507_chip *tca, int bank) static void set_times(struct tca6507_chip *tca, int bank)
{ {
int c1, c2; int c1, c2;
@ -317,7 +328,8 @@ static void set_times(struct tca6507_chip *tca, int bank)
result = choose_times(tca->bank[bank].ontime, &c1, &c2); result = choose_times(tca->bank[bank].ontime, &c1, &c2);
dev_dbg(&tca->client->dev, dev_dbg(&tca->client->dev,
"Chose on times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], "Chose on times %d(%d) %d(%d) for %dms\n",
c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].ontime); c2, time_codes[c2], tca->bank[bank].ontime);
set_code(tca, TCA6507_FADE_ON, bank, c2); set_code(tca, TCA6507_FADE_ON, bank, c2);
set_code(tca, TCA6507_FULL_ON, bank, c1); set_code(tca, TCA6507_FULL_ON, bank, c1);
@ -325,7 +337,8 @@ static void set_times(struct tca6507_chip *tca, int bank)
result = choose_times(tca->bank[bank].offtime, &c1, &c2); result = choose_times(tca->bank[bank].offtime, &c1, &c2);
dev_dbg(&tca->client->dev, dev_dbg(&tca->client->dev,
"Chose off times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], "Chose off times %d(%d) %d(%d) for %dms\n",
c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].offtime); c2, time_codes[c2], tca->bank[bank].offtime);
set_code(tca, TCA6507_FADE_OFF, bank, c2); set_code(tca, TCA6507_FADE_OFF, bank, c2);
set_code(tca, TCA6507_FIRST_OFF, bank, c1); set_code(tca, TCA6507_FIRST_OFF, bank, c1);
@ -373,7 +386,8 @@ static void led_release(struct tca6507_led *led)
static int led_prepare(struct tca6507_led *led) static int led_prepare(struct tca6507_led *led)
{ {
/* Assign this led to a bank, configuring that bank if necessary. */ /* Assign this led to a bank, configuring that bank if
* necessary. */
int level = TO_LEVEL(led->led_cdev.brightness); int level = TO_LEVEL(led->led_cdev.brightness);
struct tca6507_chip *tca = led->chip; struct tca6507_chip *tca = led->chip;
int c1, c2; int c1, c2;
@ -389,10 +403,10 @@ static int led_prepare(struct tca6507_led *led)
if (led->ontime == 0 || led->offtime == 0) { if (led->ontime == 0 || led->offtime == 0) {
/* /*
* Just set the brightness, choosing first usable bank. * Just set the brightness, choosing first usable
* If none perfect, choose best. * bank. If none perfect, choose best. Count
* Count backwards so we check MASTER bank first * backwards so we check MASTER bank first to avoid
* to avoid wasting a timer. * wasting a timer.
*/ */
int best = -1;/* full-on */ int best = -1;/* full-on */
int diff = 15-level; int diff = 15-level;
@ -433,9 +447,9 @@ static int led_prepare(struct tca6507_led *led)
} }
/* /*
* We have on/off time so we need to try to allocate a timing bank. * We have on/off time so we need to try to allocate a timing
* First check if times are compatible with hardware and give up if * bank. First check if times are compatible with hardware
* not. * and give up if not.
*/ */
if (choose_times(led->ontime, &c1, &c2) < 0) if (choose_times(led->ontime, &c1, &c2) < 0)
return -EINVAL; return -EINVAL;
@ -523,8 +537,8 @@ static int led_assign(struct tca6507_led *led)
err = led_prepare(led); err = led_prepare(led);
if (err) { if (err) {
/* /*
* Can only fail on timer setup. In that case we need to * Can only fail on timer setup. In that case we need
* re-establish as steady level. * to re-establish as steady level.
*/ */
led->ontime = 0; led->ontime = 0;
led->offtime = 0; led->offtime = 0;
@ -594,8 +608,8 @@ static void tca6507_gpio_set_value(struct gpio_chip *gc,
spin_lock_irqsave(&tca->lock, flags); spin_lock_irqsave(&tca->lock, flags);
/* /*
* 'OFF' is floating high, and 'ON' is pulled down, so it has the * 'OFF' is floating high, and 'ON' is pulled down, so it has
* inverse sense of 'val'. * the inverse sense of 'val'.
*/ */
set_select(tca, tca->gpio_map[offset], set_select(tca, tca->gpio_map[offset],
val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON); val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON);
@ -638,6 +652,9 @@ static int tca6507_probe_gpios(struct i2c_client *client,
tca->gpio.direction_output = tca6507_gpio_direction_output; tca->gpio.direction_output = tca6507_gpio_direction_output;
tca->gpio.set = tca6507_gpio_set_value; tca->gpio.set = tca6507_gpio_set_value;
tca->gpio.dev = &client->dev; tca->gpio.dev = &client->dev;
#ifdef CONFIG_OF_GPIO
tca->gpio.of_node = of_node_get(client->dev.of_node);
#endif
err = gpiochip_add(&tca->gpio); err = gpiochip_add(&tca->gpio);
if (err) { if (err) {
tca->gpio.ngpio = 0; tca->gpio.ngpio = 0;
@ -682,7 +699,7 @@ tca6507_led_dt_init(struct i2c_client *client)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
tca_leds = devm_kzalloc(&client->dev, tca_leds = devm_kzalloc(&client->dev,
sizeof(struct led_info) * count, GFP_KERNEL); sizeof(struct led_info) * NUM_LEDS, GFP_KERNEL);
if (!tca_leds) if (!tca_leds)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -695,9 +712,11 @@ tca6507_led_dt_init(struct i2c_client *client)
of_get_property(child, "label", NULL) ? : child->name; of_get_property(child, "label", NULL) ? : child->name;
led.default_trigger = led.default_trigger =
of_get_property(child, "linux,default-trigger", NULL); of_get_property(child, "linux,default-trigger", NULL);
led.flags = 0;
if (of_property_match_string(child, "compatible", "gpio") >= 0)
led.flags |= TCA6507_MAKE_GPIO;
ret = of_property_read_u32(child, "reg", &reg); ret = of_property_read_u32(child, "reg", &reg);
if (ret != 0) if (ret != 0 || reg < 0 || reg >= NUM_LEDS)
continue; continue;
tca_leds[reg] = led; tca_leds[reg] = led;
@ -708,8 +727,10 @@ tca6507_led_dt_init(struct i2c_client *client)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
pdata->leds.leds = tca_leds; pdata->leds.leds = tca_leds;
pdata->leds.num_leds = count; pdata->leds.num_leds = NUM_LEDS;
#ifdef CONFIG_GPIOLIB
pdata->gpio_base = -1;
#endif
return pdata; return pdata;
} }

View file

@ -110,9 +110,6 @@ struct mc13xxx_led_platform_data {
int id; int id;
const char *name; const char *name;
const char *default_trigger; const char *default_trigger;
/* Three or two bits current selection depending on the led */
char max_current;
}; };
#define MAX_LED_CONTROL_REGS 6 #define MAX_LED_CONTROL_REGS 6
@ -121,7 +118,7 @@ struct mc13xxx_leds_platform_data {
struct mc13xxx_led_platform_data *led; struct mc13xxx_led_platform_data *led;
int num_leds; int num_leds;
/* LED Control 0 */ /* MC13783 LED Control 0 */
#define MC13783_LED_C0_ENABLE (1 << 0) #define MC13783_LED_C0_ENABLE (1 << 0)
#define MC13783_LED_C0_TRIODE_MD (1 << 7) #define MC13783_LED_C0_TRIODE_MD (1 << 7)
#define MC13783_LED_C0_TRIODE_AD (1 << 8) #define MC13783_LED_C0_TRIODE_AD (1 << 8)
@ -129,21 +126,43 @@ struct mc13xxx_leds_platform_data {
#define MC13783_LED_C0_BOOST (1 << 10) #define MC13783_LED_C0_BOOST (1 << 10)
#define MC13783_LED_C0_ABMODE(x) (((x) & 0x7) << 11) #define MC13783_LED_C0_ABMODE(x) (((x) & 0x7) << 11)
#define MC13783_LED_C0_ABREF(x) (((x) & 0x3) << 14) #define MC13783_LED_C0_ABREF(x) (((x) & 0x3) << 14)
/* LED Control 1 */ /* MC13783 LED Control 1 */
#define MC13783_LED_C1_TC1HALF (1 << 18) #define MC13783_LED_C1_TC1HALF (1 << 18)
#define MC13783_LED_C1_SLEWLIM (1 << 23) #define MC13783_LED_C1_SLEWLIM (1 << 23)
/* LED Control 2 */ /* MC13783 LED Control 2 */
#define MC13783_LED_C2_CURRENT_MD(x) (((x) & 0x7) << 0)
#define MC13783_LED_C2_CURRENT_AD(x) (((x) & 0x7) << 3)
#define MC13783_LED_C2_CURRENT_KP(x) (((x) & 0x7) << 6)
#define MC13783_LED_C2_PERIOD(x) (((x) & 0x3) << 21) #define MC13783_LED_C2_PERIOD(x) (((x) & 0x3) << 21)
#define MC13783_LED_C2_SLEWLIM (1 << 23) #define MC13783_LED_C2_SLEWLIM (1 << 23)
/* LED Control 3 */ /* MC13783 LED Control 3 */
#define MC13783_LED_C3_CURRENT_R1(x) (((x) & 0x3) << 0)
#define MC13783_LED_C3_CURRENT_G1(x) (((x) & 0x3) << 2)
#define MC13783_LED_C3_CURRENT_B1(x) (((x) & 0x3) << 4)
#define MC13783_LED_C3_PERIOD(x) (((x) & 0x3) << 21) #define MC13783_LED_C3_PERIOD(x) (((x) & 0x3) << 21)
#define MC13783_LED_C3_TRIODE_TC1 (1 << 23) #define MC13783_LED_C3_TRIODE_TC1 (1 << 23)
/* LED Control 4 */ /* MC13783 LED Control 4 */
#define MC13783_LED_C4_CURRENT_R2(x) (((x) & 0x3) << 0)
#define MC13783_LED_C4_CURRENT_G2(x) (((x) & 0x3) << 2)
#define MC13783_LED_C4_CURRENT_B2(x) (((x) & 0x3) << 4)
#define MC13783_LED_C4_PERIOD(x) (((x) & 0x3) << 21) #define MC13783_LED_C4_PERIOD(x) (((x) & 0x3) << 21)
#define MC13783_LED_C4_TRIODE_TC2 (1 << 23) #define MC13783_LED_C4_TRIODE_TC2 (1 << 23)
/* LED Control 5 */ /* MC13783 LED Control 5 */
#define MC13783_LED_C5_CURRENT_R3(x) (((x) & 0x3) << 0)
#define MC13783_LED_C5_CURRENT_G3(x) (((x) & 0x3) << 2)
#define MC13783_LED_C5_CURRENT_B3(x) (((x) & 0x3) << 4)
#define MC13783_LED_C5_PERIOD(x) (((x) & 0x3) << 21) #define MC13783_LED_C5_PERIOD(x) (((x) & 0x3) << 21)
#define MC13783_LED_C5_TRIODE_TC3 (1 << 23) #define MC13783_LED_C5_TRIODE_TC3 (1 << 23)
/* MC13892 LED Control 0 */
#define MC13892_LED_C0_CURRENT_MD(x) (((x) & 0x7) << 9)
#define MC13892_LED_C0_CURRENT_AD(x) (((x) & 0x7) << 21)
/* MC13892 LED Control 1 */
#define MC13892_LED_C1_CURRENT_KP(x) (((x) & 0x7) << 9)
/* MC13892 LED Control 2 */
#define MC13892_LED_C2_CURRENT_R(x) (((x) & 0x7) << 9)
#define MC13892_LED_C2_CURRENT_G(x) (((x) & 0x7) << 21)
/* MC13892 LED Control 3 */
#define MC13892_LED_C3_CURRENT_B(x) (((x) & 0x7) << 9)
u32 led_control[MAX_LED_CONTROL_REGS]; u32 led_control[MAX_LED_CONTROL_REGS];
}; };

View file

@ -1,6 +1,4 @@
/* /*
* arch/arm/mach-kirkwood/include/mach/leds-netxbig.h
*
* Platform data structure for netxbig LED driver * Platform data structure for netxbig LED driver
* *
* This file is licensed under the terms of the GNU General Public * This file is licensed under the terms of the GNU General Public
@ -8,8 +6,8 @@
* warranty of any kind, whether express or implied. * warranty of any kind, whether express or implied.
*/ */
#ifndef __MACH_LEDS_NETXBIG_H #ifndef __LEDS_KIRKWOOD_NETXBIG_H
#define __MACH_LEDS_NETXBIG_H #define __LEDS_KIRKWOOD_NETXBIG_H
struct netxbig_gpio_ext { struct netxbig_gpio_ext {
unsigned *addr; unsigned *addr;
@ -52,4 +50,4 @@ struct netxbig_led_platform_data {
int num_leds; int num_leds;
}; };
#endif /* __MACH_LEDS_NETXBIG_H */ #endif /* __LEDS_KIRKWOOD_NETXBIG_H */

View file

@ -1,6 +1,4 @@
/* /*
* arch/arm/mach-kirkwood/include/mach/leds-ns2.h
*
* Platform data structure for Network Space v2 LED driver * Platform data structure for Network Space v2 LED driver
* *
* This file is licensed under the terms of the GNU General Public * This file is licensed under the terms of the GNU General Public
@ -8,8 +6,8 @@
* warranty of any kind, whether express or implied. * warranty of any kind, whether express or implied.
*/ */
#ifndef __MACH_LEDS_NS2_H #ifndef __LEDS_KIRKWOOD_NS2_H
#define __MACH_LEDS_NS2_H #define __LEDS_KIRKWOOD_NS2_H
struct ns2_led { struct ns2_led {
const char *name; const char *name;
@ -23,4 +21,4 @@ struct ns2_led_platform_data {
struct ns2_led *leds; struct ns2_led *leds;
}; };
#endif /* __MACH_LEDS_NS2_H */ #endif /* __LEDS_KIRKWOOD_NS2_H */