Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 May 2013 22:41:42 +0000 (15:41 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 May 2013 22:41:42 +0000 (15:41 -0700)
Pull LED subsystem updates from Bryan Wu:
 - move LED trigger drivers into a new directory
 - lp55xx common driver updates
 - other led drivers updates and bug fixing

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds:
  leds: leds-asic3: switch to using SIMPLE_DEV_PM_OPS
  leds: leds-bd2802: add CONFIG_PM_SLEEP to suspend/resume functions
  leds: lp55xx: configure the clock detection
  leds: lp55xx: use common clock framework when external clock is used
  leds: leds-ns2: fix oops at module removal
  leds: leds-pwm: Defer led_pwm_set() if PWM can sleep
  leds: lp55xx: fix the sysfs read operation
  leds: lm355x, lm3642: support camera LED triggers for flash and torch
  leds: add camera LED triggers
  leds: trigger: use inline functions instead of macros
  leds: tca6507: Use of_match_ptr() macro
  leds: wm8350: Complain if we fail to reenable DCDC
  leds: renesas: set gpio_request_one() flags param correctly
  leds: leds-ns2: set devm_gpio_request_one() flags param correctly
  leds: leds-lt3593: set devm_gpio_request_one() flags param correctly
  leds: leds-bd2802: remove erroneous __exit annotation
  leds: atmel-pwm: remove erroneous __exit annotation
  leds: move LED trigger drivers into new subdirectory
  leds: add new LP5562 LED driver

45 files changed:
Documentation/leds/00-INDEX
Documentation/leds/leds-lp5521.txt
Documentation/leds/leds-lp5562.txt [new file with mode: 0644]
Documentation/leds/leds-lp55xx.txt
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-asic3.c
drivers/leds/leds-atmel-pwm.c
drivers/leds/leds-bd2802.c
drivers/leds/leds-lm355x.c
drivers/leds/leds-lm3642.c
drivers/leds/leds-lp5521.c
drivers/leds/leds-lp5562.c [new file with mode: 0644]
drivers/leds/leds-lp55xx-common.c
drivers/leds/leds-lp55xx-common.h
drivers/leds/leds-lt3593.c
drivers/leds/leds-ns2.c
drivers/leds/leds-pwm.c
drivers/leds/leds-renesas-tpu.c
drivers/leds/leds-tca6507.c
drivers/leds/leds-wm8350.c
drivers/leds/ledtrig-backlight.c [deleted file]
drivers/leds/ledtrig-cpu.c [deleted file]
drivers/leds/ledtrig-default-on.c [deleted file]
drivers/leds/ledtrig-gpio.c [deleted file]
drivers/leds/ledtrig-heartbeat.c [deleted file]
drivers/leds/ledtrig-ide-disk.c [deleted file]
drivers/leds/ledtrig-oneshot.c [deleted file]
drivers/leds/ledtrig-timer.c [deleted file]
drivers/leds/ledtrig-transient.c [deleted file]
drivers/leds/trigger/Kconfig [new file with mode: 0644]
drivers/leds/trigger/Makefile [new file with mode: 0644]
drivers/leds/trigger/ledtrig-backlight.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-camera.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-cpu.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-default-on.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-gpio.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-heartbeat.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-ide-disk.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-oneshot.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-timer.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-transient.c [new file with mode: 0644]
include/linux/leds.h
include/linux/mmc/host.h
include/linux/platform_data/leds-lp55xx.h

index 5246090..1ecd159 100644 (file)
@@ -6,6 +6,8 @@ leds-lp5521.txt
        - notes on how to use the leds-lp5521 driver.
 leds-lp5523.txt
        - notes on how to use the leds-lp5523 driver.
+leds-lp5562.txt
+       - notes on how to use the leds-lp5562 driver.
 leds-lp55xx.txt
        - description about lp55xx common driver.
 leds-lm3556.txt
index 270f571..79e4c2e 100644 (file)
@@ -81,22 +81,3 @@ static struct lp55xx_platform_data lp5521_platform_data = {
 
 If the current is set to 0 in the platform data, that channel is
 disabled and it is not visible in the sysfs.
-
-The 'update_config' : CONFIG register (ADDR 08h)
-This value is platform-specific data.
-If update_config is not defined, the CONFIG register is set with
-'LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT'.
-(Enable auto-powersave, set charge pump to auto, red to battery)
-
-example of update_config :
-
-#define LP5521_CONFIGS (LP5521_PWM_HF | LP5521_PWRSAVE_EN | \
-                       LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \
-                       LP5521_CLK_INT)
-
-static struct lp55xx_platform_data lp5521_pdata = {
-       .led_config = lp5521_led_config,
-       .num_channels = ARRAY_SIZE(lp5521_led_config),
-       .clock_mode = LP55XX_CLOCK_INT,
-       .update_config = LP5521_CONFIGS,
-};
diff --git a/Documentation/leds/leds-lp5562.txt b/Documentation/leds/leds-lp5562.txt
new file mode 100644 (file)
index 0000000..5a823ff
--- /dev/null
@@ -0,0 +1,120 @@
+Kernel driver for LP5562
+========================
+
+* TI LP5562 LED Driver
+
+Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+
+Description
+
+  LP5562 can drive up to 4 channels. R/G/B and White.
+  LEDs can be controlled directly via the led class control interface.
+
+  All four channels can be also controlled using the engine micro programs.
+  LP5562 has the internal program memory for running various LED patterns.
+  For the details, please refer to 'firmware' section in leds-lp55xx.txt
+
+Device attribute: engine_mux
+
+  3 Engines are allocated in LP5562, but the number of channel is 4.
+  Therefore each channel should be mapped to the engine number.
+  Value : RGB or W
+
+  This attribute is used for programming LED data with the firmware interface.
+  Unlike the LP5521/LP5523/55231, LP5562 has unique feature for the engine mux,
+  so additional sysfs is required.
+
+  LED Map
+  Red   ... Engine 1 (fixed)
+  Green ... Engine 2 (fixed)
+  Blue  ... Engine 3 (fixed)
+  White ... Engine 1 or 2 or 3 (selective)
+
+How to load the program data using engine_mux
+
+  Before loading the LP5562 program data, engine_mux should be written between
+  the engine selection and loading the firmware.
+  Engine mux has two different mode, RGB and W.
+  RGB is used for loading RGB program data, W is used for W program data.
+
+  For example, run blinking green channel pattern,
+  echo 2 > /sys/bus/i2c/devices/xxxx/select_engine     # 2 is for green channel
+  echo "RGB" > /sys/bus/i2c/devices/xxxx/engine_mux    # engine mux for RGB
+  echo 1 > /sys/class/firmware/lp5562/loading
+  echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
+  echo 0 > /sys/class/firmware/lp5562/loading
+  echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
+
+  To run a blinking white pattern,
+  echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine
+  echo "W" > /sys/bus/i2c/devices/xxxx/engine_mux
+  echo 1 > /sys/class/firmware/lp5562/loading
+  echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
+  echo 0 > /sys/class/firmware/lp5562/loading
+  echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
+
+How to load the predefined patterns
+
+  Please refer to 'leds-lp55xx.txt"
+
+Setting Current of Each Channel
+
+  Like LP5521 and LP5523/55231, LP5562 provides LED current settings.
+  The 'led_current' and 'max_current' are used.
+
+(Example of Platform data)
+
+To configure the platform specific data, lp55xx_platform_data structure is used.
+
+static struct lp55xx_led_config lp5562_led_config[] = {
+       {
+               .name           = "R",
+               .chan_nr        = 0,
+               .led_current    = 20,
+               .max_current    = 40,
+       },
+       {
+               .name           = "G",
+               .chan_nr        = 1,
+               .led_current    = 20,
+               .max_current    = 40,
+       },
+       {
+               .name           = "B",
+               .chan_nr        = 2,
+               .led_current    = 20,
+               .max_current    = 40,
+       },
+       {
+               .name           = "W",
+               .chan_nr        = 3,
+               .led_current    = 20,
+               .max_current    = 40,
+       },
+};
+
+static int lp5562_setup(void)
+{
+       /* setup HW resources */
+}
+
+static void lp5562_release(void)
+{
+       /* Release HW resources */
+}
+
+static void lp5562_enable(bool state)
+{
+       /* Control of chip enable signal */
+}
+
+static struct lp55xx_platform_data lp5562_platform_data = {
+        .led_config     = lp5562_led_config,
+        .num_channels   = ARRAY_SIZE(lp5562_led_config),
+        .setup_resources   = lp5562_setup,
+        .release_resources = lp5562_release,
+        .enable            = lp5562_enable,
+};
+
+If the current is set to 0 in the platform data, that channel is
+disabled and it is not visible in the sysfs.
index ced4186..eec8fa2 100644 (file)
@@ -5,7 +5,7 @@ Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
 
 Description
 -----------
-LP5521, LP5523/55231 have common features as below.
+LP5521, LP5523/55231 and LP5562 have common features as below.
 
   Register access via the I2C
   Device initialization/deinitialization
@@ -116,3 +116,47 @@ To support this, 'run_engine' and 'firmware_cb' are configurable in each driver.
 run_engine  : Control the selected engine
 firmware_cb : The callback function after loading the firmware is done.
               Chip specific commands for loading and updating program memory.
+
+( Predefined pattern data )
+
+Without the firmware interface, LP55xx driver provides another method for
+loading a LED pattern. That is 'predefined' pattern.
+A predefined pattern is defined in the platform data and load it(or them)
+via the sysfs if needed.
+To use the predefined pattern concept, 'patterns' and 'num_patterns' should be
+configured.
+
+  Example of predefined pattern data:
+
+  /* mode_1: blinking data */
+  static const u8 mode_1[] = {
+               0x40, 0x00, 0x60, 0x00, 0x40, 0xFF, 0x60, 0x00,
+               };
+
+  /* mode_2: always on */
+  static const u8 mode_2[] = { 0x40, 0xFF, };
+
+  struct lp55xx_predef_pattern board_led_patterns[] = {
+       {
+               .r = mode_1,
+               .size_r = ARRAY_SIZE(mode_1),
+       },
+       {
+               .b = mode_2,
+               .size_b = ARRAY_SIZE(mode_2),
+       },
+  }
+
+  struct lp55xx_platform_data lp5562_pdata = {
+  ...
+       .patterns      = board_led_patterns,
+       .num_patterns  = ARRAY_SIZE(board_led_patterns),
+  };
+
+Then, mode_1 and mode_2 can be run via through the sysfs.
+
+  echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern    # red blinking LED pattern
+  echo 2 > /sys/bus/i2c/devices/xxxx/led_pattern    # blue LED always on
+
+To stop running pattern,
+  echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern
index ec50824..d44806d 100644 (file)
@@ -194,8 +194,8 @@ config LEDS_LP3944
          module will be called leds-lp3944.
 
 config LEDS_LP55XX_COMMON
-       tristate "Common Driver for TI/National LP5521 and LP5523/55231"
-       depends on LEDS_LP5521 || LEDS_LP5523
+       tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
+       depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
        select FW_LOADER
        help
          This option supports common operations for LP5521 and LP5523/55231
@@ -222,6 +222,16 @@ config LEDS_LP5523
          Driver provides direct control via LED class and interface for
          programming the engines.
 
+config LEDS_LP5562
+       tristate "LED Support for TI LP5562 LED driver chip"
+       depends on LEDS_CLASS && I2C
+       select LEDS_LP55XX_COMMON
+       help
+         If you say yes here you get support for TI LP5562 LED driver.
+         It is 4 channels chip with programmable engines.
+         Driver provides direct control via LED class and interface for
+         programming the engines.
+
 config LEDS_LP8788
        tristate "LED support for the TI LP8788 PMIC"
        depends on LEDS_CLASS
@@ -469,106 +479,7 @@ config LEDS_BLINKM
          This option enables support for the BlinkM RGB LED connected
          through I2C. Say Y to enable support for the BlinkM LED.
 
-config LEDS_TRIGGERS
-       bool "LED Trigger support"
-       depends on LEDS_CLASS
-       help
-         This option enables trigger support for the leds class.
-         These triggers allow kernel events to drive the LEDs and can
-         be configured via sysfs. If unsure, say Y.
-
 comment "LED Triggers"
-
-config LEDS_TRIGGER_TIMER
-       tristate "LED Timer Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be controlled by a programmable timer
-         via sysfs. Some LED hardware can be programmed to start
-         blinking the LED without any further software interaction.
-         For more details read Documentation/leds/leds-class.txt.
-
-         If unsure, say Y.
-
-config LEDS_TRIGGER_ONESHOT
-       tristate "LED One-shot Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to blink in one-shot pulses with parameters
-         controlled via sysfs.  It's useful to notify the user on
-         sporadic events, when there are no clear begin and end trap points,
-         or on dense events, where this blinks the LED at constant rate if
-         rearmed continuously.
-
-         It also shows how to use the led_blink_set_oneshot() function.
-
-         If unsure, say Y.
-
-config LEDS_TRIGGER_IDE_DISK
-       bool "LED IDE Disk Trigger"
-       depends on IDE_GD_ATA
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be controlled by IDE disk activity.
-         If unsure, say Y.
-
-config LEDS_TRIGGER_HEARTBEAT
-       tristate "LED Heartbeat Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be controlled by a CPU load average.
-         The flash frequency is a hyperbolic function of the 1-minute
-         load average.
-         If unsure, say Y.
-
-config LEDS_TRIGGER_BACKLIGHT
-       tristate "LED backlight Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be controlled as a backlight device: they
-         turn off and on when the display is blanked and unblanked.
-
-         If unsure, say N.
-
-config LEDS_TRIGGER_CPU
-       bool "LED CPU Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be controlled by active CPUs. This shows
-         the active CPUs across an array of LEDs so you can see which
-         CPUs are active on the system at any given moment.
-
-         If unsure, say N.
-
-config LEDS_TRIGGER_GPIO
-       tristate "LED GPIO Trigger"
-       depends on LEDS_TRIGGERS
-       depends on GPIOLIB
-       help
-         This allows LEDs to be controlled by gpio events. It's good
-         when using gpios as switches and triggering the needed LEDs
-         from there. One use case is n810's keypad LEDs that could
-         be triggered by this trigger when user slides up to show
-         keypad.
-
-         If unsure, say N.
-
-config LEDS_TRIGGER_DEFAULT_ON
-       tristate "LED Default ON Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows LEDs to be initialised in the ON state.
-         If unsure, say Y.
-
-comment "iptables trigger is under Netfilter config (LED target)"
-       depends on LEDS_TRIGGERS
-
-config LEDS_TRIGGER_TRANSIENT
-       tristate "LED Transient Trigger"
-       depends on LEDS_TRIGGERS
-       help
-         This allows one time activation of a transient state on
-         GPIO/PWM based hardware.
-         If unsure, say Y.
+source "drivers/leds/trigger/Kconfig"
 
 endif # NEW_LEDS
index 215e7e3..ac28977 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_LP3944)             += leds-lp3944.o
 obj-$(CONFIG_LEDS_LP55XX_COMMON)       += leds-lp55xx-common.o
 obj-$(CONFIG_LEDS_LP5521)              += leds-lp5521.o
 obj-$(CONFIG_LEDS_LP5523)              += leds-lp5523.o
+obj-$(CONFIG_LEDS_LP5562)              += leds-lp5562.o
 obj-$(CONFIG_LEDS_LP8788)              += leds-lp8788.o
 obj-$(CONFIG_LEDS_TCA6507)             += leds-tca6507.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)          += leds-clevo-mail.o
@@ -57,12 +58,4 @@ obj-$(CONFIG_LEDS_BLINKM)            += leds-blinkm.o
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
 
 # LED Triggers
-obj-$(CONFIG_LEDS_TRIGGER_TIMER)       += ledtrig-timer.o
-obj-$(CONFIG_LEDS_TRIGGER_ONESHOT)     += ledtrig-oneshot.o
-obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)    += ledtrig-ide-disk.o
-obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)   += ledtrig-heartbeat.o
-obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
-obj-$(CONFIG_LEDS_TRIGGER_GPIO)                += ledtrig-gpio.o
-obj-$(CONFIG_LEDS_TRIGGER_CPU)         += ledtrig-cpu.o
-obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)  += ledtrig-default-on.o
-obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)   += ledtrig-transient.o
+obj-$(CONFIG_LEDS_TRIGGERS)            += trigger/
index b474745..cf9efe4 100644 (file)
@@ -134,6 +134,7 @@ static int asic3_led_remove(struct platform_device *pdev)
        return mfd_cell_disable(pdev);
 }
 
+#ifdef CONFIG_PM_SLEEP
 static int asic3_led_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -159,11 +160,9 @@ static int asic3_led_resume(struct device *dev)
 
        return ret;
 }
+#endif
 
-static const struct dev_pm_ops asic3_led_pm_ops = {
-       .suspend        = asic3_led_suspend,
-       .resume         = asic3_led_resume,
-};
+static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume);
 
 static struct platform_driver asic3_led_driver = {
        .probe          = asic3_led_probe,
index 3867735..8a39c5b 100644 (file)
@@ -113,7 +113,7 @@ err:
        return status;
 }
 
-static int __exit pwmled_remove(struct platform_device *pdev)
+static int pwmled_remove(struct platform_device *pdev)
 {
        const struct gpio_led_platform_data     *pdata;
        struct pwmled                           *leds;
@@ -140,7 +140,7 @@ static struct platform_driver pwmled_driver = {
        },
        /* REVISIT add suspend() and resume() methods */
        .probe =        pwmled_probe,
-       .remove =       __exit_p(pwmled_remove),
+       .remove =       pwmled_remove,
 };
 
 module_platform_driver(pwmled_driver);
index 8515170..2db0423 100644 (file)
@@ -732,7 +732,7 @@ failed_unregister_dev_file:
        return ret;
 }
 
-static int __exit bd2802_remove(struct i2c_client *client)
+static int bd2802_remove(struct i2c_client *client)
 {
        struct bd2802_led *led = i2c_get_clientdata(client);
        int i;
@@ -747,8 +747,7 @@ static int __exit bd2802_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
 static void bd2802_restore_state(struct bd2802_led *led)
 {
        int i;
@@ -785,12 +784,9 @@ static int bd2802_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
 static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
-#define BD2802_PM (&bd2802_pm)
-#else          /* CONFIG_PM */
-#define BD2802_PM NULL
-#endif
 
 static const struct i2c_device_id bd2802_id[] = {
        { "BD2802", 0 },
@@ -801,10 +797,10 @@ MODULE_DEVICE_TABLE(i2c, bd2802_id);
 static struct i2c_driver bd2802_i2c_driver = {
        .driver = {
                .name   = "BD2802",
-               .pm     = BD2802_PM,
+               .pm     = &bd2802_pm,
        },
        .probe          = bd2802_probe,
-       .remove         = __exit_p(bd2802_remove),
+       .remove         = bd2802_remove,
        .id_table       = bd2802_id,
 };
 
index 4117235..d81a8e7 100644 (file)
@@ -477,6 +477,7 @@ static int lm355x_probe(struct i2c_client *client,
        chip->cdev_flash.name = "flash";
        chip->cdev_flash.max_brightness = 16;
        chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set;
+       chip->cdev_flash.default_trigger = "flash";
        err = led_classdev_register((struct device *)
                                    &client->dev, &chip->cdev_flash);
        if (err < 0)
@@ -486,6 +487,7 @@ static int lm355x_probe(struct i2c_client *client,
        chip->cdev_torch.name = "torch";
        chip->cdev_torch.max_brightness = 8;
        chip->cdev_torch.brightness_set = lm355x_torch_brightness_set;
+       chip->cdev_torch.default_trigger = "torch";
        err = led_classdev_register((struct device *)
                                    &client->dev, &chip->cdev_torch);
        if (err < 0)
index 9f428d9..f361bbe 100644 (file)
@@ -363,6 +363,7 @@ static int lm3642_probe(struct i2c_client *client,
        chip->cdev_flash.name = "flash";
        chip->cdev_flash.max_brightness = 16;
        chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set;
+       chip->cdev_flash.default_trigger = "flash";
        err = led_classdev_register((struct device *)
                                    &client->dev, &chip->cdev_flash);
        if (err < 0) {
@@ -380,6 +381,7 @@ static int lm3642_probe(struct i2c_client *client,
        chip->cdev_torch.name = "torch";
        chip->cdev_torch.max_brightness = 8;
        chip->cdev_torch.brightness_set = lm3642_torch_brightness_set;
+       chip->cdev_torch.default_trigger = "torch";
        err = led_classdev_register((struct device *)
                                    &client->dev, &chip->cdev_torch);
        if (err < 0) {
index 1001347..19752c9 100644 (file)
 #define LP5521_ENABLE_RUN_PROGRAM      \
        (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
 
+/* CONFIG register */
+#define LP5521_PWM_HF                  0x40    /* PWM: 0 = 256Hz, 1 = 558Hz */
+#define LP5521_PWRSAVE_EN              0x20    /* 1 = Power save mode */
+#define LP5521_CP_MODE_OFF             0       /* Charge pump (CP) off */
+#define LP5521_CP_MODE_BYPASS          8       /* CP forced to bypass mode */
+#define LP5521_CP_MODE_1X5             0x10    /* CP forced to 1.5x mode */
+#define LP5521_CP_MODE_AUTO            0x18    /* Automatic mode selection */
+#define LP5521_R_TO_BATT               0x04    /* R out: 0 = CP, 1 = Vbat */
+#define LP5521_CLK_INT                 0x01    /* Internal clock */
+#define LP5521_DEFAULT_CFG             \
+       (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO)
+
 /* Status */
 #define LP5521_EXT_CLK_USED            0x08
 
@@ -296,8 +308,11 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip)
        /* Set all PWMs to direct control mode */
        ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
 
-       val = chip->pdata->update_config ?
-               : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+       /* Update configuration for the clock setting */
+       val = LP5521_DEFAULT_CFG;
+       if (!lp55xx_is_extclk_used(chip))
+               val |= LP5521_CLK_INT;
+
        ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
        if (ret)
                return ret;
@@ -360,7 +375,8 @@ static ssize_t lp5521_selftest(struct device *dev,
        mutex_lock(&chip->lock);
        ret = lp5521_run_selftest(chip, buf);
        mutex_unlock(&chip->lock);
-       return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", ret ? "FAIL" : "OK");
 }
 
 /* device attributes */
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
new file mode 100644 (file)
index 0000000..513f239
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * LP5562 LED driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/leds-lp55xx.h>
+#include <linux/slab.h>
+
+#include "leds-lp55xx-common.h"
+
+#define LP5562_PROGRAM_LENGTH          32
+#define LP5562_MAX_LEDS                        4
+
+/* ENABLE Register 00h */
+#define LP5562_REG_ENABLE              0x00
+#define LP5562_EXEC_ENG1_M             0x30
+#define LP5562_EXEC_ENG2_M             0x0C
+#define LP5562_EXEC_ENG3_M             0x03
+#define LP5562_EXEC_M                  0x3F
+#define LP5562_MASTER_ENABLE           0x40    /* Chip master enable */
+#define LP5562_LOGARITHMIC_PWM         0x80    /* Logarithmic PWM adjustment */
+#define LP5562_EXEC_RUN                        0x2A
+#define LP5562_ENABLE_DEFAULT  \
+       (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
+#define LP5562_ENABLE_RUN_PROGRAM      \
+       (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
+
+/* OPMODE Register 01h */
+#define LP5562_REG_OP_MODE             0x01
+#define LP5562_MODE_ENG1_M             0x30
+#define LP5562_MODE_ENG2_M             0x0C
+#define LP5562_MODE_ENG3_M             0x03
+#define LP5562_LOAD_ENG1               0x10
+#define LP5562_LOAD_ENG2               0x04
+#define LP5562_LOAD_ENG3               0x01
+#define LP5562_RUN_ENG1                        0x20
+#define LP5562_RUN_ENG2                        0x08
+#define LP5562_RUN_ENG3                        0x02
+#define LP5562_ENG1_IS_LOADING(mode)   \
+       ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
+#define LP5562_ENG2_IS_LOADING(mode)   \
+       ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
+#define LP5562_ENG3_IS_LOADING(mode)   \
+       ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
+
+/* BRIGHTNESS Registers */
+#define LP5562_REG_R_PWM               0x04
+#define LP5562_REG_G_PWM               0x03
+#define LP5562_REG_B_PWM               0x02
+#define LP5562_REG_W_PWM               0x0E
+
+/* CURRENT Registers */
+#define LP5562_REG_R_CURRENT           0x07
+#define LP5562_REG_G_CURRENT           0x06
+#define LP5562_REG_B_CURRENT           0x05
+#define LP5562_REG_W_CURRENT           0x0F
+
+/* CONFIG Register 08h */
+#define LP5562_REG_CONFIG              0x08
+#define LP5562_PWM_HF                  0x40
+#define LP5562_PWRSAVE_EN              0x20
+#define LP5562_CLK_INT                 0x01    /* Internal clock */
+#define LP5562_DEFAULT_CFG             (LP5562_PWM_HF | LP5562_PWRSAVE_EN)
+
+/* RESET Register 0Dh */
+#define LP5562_REG_RESET               0x0D
+#define LP5562_RESET                   0xFF
+
+/* PROGRAM ENGINE Registers */
+#define LP5562_REG_PROG_MEM_ENG1       0x10
+#define LP5562_REG_PROG_MEM_ENG2       0x30
+#define LP5562_REG_PROG_MEM_ENG3       0x50
+
+/* LEDMAP Register 70h */
+#define LP5562_REG_ENG_SEL             0x70
+#define LP5562_ENG_SEL_PWM             0
+#define LP5562_ENG_FOR_RGB_M           0x3F
+#define LP5562_ENG_SEL_RGB             0x1B    /* R:ENG1, G:ENG2, B:ENG3 */
+#define LP5562_ENG_FOR_W_M             0xC0
+#define LP5562_ENG1_FOR_W              0x40    /* W:ENG1 */
+#define LP5562_ENG2_FOR_W              0x80    /* W:ENG2 */
+#define LP5562_ENG3_FOR_W              0xC0    /* W:ENG3 */
+
+/* Program Commands */
+#define LP5562_CMD_DISABLE             0x00
+#define LP5562_CMD_LOAD                        0x15
+#define LP5562_CMD_RUN                 0x2A
+#define LP5562_CMD_DIRECT              0x3F
+#define LP5562_PATTERN_OFF             0
+
+static inline void lp5562_wait_opmode_done(void)
+{
+       /* operation mode change needs to be longer than 153 us */
+       usleep_range(200, 300);
+}
+
+static inline void lp5562_wait_enable_done(void)
+{
+       /* it takes more 488 us to update ENABLE register */
+       usleep_range(500, 600);
+}
+
+static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
+{
+       u8 addr[] = {
+               LP5562_REG_R_CURRENT,
+               LP5562_REG_G_CURRENT,
+               LP5562_REG_B_CURRENT,
+               LP5562_REG_W_CURRENT,
+       };
+
+       led->led_current = led_current;
+       lp55xx_write(led->chip, addr[led->chan_nr], led_current);
+}
+
+static void lp5562_load_engine(struct lp55xx_chip *chip)
+{
+       enum lp55xx_engine_index idx = chip->engine_idx;
+       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[] = {
+               [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
+               [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
+               [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
+       };
+
+       lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
+
+       lp5562_wait_opmode_done();
+}
+
+static void lp5562_stop_engine(struct lp55xx_chip *chip)
+{
+       lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
+       lp5562_wait_opmode_done();
+}
+
+static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
+{
+       int ret;
+       u8 mode;
+       u8 exec;
+
+       /* stop engine */
+       if (!start) {
+               lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
+               lp5562_wait_enable_done();
+               lp5562_stop_engine(chip);
+               lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
+               lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
+               lp5562_wait_opmode_done();
+               return;
+       }
+
+       /*
+        * To run the engine,
+        * operation mode and enable register should updated at the same time
+        */
+
+       ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
+       if (ret)
+               return;
+
+       ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
+       if (ret)
+               return;
+
+       /* change operation mode to RUN only when each engine is loading */
+       if (LP5562_ENG1_IS_LOADING(mode)) {
+               mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
+               exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
+       }
+
+       if (LP5562_ENG2_IS_LOADING(mode)) {
+               mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
+               exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
+       }
+
+       if (LP5562_ENG3_IS_LOADING(mode)) {
+               mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
+               exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
+       }
+
+       lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
+       lp5562_wait_opmode_done();
+
+       lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
+       lp5562_wait_enable_done();
+}
+
+static int lp5562_update_firmware(struct lp55xx_chip *chip,
+                                       const u8 *data, size_t size)
+{
+       enum lp55xx_engine_index idx = chip->engine_idx;
+       u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
+       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,
+       };
+       unsigned cmd;
+       char c[3];
+       int program_size;
+       int nrchars;
+       int offset = 0;
+       int ret;
+       int i;
+
+       /* clear program memory before updating */
+       for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
+               lp55xx_write(chip, addr[idx] + i, 0);
+
+       i = 0;
+       while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
+               /* separate sscanfs because length is working only for %s */
+               ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
+               if (ret != 1)
+                       goto err;
+
+               ret = sscanf(c, "%2x", &cmd);
+               if (ret != 1)
+                       goto err;
+
+               pattern[i] = (u8)cmd;
+               offset += nrchars;
+               i++;
+       }
+
+       /* Each instruction is 16bit long. Check that length is even */
+       if (i % 2)
+               goto err;
+
+       program_size = i;
+       for (i = 0; i < program_size; i++)
+               lp55xx_write(chip, addr[idx] + i, pattern[i]);
+
+       return 0;
+
+err:
+       dev_err(&chip->cl->dev, "wrong pattern format\n");
+       return -EINVAL;
+}
+
+static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
+{
+       const struct firmware *fw = chip->fw;
+
+       if (fw->size > LP5562_PROGRAM_LENGTH) {
+               dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
+                       fw->size);
+               return;
+       }
+
+       /*
+        * Program momery sequence
+        *  1) set engine mode to "LOAD"
+        *  2) write firmware data into program memory
+        */
+
+       lp5562_load_engine(chip);
+       lp5562_update_firmware(chip, fw->data, fw->size);
+}
+
+static int lp5562_post_init_device(struct lp55xx_chip *chip)
+{
+       int ret;
+       u8 cfg = LP5562_DEFAULT_CFG;
+
+       /* Set all PWMs to direct control mode */
+       ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
+       if (ret)
+               return ret;
+
+       lp5562_wait_opmode_done();
+
+       /* Update configuration for the clock setting */
+       if (!lp55xx_is_extclk_used(chip))
+               cfg |= LP5562_CLK_INT;
+
+       ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg);
+       if (ret)
+               return ret;
+
+       /* Initialize all channels PWM to zero -> leds off */
+       lp55xx_write(chip, LP5562_REG_R_PWM, 0);
+       lp55xx_write(chip, LP5562_REG_G_PWM, 0);
+       lp55xx_write(chip, LP5562_REG_B_PWM, 0);
+       lp55xx_write(chip, LP5562_REG_W_PWM, 0);
+
+       /* Set LED map as register PWM by default */
+       lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
+
+       return 0;
+}
+
+static void lp5562_led_brightness_work(struct work_struct *work)
+{
+       struct lp55xx_led *led = container_of(work, struct lp55xx_led,
+                                             brightness_work);
+       struct lp55xx_chip *chip = led->chip;
+       u8 addr[] = {
+               LP5562_REG_R_PWM,
+               LP5562_REG_G_PWM,
+               LP5562_REG_B_PWM,
+               LP5562_REG_W_PWM,
+       };
+
+       mutex_lock(&chip->lock);
+       lp55xx_write(chip, addr[led->chan_nr], led->brightness);
+       mutex_unlock(&chip->lock);
+}
+
+static void lp5562_write_program_memory(struct lp55xx_chip *chip,
+                                       u8 base, const u8 *rgb, int size)
+{
+       int i;
+
+       if (!rgb || size <= 0)
+               return;
+
+       for (i = 0; i < size; i++)
+               lp55xx_write(chip, base + i, *(rgb + i));
+
+       lp55xx_write(chip, base + i, 0);
+       lp55xx_write(chip, base + i + 1, 0);
+}
+
+/* check the size of program count */
+static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
+{
+       return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
+               ptn->size_g >= LP5562_PROGRAM_LENGTH ||
+               ptn->size_b >= LP5562_PROGRAM_LENGTH);
+}
+
+static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
+{
+       struct lp55xx_predef_pattern *ptn;
+       int i;
+
+       if (mode == LP5562_PATTERN_OFF) {
+               lp5562_run_engine(chip, false);
+               return 0;
+       }
+
+       ptn = chip->pdata->patterns + (mode - 1);
+       if (!ptn || _is_pc_overflow(ptn)) {
+               dev_err(&chip->cl->dev, "invalid pattern data\n");
+               return -EINVAL;
+       }
+
+       lp5562_stop_engine(chip);
+
+       /* Set LED map as RGB */
+       lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
+
+       /* Load engines */
+       for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
+               chip->engine_idx = i;
+               lp5562_load_engine(chip);
+       }
+
+       /* Clear program registers */
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
+       lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
+
+       /* Program engines */
+       lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
+                               ptn->r, ptn->size_r);
+       lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
+                               ptn->g, ptn->size_g);
+       lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
+                               ptn->b, ptn->size_b);
+
+       /* Run engines */
+       lp5562_run_engine(chip, true);
+
+       return 0;
+}
+
+static ssize_t lp5562_store_pattern(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t len)
+{
+       struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+       struct lp55xx_chip *chip = led->chip;
+       struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
+       int num_patterns = chip->pdata->num_patterns;
+       unsigned long mode;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &mode);
+       if (ret)
+               return ret;
+
+       if (mode > num_patterns || !ptn)
+               return -EINVAL;
+
+       mutex_lock(&chip->lock);
+       ret = lp5562_run_predef_led_pattern(chip, mode);
+       mutex_unlock(&chip->lock);
+
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t lp5562_store_engine_mux(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t len)
+{
+       struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+       struct lp55xx_chip *chip = led->chip;
+       u8 mask;
+       u8 val;
+
+       /* LED map
+        * R ... Engine 1 (fixed)
+        * G ... Engine 2 (fixed)
+        * B ... Engine 3 (fixed)
+        * W ... Engine 1 or 2 or 3
+        */
+
+       if (sysfs_streq(buf, "RGB")) {
+               mask = LP5562_ENG_FOR_RGB_M;
+               val = LP5562_ENG_SEL_RGB;
+       } else if (sysfs_streq(buf, "W")) {
+               enum lp55xx_engine_index idx = chip->engine_idx;
+
+               mask = LP5562_ENG_FOR_W_M;
+               switch (idx) {
+               case LP55XX_ENGINE_1:
+                       val = LP5562_ENG1_FOR_W;
+                       break;
+               case LP55XX_ENGINE_2:
+                       val = LP5562_ENG2_FOR_W;
+                       break;
+               case LP55XX_ENGINE_3:
+                       val = LP5562_ENG3_FOR_W;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+       } else {
+               dev_err(dev, "choose RGB or W\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&chip->lock);
+       lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
+       mutex_unlock(&chip->lock);
+
+       return len;
+}
+
+static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
+static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
+
+static struct attribute *lp5562_attributes[] = {
+       &dev_attr_led_pattern.attr,
+       &dev_attr_engine_mux.attr,
+       NULL,
+};
+
+static const struct attribute_group lp5562_group = {
+       .attrs = lp5562_attributes,
+};
+
+/* Chip specific configurations */
+static struct lp55xx_device_config lp5562_cfg = {
+       .max_channel  = LP5562_MAX_LEDS,
+       .reset = {
+               .addr = LP5562_REG_RESET,
+               .val  = LP5562_RESET,
+       },
+       .enable = {
+               .addr = LP5562_REG_ENABLE,
+               .val  = LP5562_ENABLE_DEFAULT,
+       },
+       .post_init_device   = lp5562_post_init_device,
+       .set_led_current    = lp5562_set_led_current,
+       .brightness_work_fn = lp5562_led_brightness_work,
+       .run_engine         = lp5562_run_engine,
+       .firmware_cb        = lp5562_firmware_loaded,
+       .dev_attr_group     = &lp5562_group,
+};
+
+static int lp5562_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int ret;
+       struct lp55xx_chip *chip;
+       struct lp55xx_led *led;
+       struct lp55xx_platform_data *pdata = client->dev.platform_data;
+
+       if (!pdata) {
+               dev_err(&client->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       led = devm_kzalloc(&client->dev,
+                       sizeof(*led) * pdata->num_channels, GFP_KERNEL);
+       if (!led)
+               return -ENOMEM;
+
+       chip->cl = client;
+       chip->pdata = pdata;
+       chip->cfg = &lp5562_cfg;
+
+       mutex_init(&chip->lock);
+
+       i2c_set_clientdata(client, led);
+
+       ret = lp55xx_init_device(chip);
+       if (ret)
+               goto err_init;
+
+       ret = lp55xx_register_leds(led, chip);
+       if (ret)
+               goto err_register_leds;
+
+       ret = lp55xx_register_sysfs(chip);
+       if (ret) {
+               dev_err(&client->dev, "registering sysfs failed\n");
+               goto err_register_sysfs;
+       }
+
+       return 0;
+
+err_register_sysfs:
+       lp55xx_unregister_leds(led, chip);
+err_register_leds:
+       lp55xx_deinit_device(chip);
+err_init:
+       return ret;
+}
+
+static int lp5562_remove(struct i2c_client *client)
+{
+       struct lp55xx_led *led = i2c_get_clientdata(client);
+       struct lp55xx_chip *chip = led->chip;
+
+       lp5562_stop_engine(chip);
+
+       lp55xx_unregister_sysfs(chip);
+       lp55xx_unregister_leds(led, chip);
+       lp55xx_deinit_device(chip);
+
+       return 0;
+}
+
+static const struct i2c_device_id lp5562_id[] = {
+       { "lp5562", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lp5562_id);
+
+static struct i2c_driver lp5562_driver = {
+       .driver = {
+               .name   = "lp5562",
+       },
+       .probe          = lp5562_probe,
+       .remove         = lp5562_remove,
+       .id_table       = lp5562_id,
+};
+
+module_i2c_driver(lp5562_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
index d9eb841..ba34199 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * LP5521/LP5523/LP55231 Common Driver
+ * LP5521/LP5523/LP55231/LP5562 Common Driver
  *
  * Copyright 2012 Texas Instruments
  *
@@ -12,6 +12,7 @@
  * Derived from leds-lp5521.c, leds-lp5523.c
  */
 
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/i2c.h>
@@ -21,6 +22,9 @@
 
 #include "leds-lp55xx-common.h"
 
+/* External clock rate */
+#define LP55XX_CLK_32K                 32768
+
 static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
 {
        return container_of(cdev, struct lp55xx_led, cdev);
@@ -80,7 +84,7 @@ static ssize_t lp55xx_show_current(struct device *dev,
 {
        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
 
-       return sprintf(buf, "%d\n", led->led_current);
+       return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_current);
 }
 
 static ssize_t lp55xx_store_current(struct device *dev,
@@ -113,7 +117,7 @@ static ssize_t lp55xx_show_max_current(struct device *dev,
 {
        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
 
-       return sprintf(buf, "%d\n", led->max_current);
+       return scnprintf(buf, PAGE_SIZE, "%d\n", led->max_current);
 }
 
 static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
@@ -357,6 +361,35 @@ int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
 }
 EXPORT_SYMBOL_GPL(lp55xx_update_bits);
 
+bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
+{
+       struct clk *clk;
+       int err;
+
+       clk = devm_clk_get(&chip->cl->dev, "32k_clk");
+       if (IS_ERR(clk))
+               goto use_internal_clk;
+
+       err = clk_prepare_enable(clk);
+       if (err)
+               goto use_internal_clk;
+
+       if (clk_get_rate(clk) != LP55XX_CLK_32K) {
+               clk_disable_unprepare(clk);
+               goto use_internal_clk;
+       }
+
+       dev_info(&chip->cl->dev, "%dHz external clock used\n",  LP55XX_CLK_32K);
+
+       chip->clk = clk;
+       return true;
+
+use_internal_clk:
+       dev_info(&chip->cl->dev, "internal clock used\n");
+       return false;
+}
+EXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
+
 int lp55xx_init_device(struct lp55xx_chip *chip)
 {
        struct lp55xx_platform_data *pdata;
@@ -421,6 +454,9 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip)
 {
        struct lp55xx_platform_data *pdata = chip->pdata;
 
+       if (chip->clk)
+               clk_disable_unprepare(chip->clk);
+
        if (pdata->enable)
                pdata->enable(0);
 
index ece4761..fa6a078 100644 (file)
@@ -83,6 +83,7 @@ struct lp55xx_device_config {
  */
 struct lp55xx_chip {
        struct i2c_client *cl;
+       struct clk *clk;
        struct lp55xx_platform_data *pdata;
        struct mutex lock;      /* lock for user-space interface */
        int num_leds;
@@ -117,6 +118,9 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val);
 extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg,
                        u8 mask, u8 val);
 
+/* external clock detection */
+extern bool lp55xx_is_extclk_used(struct lp55xx_chip *chip);
+
 /* common device init/deinit functions */
 extern int lp55xx_init_device(struct lp55xx_chip *chip);
 extern void lp55xx_deinit_device(struct lp55xx_chip *chip);
index c9b9e1f..ca48a7d 100644 (file)
@@ -106,8 +106,9 @@ static int create_lt3593_led(const struct gpio_led *template,
        if (!template->retain_state_suspended)
                led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
-       ret = devm_gpio_request_one(parent, template->gpio,
-                                   GPIOF_DIR_OUT | state, template->name);
+       ret = devm_gpio_request_one(parent, template->gpio, state ?
+                                   GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
+                                   template->name);
        if (ret < 0)
                return ret;
 
index d978171..70137b1 100644 (file)
@@ -193,7 +193,8 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
        enum ns2_led_modes mode;
 
        ret = devm_gpio_request_one(&pdev->dev, template->cmd,
-                       GPIOF_DIR_OUT | gpio_get_value(template->cmd),
+                       gpio_get_value(template->cmd) ?
+                       GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                        template->name);
        if (ret) {
                dev_err(&pdev->dev, "%s: failed to setup command GPIO\n",
@@ -202,7 +203,8 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
        }
 
        ret = devm_gpio_request_one(&pdev->dev, template->slow,
-                       GPIOF_DIR_OUT | gpio_get_value(template->slow),
+                       gpio_get_value(template->slow) ?
+                       GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                        template->name);
        if (ret) {
                dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n",
@@ -306,10 +308,21 @@ static const struct of_device_id of_ns2_leds_match[] = {
 };
 #endif /* CONFIG_OF_GPIO */
 
+struct ns2_led_priv {
+       int num_leds;
+       struct ns2_led_data leds_data[];
+};
+
+static inline int sizeof_ns2_led_priv(int num_leds)
+{
+       return sizeof(struct ns2_led_priv) +
+                     (sizeof(struct ns2_led_data) * num_leds);
+}
+
 static int ns2_led_probe(struct platform_device *pdev)
 {
        struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
-       struct ns2_led_data *leds_data;
+       struct ns2_led_priv *priv;
        int i;
        int ret;
 
@@ -330,21 +343,23 @@ static int ns2_led_probe(struct platform_device *pdev)
                return -EINVAL;
 #endif /* CONFIG_OF_GPIO */
 
-       leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) *
-                                pdata->num_leds, GFP_KERNEL);
-       if (!leds_data)
+       priv = devm_kzalloc(&pdev->dev,
+                           sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL);
+       if (!priv)
                return -ENOMEM;
+       priv->num_leds = pdata->num_leds;
 
-       for (i = 0; i < pdata->num_leds; i++) {
-               ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]);
+       for (i = 0; i < priv->num_leds; i++) {
+               ret = create_ns2_led(pdev, &priv->leds_data[i],
+                                    &pdata->leds[i]);
                if (ret < 0) {
                        for (i = i - 1; i >= 0; i--)
-                               delete_ns2_led(&leds_data[i]);
+                               delete_ns2_led(&priv->leds_data[i]);
                        return ret;
                }
        }
 
-       platform_set_drvdata(pdev, leds_data);
+       platform_set_drvdata(pdev, priv);
 
        return 0;
 }
@@ -352,13 +367,12 @@ static int ns2_led_probe(struct platform_device *pdev)
 static int ns2_led_remove(struct platform_device *pdev)
 {
        int i;
-       struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
-       struct ns2_led_data *leds_data;
+       struct ns2_led_priv *priv;
 
-       leds_data = platform_get_drvdata(pdev);
+       priv = platform_get_drvdata(pdev);
 
-       for (i = 0; i < pdata->num_leds; i++)
-               delete_ns2_led(&leds_data[i]);
+       for (i = 0; i < priv->num_leds; i++)
+               delete_ns2_led(&priv->leds_data[i]);
 
        platform_set_drvdata(pdev, NULL);
 
index a1ea5f6..faf52c0 100644 (file)
 #include <linux/pwm.h>
 #include <linux/leds_pwm.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 struct led_pwm_data {
        struct led_classdev     cdev;
        struct pwm_device       *pwm;
+       struct work_struct      work;
        unsigned int            active_low;
        unsigned int            period;
+       int                     duty;
+       bool                    can_sleep;
 };
 
 struct led_pwm_priv {
@@ -36,6 +40,26 @@ struct led_pwm_priv {
        struct led_pwm_data leds[0];
 };
 
+static void __led_pwm_set(struct led_pwm_data *led_dat)
+{
+       int new_duty = led_dat->duty;
+
+       pwm_config(led_dat->pwm, new_duty, led_dat->period);
+
+       if (new_duty == 0)
+               pwm_disable(led_dat->pwm);
+       else
+               pwm_enable(led_dat->pwm);
+}
+
+static void led_pwm_work(struct work_struct *work)
+{
+       struct led_pwm_data *led_dat =
+               container_of(work, struct led_pwm_data, work);
+
+       __led_pwm_set(led_dat);
+}
+
 static void led_pwm_set(struct led_classdev *led_cdev,
        enum led_brightness brightness)
 {
@@ -44,13 +68,12 @@ static void led_pwm_set(struct led_classdev *led_cdev,
        unsigned int max = led_dat->cdev.max_brightness;
        unsigned int period =  led_dat->period;
 
-       if (brightness == 0) {
-               pwm_config(led_dat->pwm, 0, period);
-               pwm_disable(led_dat->pwm);
-       } else {
-               pwm_config(led_dat->pwm, brightness * period / max, period);
-               pwm_enable(led_dat->pwm);
-       }
+       led_dat->duty = brightness * period / max;
+
+       if (led_dat->can_sleep)
+               schedule_work(&led_dat->work);
+       else
+               __led_pwm_set(led_dat);
 }
 
 static inline size_t sizeof_pwm_leds_priv(int num_leds)
@@ -100,6 +123,10 @@ static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
                led_dat->cdev.brightness = LED_OFF;
                led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
+               led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+               if (led_dat->can_sleep)
+                       INIT_WORK(&led_dat->work, led_pwm_work);
+
                ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
                if (ret < 0) {
                        dev_err(&pdev->dev, "failed to register for %s\n",
@@ -153,6 +180,10 @@ static int led_pwm_probe(struct platform_device *pdev)
                        led_dat->cdev.max_brightness = cur_led->max_brightness;
                        led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
+                       led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+                       if (led_dat->can_sleep)
+                               INIT_WORK(&led_dat->work, led_pwm_work);
+
                        ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
                        if (ret < 0)
                                goto err;
@@ -180,8 +211,11 @@ static int led_pwm_remove(struct platform_device *pdev)
        struct led_pwm_priv *priv = platform_get_drvdata(pdev);
        int i;
 
-       for (i = 0; i < priv->num_leds; i++)
+       for (i = 0; i < priv->num_leds; i++) {
                led_classdev_unregister(&priv->leds[i].cdev);
+               if (priv->leds[i].can_sleep)
+                       cancel_work_sync(&priv->leds[i].work);
+       }
 
        return 0;
 }
index d3c2b7e..9483f1c 100644 (file)
@@ -205,7 +205,8 @@ static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
                gpio_free(cfg->pin_gpio_fn);
 
        if (new_state == R_TPU_PIN_GPIO)
-               gpio_request_one(cfg->pin_gpio, GPIOF_DIR_OUT | !!brightness,
+               gpio_request_one(cfg->pin_gpio, !!brightness ?
+                               GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                                cfg->name);
 
        if (new_state == R_TPU_PIN_GPIO_FN)
index 070ba07..98fe021 100644 (file)
@@ -85,6 +85,7 @@
 #include <linux/gpio.h>
 #include <linux/workqueue.h>
 #include <linux/leds-tca6507.h>
+#include <linux/of.h>
 
 /* LED select registers determine the source that drives LED outputs */
 #define TCA6507_LS_LED_OFF     0x0     /* Output HI-Z (off) */
@@ -724,7 +725,6 @@ tca6507_led_dt_init(struct i2c_client *client)
        return ERR_PTR(-ENODEV);
 }
 
-#define of_tca6507_leds_match NULL
 #endif
 
 static int tca6507_probe(struct i2c_client *client,
@@ -813,7 +813,7 @@ static struct i2c_driver tca6507_driver = {
        .driver   = {
                .name    = "leds-tca6507",
                .owner   = THIS_MODULE,
-               .of_match_table = of_tca6507_leds_match,
+               .of_match_table = of_match_ptr(of_tca6507_leds_match),
        },
        .probe    = tca6507_probe,
        .remove   = tca6507_remove,
index ed15157..8a181d5 100644 (file)
@@ -129,7 +129,10 @@ static void wm8350_led_disable(struct wm8350_led *led)
        ret = regulator_disable(led->isink);
        if (ret != 0) {
                dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
-               regulator_enable(led->dcdc);
+               ret = regulator_enable(led->dcdc);
+               if (ret != 0)
+                       dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
+                               ret);
                return;
        }
 
diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c
deleted file mode 100644 (file)
index 027a2b1..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Backlight emulation LED trigger
- *
- * Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it>
- * Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/fb.h>
-#include <linux/leds.h>
-#include "leds.h"
-
-#define BLANK          1
-#define UNBLANK                0
-
-struct bl_trig_notifier {
-       struct led_classdev *led;
-       int brightness;
-       int old_status;
-       struct notifier_block notifier;
-       unsigned invert;
-};
-
-static int fb_notifier_callback(struct notifier_block *p,
-                               unsigned long event, void *data)
-{
-       struct bl_trig_notifier *n = container_of(p,
-                                       struct bl_trig_notifier, notifier);
-       struct led_classdev *led = n->led;
-       struct fb_event *fb_event = data;
-       int *blank = fb_event->data;
-       int new_status = *blank ? BLANK : UNBLANK;
-
-       switch (event) {
-       case FB_EVENT_BLANK:
-               if (new_status == n->old_status)
-                       break;
-
-               if ((n->old_status == UNBLANK) ^ n->invert) {
-                       n->brightness = led->brightness;
-                       __led_set_brightness(led, LED_OFF);
-               } else {
-                       __led_set_brightness(led, n->brightness);
-               }
-
-               n->old_status = new_status;
-
-               break;
-       }
-
-       return 0;
-}
-
-static ssize_t bl_trig_invert_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct bl_trig_notifier *n = led->trigger_data;
-
-       return sprintf(buf, "%u\n", n->invert);
-}
-
-static ssize_t bl_trig_invert_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t num)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct bl_trig_notifier *n = led->trigger_data;
-       unsigned long invert;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &invert);
-       if (ret < 0)
-               return ret;
-
-       if (invert > 1)
-               return -EINVAL;
-
-       n->invert = invert;
-
-       /* After inverting, we need to update the LED. */
-       if ((n->old_status == BLANK) ^ n->invert)
-               __led_set_brightness(led, LED_OFF);
-       else
-               __led_set_brightness(led, n->brightness);
-
-       return num;
-}
-static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store);
-
-static void bl_trig_activate(struct led_classdev *led)
-{
-       int ret;
-
-       struct bl_trig_notifier *n;
-
-       n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL);
-       led->trigger_data = n;
-       if (!n) {
-               dev_err(led->dev, "unable to allocate backlight trigger\n");
-               return;
-       }
-
-       ret = device_create_file(led->dev, &dev_attr_inverted);
-       if (ret)
-               goto err_invert;
-
-       n->led = led;
-       n->brightness = led->brightness;
-       n->old_status = UNBLANK;
-       n->notifier.notifier_call = fb_notifier_callback;
-
-       ret = fb_register_client(&n->notifier);
-       if (ret)
-               dev_err(led->dev, "unable to register backlight trigger\n");
-       led->activated = true;
-
-       return;
-
-err_invert:
-       led->trigger_data = NULL;
-       kfree(n);
-}
-
-static void bl_trig_deactivate(struct led_classdev *led)
-{
-       struct bl_trig_notifier *n =
-               (struct bl_trig_notifier *) led->trigger_data;
-
-       if (led->activated) {
-               device_remove_file(led->dev, &dev_attr_inverted);
-               fb_unregister_client(&n->notifier);
-               kfree(n);
-               led->activated = false;
-       }
-}
-
-static struct led_trigger bl_led_trigger = {
-       .name           = "backlight",
-       .activate       = bl_trig_activate,
-       .deactivate     = bl_trig_deactivate
-};
-
-static int __init bl_trig_init(void)
-{
-       return led_trigger_register(&bl_led_trigger);
-}
-
-static void __exit bl_trig_exit(void)
-{
-       led_trigger_unregister(&bl_led_trigger);
-}
-
-module_init(bl_trig_init);
-module_exit(bl_trig_exit);
-
-MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
-MODULE_DESCRIPTION("Backlight emulation LED trigger");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c
deleted file mode 100644 (file)
index 4239b39..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * ledtrig-cpu.c - LED trigger based on CPU activity
- *
- * This LED trigger will be registered for each possible CPU and named as
- * cpu0, cpu1, cpu2, cpu3, etc.
- *
- * It can be bound to any LED just like other triggers using either a
- * board file or via sysfs interface.
- *
- * An API named ledtrig_cpu is exported for any user, who want to add CPU
- * activity indication in their code
- *
- * Copyright 2011 Linus Walleij <linus.walleij@linaro.org>
- * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/percpu.h>
-#include <linux/syscore_ops.h>
-#include <linux/rwsem.h>
-#include "leds.h"
-
-#define MAX_NAME_LEN   8
-
-struct led_trigger_cpu {
-       char name[MAX_NAME_LEN];
-       struct led_trigger *_trig;
-};
-
-static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
-
-/**
- * ledtrig_cpu - emit a CPU event as a trigger
- * @evt: CPU event to be emitted
- *
- * Emit a CPU event on a CPU core, which will trigger a
- * binded LED to turn on or turn off.
- */
-void ledtrig_cpu(enum cpu_led_event ledevt)
-{
-       struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
-
-       /* Locate the correct CPU LED */
-       switch (ledevt) {
-       case CPU_LED_IDLE_END:
-       case CPU_LED_START:
-               /* Will turn the LED on, max brightness */
-               led_trigger_event(trig->_trig, LED_FULL);
-               break;
-
-       case CPU_LED_IDLE_START:
-       case CPU_LED_STOP:
-       case CPU_LED_HALTED:
-               /* Will turn the LED off */
-               led_trigger_event(trig->_trig, LED_OFF);
-               break;
-
-       default:
-               /* Will leave the LED as it is */
-               break;
-       }
-}
-EXPORT_SYMBOL(ledtrig_cpu);
-
-static int ledtrig_cpu_syscore_suspend(void)
-{
-       ledtrig_cpu(CPU_LED_STOP);
-       return 0;
-}
-
-static void ledtrig_cpu_syscore_resume(void)
-{
-       ledtrig_cpu(CPU_LED_START);
-}
-
-static void ledtrig_cpu_syscore_shutdown(void)
-{
-       ledtrig_cpu(CPU_LED_HALTED);
-}
-
-static struct syscore_ops ledtrig_cpu_syscore_ops = {
-       .shutdown       = ledtrig_cpu_syscore_shutdown,
-       .suspend        = ledtrig_cpu_syscore_suspend,
-       .resume         = ledtrig_cpu_syscore_resume,
-};
-
-static int __init ledtrig_cpu_init(void)
-{
-       int cpu;
-
-       /* Supports up to 9999 cpu cores */
-       BUILD_BUG_ON(CONFIG_NR_CPUS > 9999);
-
-       /*
-        * Registering CPU led trigger for each CPU core here
-        * ignores CPU hotplug, but after this CPU hotplug works
-        * fine with this trigger.
-        */
-       for_each_possible_cpu(cpu) {
-               struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
-
-               snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu);
-
-               led_trigger_register_simple(trig->name, &trig->_trig);
-       }
-
-       register_syscore_ops(&ledtrig_cpu_syscore_ops);
-
-       pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
-
-       return 0;
-}
-module_init(ledtrig_cpu_init);
-
-static void __exit ledtrig_cpu_exit(void)
-{
-       int cpu;
-
-       for_each_possible_cpu(cpu) {
-               struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
-
-               led_trigger_unregister_simple(trig->_trig);
-               trig->_trig = NULL;
-               memset(trig->name, 0, MAX_NAME_LEN);
-       }
-
-       unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
-}
-module_exit(ledtrig_cpu_exit);
-
-MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
-MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
-MODULE_DESCRIPTION("CPU LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c
deleted file mode 100644 (file)
index eac1f1b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * LED Kernel Default ON Trigger
- *
- * Copyright 2008 Nick Forbes <nick.forbes@incepta.com>
- *
- * Based on Richard Purdie's ledtrig-timer.c.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/leds.h>
-#include "leds.h"
-
-static void defon_trig_activate(struct led_classdev *led_cdev)
-{
-       __led_set_brightness(led_cdev, led_cdev->max_brightness);
-}
-
-static struct led_trigger defon_led_trigger = {
-       .name     = "default-on",
-       .activate = defon_trig_activate,
-};
-
-static int __init defon_trig_init(void)
-{
-       return led_trigger_register(&defon_led_trigger);
-}
-
-static void __exit defon_trig_exit(void)
-{
-       led_trigger_unregister(&defon_led_trigger);
-}
-
-module_init(defon_trig_init);
-module_exit(defon_trig_exit);
-
-MODULE_AUTHOR("Nick Forbes <nick.forbes@incepta.com>");
-MODULE_DESCRIPTION("Default-ON LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
deleted file mode 100644 (file)
index 72e3ebf..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * ledtrig-gio.c - LED Trigger Based on GPIO events
- *
- * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include "leds.h"
-
-struct gpio_trig_data {
-       struct led_classdev *led;
-       struct work_struct work;
-
-       unsigned desired_brightness;    /* desired brightness when led is on */
-       unsigned inverted;              /* true when gpio is inverted */
-       unsigned gpio;                  /* gpio that triggers the leds */
-};
-
-static irqreturn_t gpio_trig_irq(int irq, void *_led)
-{
-       struct led_classdev *led = _led;
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-
-       /* just schedule_work since gpio_get_value can sleep */
-       schedule_work(&gpio_data->work);
-
-       return IRQ_HANDLED;
-};
-
-static void gpio_trig_work(struct work_struct *work)
-{
-       struct gpio_trig_data *gpio_data = container_of(work,
-                       struct gpio_trig_data, work);
-       int tmp;
-
-       if (!gpio_data->gpio)
-               return;
-
-       tmp = gpio_get_value(gpio_data->gpio);
-       if (gpio_data->inverted)
-               tmp = !tmp;
-
-       if (tmp) {
-               if (gpio_data->desired_brightness)
-                       __led_set_brightness(gpio_data->led,
-                                          gpio_data->desired_brightness);
-               else
-                       __led_set_brightness(gpio_data->led, LED_FULL);
-       } else {
-               __led_set_brightness(gpio_data->led, LED_OFF);
-       }
-}
-
-static ssize_t gpio_trig_brightness_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-
-       return sprintf(buf, "%u\n", gpio_data->desired_brightness);
-}
-
-static ssize_t gpio_trig_brightness_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t n)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-       unsigned desired_brightness;
-       int ret;
-
-       ret = sscanf(buf, "%u", &desired_brightness);
-       if (ret < 1 || desired_brightness > 255) {
-               dev_err(dev, "invalid value\n");
-               return -EINVAL;
-       }
-
-       gpio_data->desired_brightness = desired_brightness;
-
-       return n;
-}
-static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
-               gpio_trig_brightness_store);
-
-static ssize_t gpio_trig_inverted_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-
-       return sprintf(buf, "%u\n", gpio_data->inverted);
-}
-
-static ssize_t gpio_trig_inverted_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t n)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-       unsigned long inverted;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &inverted);
-       if (ret < 0)
-               return ret;
-
-       if (inverted > 1)
-               return -EINVAL;
-
-       gpio_data->inverted = inverted;
-
-       /* After inverting, we need to update the LED. */
-       schedule_work(&gpio_data->work);
-
-       return n;
-}
-static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
-               gpio_trig_inverted_store);
-
-static ssize_t gpio_trig_gpio_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-
-       return sprintf(buf, "%u\n", gpio_data->gpio);
-}
-
-static ssize_t gpio_trig_gpio_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t n)
-{
-       struct led_classdev *led = dev_get_drvdata(dev);
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-       unsigned gpio;
-       int ret;
-
-       ret = sscanf(buf, "%u", &gpio);
-       if (ret < 1) {
-               dev_err(dev, "couldn't read gpio number\n");
-               flush_work(&gpio_data->work);
-               return -EINVAL;
-       }
-
-       if (gpio_data->gpio == gpio)
-               return n;
-
-       if (!gpio) {
-               if (gpio_data->gpio != 0)
-                       free_irq(gpio_to_irq(gpio_data->gpio), led);
-               gpio_data->gpio = 0;
-               return n;
-       }
-
-       ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
-                       IRQF_SHARED | IRQF_TRIGGER_RISING
-                       | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
-       if (ret) {
-               dev_err(dev, "request_irq failed with error %d\n", ret);
-       } else {
-               if (gpio_data->gpio != 0)
-                       free_irq(gpio_to_irq(gpio_data->gpio), led);
-               gpio_data->gpio = gpio;
-       }
-
-       return ret ? ret : n;
-}
-static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
-
-static void gpio_trig_activate(struct led_classdev *led)
-{
-       struct gpio_trig_data *gpio_data;
-       int ret;
-
-       gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
-       if (!gpio_data)
-               return;
-
-       ret = device_create_file(led->dev, &dev_attr_gpio);
-       if (ret)
-               goto err_gpio;
-
-       ret = device_create_file(led->dev, &dev_attr_inverted);
-       if (ret)
-               goto err_inverted;
-
-       ret = device_create_file(led->dev, &dev_attr_desired_brightness);
-       if (ret)
-               goto err_brightness;
-
-       gpio_data->led = led;
-       led->trigger_data = gpio_data;
-       INIT_WORK(&gpio_data->work, gpio_trig_work);
-       led->activated = true;
-
-       return;
-
-err_brightness:
-       device_remove_file(led->dev, &dev_attr_inverted);
-
-err_inverted:
-       device_remove_file(led->dev, &dev_attr_gpio);
-
-err_gpio:
-       kfree(gpio_data);
-}
-
-static void gpio_trig_deactivate(struct led_classdev *led)
-{
-       struct gpio_trig_data *gpio_data = led->trigger_data;
-
-       if (led->activated) {
-               device_remove_file(led->dev, &dev_attr_gpio);
-               device_remove_file(led->dev, &dev_attr_inverted);
-               device_remove_file(led->dev, &dev_attr_desired_brightness);
-               flush_work(&gpio_data->work);
-               if (gpio_data->gpio != 0)
-                       free_irq(gpio_to_irq(gpio_data->gpio), led);
-               kfree(gpio_data);
-               led->activated = false;
-       }
-}
-
-static struct led_trigger gpio_led_trigger = {
-       .name           = "gpio",
-       .activate       = gpio_trig_activate,
-       .deactivate     = gpio_trig_deactivate,
-};
-
-static int __init gpio_trig_init(void)
-{
-       return led_trigger_register(&gpio_led_trigger);
-}
-module_init(gpio_trig_init);
-
-static void __exit gpio_trig_exit(void)
-{
-       led_trigger_unregister(&gpio_led_trigger);
-}
-module_exit(gpio_trig_exit);
-
-MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
-MODULE_DESCRIPTION("GPIO LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c
deleted file mode 100644 (file)
index 1edc746..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * LED Heartbeat Trigger
- *
- * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
- *
- * Based on Richard Purdie's ledtrig-timer.c and some arch's
- * CONFIG_HEARTBEAT code.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/leds.h>
-#include <linux/reboot.h>
-#include "leds.h"
-
-static int panic_heartbeats;
-
-struct heartbeat_trig_data {
-       unsigned int phase;
-       unsigned int period;
-       struct timer_list timer;
-};
-
-static void led_heartbeat_function(unsigned long data)
-{
-       struct led_classdev *led_cdev = (struct led_classdev *) data;
-       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
-       unsigned long brightness = LED_OFF;
-       unsigned long delay = 0;
-
-       if (unlikely(panic_heartbeats)) {
-               led_set_brightness(led_cdev, LED_OFF);
-               return;
-       }
-
-       /* acts like an actual heart beat -- ie thump-thump-pause... */
-       switch (heartbeat_data->phase) {
-       case 0:
-               /*
-                * The hyperbolic function below modifies the
-                * heartbeat period length in dependency of the
-                * current (1min) load. It goes through the points
-                * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
-                */
-               heartbeat_data->period = 300 +
-                       (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
-               heartbeat_data->period =
-                       msecs_to_jiffies(heartbeat_data->period);
-               delay = msecs_to_jiffies(70);
-               heartbeat_data->phase++;
-               brightness = led_cdev->max_brightness;
-               break;
-       case 1:
-               delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
-               heartbeat_data->phase++;
-               break;
-       case 2:
-               delay = msecs_to_jiffies(70);
-               heartbeat_data->phase++;
-               brightness = led_cdev->max_brightness;
-               break;
-       default:
-               delay = heartbeat_data->period - heartbeat_data->period / 4 -
-                       msecs_to_jiffies(70);
-               heartbeat_data->phase = 0;
-               break;
-       }
-
-       __led_set_brightness(led_cdev, brightness);
-       mod_timer(&heartbeat_data->timer, jiffies + delay);
-}
-
-static void heartbeat_trig_activate(struct led_classdev *led_cdev)
-{
-       struct heartbeat_trig_data *heartbeat_data;
-
-       heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
-       if (!heartbeat_data)
-               return;
-
-       led_cdev->trigger_data = heartbeat_data;
-       setup_timer(&heartbeat_data->timer,
-                   led_heartbeat_function, (unsigned long) led_cdev);
-       heartbeat_data->phase = 0;
-       led_heartbeat_function(heartbeat_data->timer.data);
-       led_cdev->activated = true;
-}
-
-static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
-{
-       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
-
-       if (led_cdev->activated) {
-               del_timer_sync(&heartbeat_data->timer);
-               kfree(heartbeat_data);
-               led_cdev->activated = false;
-       }
-}
-
-static struct led_trigger heartbeat_led_trigger = {
-       .name     = "heartbeat",
-       .activate = heartbeat_trig_activate,
-       .deactivate = heartbeat_trig_deactivate,
-};
-
-static int heartbeat_reboot_notifier(struct notifier_block *nb,
-                                    unsigned long code, void *unused)
-{
-       led_trigger_unregister(&heartbeat_led_trigger);
-       return NOTIFY_DONE;
-}
-
-static int heartbeat_panic_notifier(struct notifier_block *nb,
-                                    unsigned long code, void *unused)
-{
-       panic_heartbeats = 1;
-       return NOTIFY_DONE;
-}
-
-static struct notifier_block heartbeat_reboot_nb = {
-       .notifier_call = heartbeat_reboot_notifier,
-};
-
-static struct notifier_block heartbeat_panic_nb = {
-       .notifier_call = heartbeat_panic_notifier,
-};
-
-static int __init heartbeat_trig_init(void)
-{
-       int rc = led_trigger_register(&heartbeat_led_trigger);
-
-       if (!rc) {
-               atomic_notifier_chain_register(&panic_notifier_list,
-                                              &heartbeat_panic_nb);
-               register_reboot_notifier(&heartbeat_reboot_nb);
-       }
-       return rc;
-}
-
-static void __exit heartbeat_trig_exit(void)
-{
-       unregister_reboot_notifier(&heartbeat_reboot_nb);
-       atomic_notifier_chain_unregister(&panic_notifier_list,
-                                        &heartbeat_panic_nb);
-       led_trigger_unregister(&heartbeat_led_trigger);
-}
-
-module_init(heartbeat_trig_init);
-module_exit(heartbeat_trig_exit);
-
-MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
-MODULE_DESCRIPTION("Heartbeat LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
deleted file mode 100644 (file)
index 2cd7c0c..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * LED IDE-Disk Activity Trigger
- *
- * Copyright 2006 Openedhand Ltd.
- *
- * Author: Richard Purdie <rpurdie@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/leds.h>
-
-#define BLINK_DELAY 30
-
-DEFINE_LED_TRIGGER(ledtrig_ide);
-static unsigned long ide_blink_delay = BLINK_DELAY;
-
-void ledtrig_ide_activity(void)
-{
-       led_trigger_blink_oneshot(ledtrig_ide,
-                                 &ide_blink_delay, &ide_blink_delay, 0);
-}
-EXPORT_SYMBOL(ledtrig_ide_activity);
-
-static int __init ledtrig_ide_init(void)
-{
-       led_trigger_register_simple("ide-disk", &ledtrig_ide);
-       return 0;
-}
-
-static void __exit ledtrig_ide_exit(void)
-{
-       led_trigger_unregister_simple(ledtrig_ide);
-}
-
-module_init(ledtrig_ide_init);
-module_exit(ledtrig_ide_exit);
-
-MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
-MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c
deleted file mode 100644 (file)
index 2c029aa..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * One-shot LED Trigger
- *
- * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
- *
- * Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/ctype.h>
-#include <linux/slab.h>
-#include <linux/leds.h>
-#include "leds.h"
-
-#define DEFAULT_DELAY 100
-
-struct oneshot_trig_data {
-       unsigned int invert;
-};
-
-static ssize_t led_shot(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
-
-       led_blink_set_oneshot(led_cdev,
-                       &led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
-                       oneshot_data->invert);
-
-       /* content is ignored */
-       return size;
-}
-static ssize_t led_invert_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
-
-       return sprintf(buf, "%u\n", oneshot_data->invert);
-}
-
-static ssize_t led_invert_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
-       unsigned long state;
-       int ret;
-
-       ret = kstrtoul(buf, 0, &state);
-       if (ret)
-               return ret;
-
-       oneshot_data->invert = !!state;
-
-       if (oneshot_data->invert)
-               __led_set_brightness(led_cdev, LED_FULL);
-       else
-               __led_set_brightness(led_cdev, LED_OFF);
-
-       return size;
-}
-
-static ssize_t led_delay_on_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
-}
-
-static ssize_t led_delay_on_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       unsigned long state;
-       int ret;
-
-       ret = kstrtoul(buf, 0, &state);
-       if (ret)
-               return ret;
-
-       led_cdev->blink_delay_on = state;
-
-       return size;
-}
-static ssize_t led_delay_off_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
-}
-
-static ssize_t led_delay_off_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       unsigned long state;
-       int ret;
-
-       ret = kstrtoul(buf, 0, &state);
-       if (ret)
-               return ret;
-
-       led_cdev->blink_delay_off = state;
-
-       return size;
-}
-
-static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
-static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
-static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
-static DEVICE_ATTR(shot, 0200, NULL, led_shot);
-
-static void oneshot_trig_activate(struct led_classdev *led_cdev)
-{
-       struct oneshot_trig_data *oneshot_data;
-       int rc;
-
-       oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
-       if (!oneshot_data)
-               return;
-
-       led_cdev->trigger_data = oneshot_data;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
-       if (rc)
-               goto err_out_trig_data;
-       rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
-       if (rc)
-               goto err_out_delayon;
-       rc = device_create_file(led_cdev->dev, &dev_attr_invert);
-       if (rc)
-               goto err_out_delayoff;
-       rc = device_create_file(led_cdev->dev, &dev_attr_shot);
-       if (rc)
-               goto err_out_invert;
-
-       led_cdev->blink_delay_on = DEFAULT_DELAY;
-       led_cdev->blink_delay_off = DEFAULT_DELAY;
-
-       led_cdev->activated = true;
-
-       return;
-
-err_out_invert:
-       device_remove_file(led_cdev->dev, &dev_attr_invert);
-err_out_delayoff:
-       device_remove_file(led_cdev->dev, &dev_attr_delay_off);
-err_out_delayon:
-       device_remove_file(led_cdev->dev, &dev_attr_delay_on);
-err_out_trig_data:
-       kfree(led_cdev->trigger_data);
-}
-
-static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
-{
-       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
-
-       if (led_cdev->activated) {
-               device_remove_file(led_cdev->dev, &dev_attr_delay_on);
-               device_remove_file(led_cdev->dev, &dev_attr_delay_off);
-               device_remove_file(led_cdev->dev, &dev_attr_invert);
-               device_remove_file(led_cdev->dev, &dev_attr_shot);
-               kfree(oneshot_data);
-               led_cdev->activated = false;
-       }
-
-       /* Stop blinking */
-       led_set_brightness(led_cdev, LED_OFF);
-}
-
-static struct led_trigger oneshot_led_trigger = {
-       .name     = "oneshot",
-       .activate = oneshot_trig_activate,
-       .deactivate = oneshot_trig_deactivate,
-};
-
-static int __init oneshot_trig_init(void)
-{
-       return led_trigger_register(&oneshot_led_trigger);
-}
-
-static void __exit oneshot_trig_exit(void)
-{
-       led_trigger_unregister(&oneshot_led_trigger);
-}
-
-module_init(oneshot_trig_init);
-module_exit(oneshot_trig_exit);
-
-MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
-MODULE_DESCRIPTION("One-shot LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
deleted file mode 100644 (file)
index f774d05..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * LED Kernel Timer Trigger
- *
- * Copyright 2005-2006 Openedhand Ltd.
- *
- * Author: Richard Purdie <rpurdie@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/ctype.h>
-#include <linux/leds.h>
-#include "leds.h"
-
-static ssize_t led_delay_on_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
-}
-
-static ssize_t led_delay_on_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       unsigned long state;
-       ssize_t ret = -EINVAL;
-
-       ret = kstrtoul(buf, 10, &state);
-       if (ret)
-               return ret;
-
-       led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
-       led_cdev->blink_delay_on = state;
-
-       return size;
-}
-
-static ssize_t led_delay_off_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
-}
-
-static ssize_t led_delay_off_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       unsigned long state;
-       ssize_t ret = -EINVAL;
-
-       ret = kstrtoul(buf, 10, &state);
-       if (ret)
-               return ret;
-
-       led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
-       led_cdev->blink_delay_off = state;
-
-       return size;
-}
-
-static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
-static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
-
-static void timer_trig_activate(struct led_classdev *led_cdev)
-{
-       int rc;
-
-       led_cdev->trigger_data = NULL;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
-       if (rc)
-               return;
-       rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
-       if (rc)
-               goto err_out_delayon;
-
-       led_blink_set(led_cdev, &led_cdev->blink_delay_on,
-                     &led_cdev->blink_delay_off);
-       led_cdev->activated = true;
-
-       return;
-
-err_out_delayon:
-       device_remove_file(led_cdev->dev, &dev_attr_delay_on);
-}
-
-static void timer_trig_deactivate(struct led_classdev *led_cdev)
-{
-       if (led_cdev->activated) {
-               device_remove_file(led_cdev->dev, &dev_attr_delay_on);
-               device_remove_file(led_cdev->dev, &dev_attr_delay_off);
-               led_cdev->activated = false;
-       }
-
-       /* Stop blinking */
-       led_set_brightness(led_cdev, LED_OFF);
-}
-
-static struct led_trigger timer_led_trigger = {
-       .name     = "timer",
-       .activate = timer_trig_activate,
-       .deactivate = timer_trig_deactivate,
-};
-
-static int __init timer_trig_init(void)
-{
-       return led_trigger_register(&timer_led_trigger);
-}
-
-static void __exit timer_trig_exit(void)
-{
-       led_trigger_unregister(&timer_led_trigger);
-}
-
-module_init(timer_trig_init);
-module_exit(timer_trig_exit);
-
-MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
-MODULE_DESCRIPTION("Timer LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/ledtrig-transient.c
deleted file mode 100644 (file)
index 398f104..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * LED Kernel Transient Trigger
- *
- * Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
- *
- * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
- * ledtrig-heartbeat.c
- * Design and use-case input from Jonas Bonn <jonas@southpole.se> and
- * Neil Brown <neilb@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-/*
- * Transient trigger allows one shot timer activation. Please refer to
- * Documentation/leds/ledtrig-transient.txt for details
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-#include <linux/leds.h>
-#include "leds.h"
-
-struct transient_trig_data {
-       int activate;
-       int state;
-       int restore_state;
-       unsigned long duration;
-       struct timer_list timer;
-};
-
-static void transient_timer_function(unsigned long data)
-{
-       struct led_classdev *led_cdev = (struct led_classdev *) data;
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-
-       transient_data->activate = 0;
-       __led_set_brightness(led_cdev, transient_data->restore_state);
-}
-
-static ssize_t transient_activate_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-
-       return sprintf(buf, "%d\n", transient_data->activate);
-}
-
-static ssize_t transient_activate_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-       unsigned long state;
-       ssize_t ret;
-
-       ret = kstrtoul(buf, 10, &state);
-       if (ret)
-               return ret;
-
-       if (state != 1 && state != 0)
-               return -EINVAL;
-
-       /* cancel the running timer */
-       if (state == 0 && transient_data->activate == 1) {
-               del_timer(&transient_data->timer);
-               transient_data->activate = state;
-               __led_set_brightness(led_cdev, transient_data->restore_state);
-               return size;
-       }
-
-       /* start timer if there is no active timer */
-       if (state == 1 && transient_data->activate == 0 &&
-           transient_data->duration != 0) {
-               transient_data->activate = state;
-               __led_set_brightness(led_cdev, transient_data->state);
-               transient_data->restore_state =
-                   (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
-               mod_timer(&transient_data->timer,
-                         jiffies + transient_data->duration);
-       }
-
-       /* state == 0 && transient_data->activate == 0
-               timer is not active - just return */
-       /* state == 1 && transient_data->activate == 1
-               timer is already active - just return */
-
-       return size;
-}
-
-static ssize_t transient_duration_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-
-       return sprintf(buf, "%lu\n", transient_data->duration);
-}
-
-static ssize_t transient_duration_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-       unsigned long state;
-       ssize_t ret;
-
-       ret = kstrtoul(buf, 10, &state);
-       if (ret)
-               return ret;
-
-       transient_data->duration = state;
-       return size;
-}
-
-static ssize_t transient_state_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-       int state;
-
-       state = (transient_data->state == LED_FULL) ? 1 : 0;
-       return sprintf(buf, "%d\n", state);
-}
-
-static ssize_t transient_state_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-       unsigned long state;
-       ssize_t ret;
-
-       ret = kstrtoul(buf, 10, &state);
-       if (ret)
-               return ret;
-
-       if (state != 1 && state != 0)
-               return -EINVAL;
-
-       transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
-       return size;
-}
-
-static DEVICE_ATTR(activate, 0644, transient_activate_show,
-                  transient_activate_store);
-static DEVICE_ATTR(duration, 0644, transient_duration_show,
-                  transient_duration_store);
-static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
-
-static void transient_trig_activate(struct led_classdev *led_cdev)
-{
-       int rc;
-       struct transient_trig_data *tdata;
-
-       tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
-       if (!tdata) {
-               dev_err(led_cdev->dev,
-                       "unable to allocate transient trigger\n");
-               return;
-       }
-       led_cdev->trigger_data = tdata;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_activate);
-       if (rc)
-               goto err_out;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_duration);
-       if (rc)
-               goto err_out_duration;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_state);
-       if (rc)
-               goto err_out_state;
-
-       setup_timer(&tdata->timer, transient_timer_function,
-                   (unsigned long) led_cdev);
-       led_cdev->activated = true;
-
-       return;
-
-err_out_state:
-       device_remove_file(led_cdev->dev, &dev_attr_duration);
-err_out_duration:
-       device_remove_file(led_cdev->dev, &dev_attr_activate);
-err_out:
-       dev_err(led_cdev->dev, "unable to register transient trigger\n");
-       led_cdev->trigger_data = NULL;
-       kfree(tdata);
-}
-
-static void transient_trig_deactivate(struct led_classdev *led_cdev)
-{
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
-
-       if (led_cdev->activated) {
-               del_timer_sync(&transient_data->timer);
-               __led_set_brightness(led_cdev, transient_data->restore_state);
-               device_remove_file(led_cdev->dev, &dev_attr_activate);
-               device_remove_file(led_cdev->dev, &dev_attr_duration);
-               device_remove_file(led_cdev->dev, &dev_attr_state);
-               led_cdev->trigger_data = NULL;
-               led_cdev->activated = false;
-               kfree(transient_data);
-       }
-}
-
-static struct led_trigger transient_trigger = {
-       .name     = "transient",
-       .activate = transient_trig_activate,
-       .deactivate = transient_trig_deactivate,
-};
-
-static int __init transient_trig_init(void)
-{
-       return led_trigger_register(&transient_trigger);
-}
-
-static void __exit transient_trig_exit(void)
-{
-       led_trigger_unregister(&transient_trigger);
-}
-
-module_init(transient_trig_init);
-module_exit(transient_trig_exit);
-
-MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
-MODULE_DESCRIPTION("Transient LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
new file mode 100644 (file)
index 0000000..49794b4
--- /dev/null
@@ -0,0 +1,111 @@
+menuconfig LEDS_TRIGGERS
+       bool "LED Trigger support"
+       depends on LEDS_CLASS
+       help
+         This option enables trigger support for the leds class.
+         These triggers allow kernel events to drive the LEDs and can
+         be configured via sysfs. If unsure, say Y.
+
+if LEDS_TRIGGERS
+
+config LEDS_TRIGGER_TIMER
+       tristate "LED Timer Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled by a programmable timer
+         via sysfs. Some LED hardware can be programmed to start
+         blinking the LED without any further software interaction.
+         For more details read Documentation/leds/leds-class.txt.
+
+         If unsure, say Y.
+
+config LEDS_TRIGGER_ONESHOT
+       tristate "LED One-shot Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to blink in one-shot pulses with parameters
+         controlled via sysfs.  It's useful to notify the user on
+         sporadic events, when there are no clear begin and end trap points,
+         or on dense events, where this blinks the LED at constant rate if
+         rearmed continuously.
+
+         It also shows how to use the led_blink_set_oneshot() function.
+
+         If unsure, say Y.
+
+config LEDS_TRIGGER_IDE_DISK
+       bool "LED IDE Disk Trigger"
+       depends on IDE_GD_ATA
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled by IDE disk activity.
+         If unsure, say Y.
+
+config LEDS_TRIGGER_HEARTBEAT
+       tristate "LED Heartbeat Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled by a CPU load average.
+         The flash frequency is a hyperbolic function of the 1-minute
+         load average.
+         If unsure, say Y.
+
+config LEDS_TRIGGER_BACKLIGHT
+       tristate "LED backlight Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled as a backlight device: they
+         turn off and on when the display is blanked and unblanked.
+
+         If unsure, say N.
+
+config LEDS_TRIGGER_CPU
+       bool "LED CPU Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled by active CPUs. This shows
+         the active CPUs across an array of LEDs so you can see which
+         CPUs are active on the system at any given moment.
+
+         If unsure, say N.
+
+config LEDS_TRIGGER_GPIO
+       tristate "LED GPIO Trigger"
+       depends on LEDS_TRIGGERS
+       depends on GPIOLIB
+       help
+         This allows LEDs to be controlled by gpio events. It's good
+         when using gpios as switches and triggering the needed LEDs
+         from there. One use case is n810's keypad LEDs that could
+         be triggered by this trigger when user slides up to show
+         keypad.
+
+         If unsure, say N.
+
+config LEDS_TRIGGER_DEFAULT_ON
+       tristate "LED Default ON Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be initialised in the ON state.
+         If unsure, say Y.
+
+comment "iptables trigger is under Netfilter config (LED target)"
+       depends on LEDS_TRIGGERS
+
+config LEDS_TRIGGER_TRANSIENT
+       tristate "LED Transient Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows one time activation of a transient state on
+         GPIO/PWM based hardware.
+         If unsure, say Y.
+
+config LEDS_TRIGGER_CAMERA
+       tristate "LED Camera Flash/Torch Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled as a camera flash/torch device.
+         This enables direct flash/torch on/off by the driver, kernel space.
+         If unsure, say Y.
+
+endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
new file mode 100644 (file)
index 0000000..1abf48d
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_LEDS_TRIGGER_TIMER)       += ledtrig-timer.o
+obj-$(CONFIG_LEDS_TRIGGER_ONESHOT)     += ledtrig-oneshot.o
+obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)    += ledtrig-ide-disk.o
+obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)   += ledtrig-heartbeat.o
+obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
+obj-$(CONFIG_LEDS_TRIGGER_GPIO)                += ledtrig-gpio.o
+obj-$(CONFIG_LEDS_TRIGGER_CPU)         += ledtrig-cpu.o
+obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)  += ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)   += ledtrig-transient.o
+obj-$(CONFIG_LEDS_TRIGGER_CAMERA)      += ledtrig-camera.o
diff --git a/drivers/leds/trigger/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c
new file mode 100644 (file)
index 0000000..3c9c88a
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Backlight emulation LED trigger
+ *
+ * Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it>
+ * Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+#define BLANK          1
+#define UNBLANK                0
+
+struct bl_trig_notifier {
+       struct led_classdev *led;
+       int brightness;
+       int old_status;
+       struct notifier_block notifier;
+       unsigned invert;
+};
+
+static int fb_notifier_callback(struct notifier_block *p,
+                               unsigned long event, void *data)
+{
+       struct bl_trig_notifier *n = container_of(p,
+                                       struct bl_trig_notifier, notifier);
+       struct led_classdev *led = n->led;
+       struct fb_event *fb_event = data;
+       int *blank = fb_event->data;
+       int new_status = *blank ? BLANK : UNBLANK;
+
+       switch (event) {
+       case FB_EVENT_BLANK:
+               if (new_status == n->old_status)
+                       break;
+
+               if ((n->old_status == UNBLANK) ^ n->invert) {
+                       n->brightness = led->brightness;
+                       __led_set_brightness(led, LED_OFF);
+               } else {
+                       __led_set_brightness(led, n->brightness);
+               }
+
+               n->old_status = new_status;
+
+               break;
+       }
+
+       return 0;
+}
+
+static ssize_t bl_trig_invert_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct bl_trig_notifier *n = led->trigger_data;
+
+       return sprintf(buf, "%u\n", n->invert);
+}
+
+static ssize_t bl_trig_invert_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t num)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct bl_trig_notifier *n = led->trigger_data;
+       unsigned long invert;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &invert);
+       if (ret < 0)
+               return ret;
+
+       if (invert > 1)
+               return -EINVAL;
+
+       n->invert = invert;
+
+       /* After inverting, we need to update the LED. */
+       if ((n->old_status == BLANK) ^ n->invert)
+               __led_set_brightness(led, LED_OFF);
+       else
+               __led_set_brightness(led, n->brightness);
+
+       return num;
+}
+static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store);
+
+static void bl_trig_activate(struct led_classdev *led)
+{
+       int ret;
+
+       struct bl_trig_notifier *n;
+
+       n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL);
+       led->trigger_data = n;
+       if (!n) {
+               dev_err(led->dev, "unable to allocate backlight trigger\n");
+               return;
+       }
+
+       ret = device_create_file(led->dev, &dev_attr_inverted);
+       if (ret)
+               goto err_invert;
+
+       n->led = led;
+       n->brightness = led->brightness;
+       n->old_status = UNBLANK;
+       n->notifier.notifier_call = fb_notifier_callback;
+
+       ret = fb_register_client(&n->notifier);
+       if (ret)
+               dev_err(led->dev, "unable to register backlight trigger\n");
+       led->activated = true;
+
+       return;
+
+err_invert:
+       led->trigger_data = NULL;
+       kfree(n);
+}
+
+static void bl_trig_deactivate(struct led_classdev *led)
+{
+       struct bl_trig_notifier *n =
+               (struct bl_trig_notifier *) led->trigger_data;
+
+       if (led->activated) {
+               device_remove_file(led->dev, &dev_attr_inverted);
+               fb_unregister_client(&n->notifier);
+               kfree(n);
+               led->activated = false;
+       }
+}
+
+static struct led_trigger bl_led_trigger = {
+       .name           = "backlight",
+       .activate       = bl_trig_activate,
+       .deactivate     = bl_trig_deactivate
+};
+
+static int __init bl_trig_init(void)
+{
+       return led_trigger_register(&bl_led_trigger);
+}
+
+static void __exit bl_trig_exit(void)
+{
+       led_trigger_unregister(&bl_led_trigger);
+}
+
+module_init(bl_trig_init);
+module_exit(bl_trig_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("Backlight emulation LED trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/trigger/ledtrig-camera.c b/drivers/leds/trigger/ledtrig-camera.c
new file mode 100644 (file)
index 0000000..9bd73a8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Camera Flash and Torch On/Off Trigger
+ *
+ * based on ledtrig-ide-disk.c
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+
+DEFINE_LED_TRIGGER(ledtrig_flash);
+DEFINE_LED_TRIGGER(ledtrig_torch);
+
+void ledtrig_flash_ctrl(bool on)
+{
+       enum led_brightness brt = on ? LED_FULL : LED_OFF;
+
+       led_trigger_event(ledtrig_flash, brt);
+}
+EXPORT_SYMBOL_GPL(ledtrig_flash_ctrl);
+
+void ledtrig_torch_ctrl(bool on)
+{
+       enum led_brightness brt = on ? LED_FULL : LED_OFF;
+
+       led_trigger_event(ledtrig_torch, brt);
+}
+EXPORT_SYMBOL_GPL(ledtrig_torch_ctrl);
+
+static int __init ledtrig_camera_init(void)
+{
+       led_trigger_register_simple("flash", &ledtrig_flash);
+       led_trigger_register_simple("torch", &ledtrig_torch);
+       return 0;
+}
+module_init(ledtrig_camera_init);
+
+static void __exit ledtrig_camera_exit(void)
+{
+       led_trigger_unregister_simple(ledtrig_torch);
+       led_trigger_unregister_simple(ledtrig_flash);
+}
+module_exit(ledtrig_camera_exit);
+
+MODULE_DESCRIPTION("LED Trigger for Camera Flash/Torch Control");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
new file mode 100644 (file)
index 0000000..118335e
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * ledtrig-cpu.c - LED trigger based on CPU activity
+ *
+ * This LED trigger will be registered for each possible CPU and named as
+ * cpu0, cpu1, cpu2, cpu3, etc.
+ *
+ * It can be bound to any LED just like other triggers using either a
+ * board file or via sysfs interface.
+ *
+ * An API named ledtrig_cpu is exported for any user, who want to add CPU
+ * activity indication in their code
+ *
+ * Copyright 2011 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/percpu.h>
+#include <linux/syscore_ops.h>
+#include <linux/rwsem.h>
+#include "../leds.h"
+
+#define MAX_NAME_LEN   8
+
+struct led_trigger_cpu {
+       char name[MAX_NAME_LEN];
+       struct led_trigger *_trig;
+};
+
+static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
+
+/**
+ * ledtrig_cpu - emit a CPU event as a trigger
+ * @evt: CPU event to be emitted
+ *
+ * Emit a CPU event on a CPU core, which will trigger a
+ * binded LED to turn on or turn off.
+ */
+void ledtrig_cpu(enum cpu_led_event ledevt)
+{
+       struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
+
+       /* Locate the correct CPU LED */
+       switch (ledevt) {
+       case CPU_LED_IDLE_END:
+       case CPU_LED_START:
+               /* Will turn the LED on, max brightness */
+               led_trigger_event(trig->_trig, LED_FULL);
+               break;
+
+       case CPU_LED_IDLE_START:
+       case CPU_LED_STOP:
+       case CPU_LED_HALTED:
+               /* Will turn the LED off */
+               led_trigger_event(trig->_trig, LED_OFF);
+               break;
+
+       default:
+               /* Will leave the LED as it is */
+               break;
+       }
+}
+EXPORT_SYMBOL(ledtrig_cpu);
+
+static int ledtrig_cpu_syscore_suspend(void)
+{
+       ledtrig_cpu(CPU_LED_STOP);
+       return 0;
+}
+
+static void ledtrig_cpu_syscore_resume(void)
+{
+       ledtrig_cpu(CPU_LED_START);
+}
+
+static void ledtrig_cpu_syscore_shutdown(void)
+{
+       ledtrig_cpu(CPU_LED_HALTED);
+}
+
+static struct syscore_ops ledtrig_cpu_syscore_ops = {
+       .shutdown       = ledtrig_cpu_syscore_shutdown,
+       .suspend        = ledtrig_cpu_syscore_suspend,
+       .resume         = ledtrig_cpu_syscore_resume,
+};
+
+static int __init ledtrig_cpu_init(void)
+{
+       int cpu;
+
+       /* Supports up to 9999 cpu cores */
+       BUILD_BUG_ON(CONFIG_NR_CPUS > 9999);
+
+       /*
+        * Registering CPU led trigger for each CPU core here
+        * ignores CPU hotplug, but after this CPU hotplug works
+        * fine with this trigger.
+        */
+       for_each_possible_cpu(cpu) {
+               struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+               snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu);
+
+               led_trigger_register_simple(trig->name, &trig->_trig);
+       }
+
+       register_syscore_ops(&ledtrig_cpu_syscore_ops);
+
+       pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
+
+       return 0;
+}
+module_init(ledtrig_cpu_init);
+
+static void __exit ledtrig_cpu_exit(void)
+{
+       int cpu;
+
+       for_each_possible_cpu(cpu) {
+               struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+               led_trigger_unregister_simple(trig->_trig);
+               trig->_trig = NULL;
+               memset(trig->name, 0, MAX_NAME_LEN);
+       }
+
+       unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
+}
+module_exit(ledtrig_cpu_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
+MODULE_DESCRIPTION("CPU LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-default-on.c b/drivers/leds/trigger/ledtrig-default-on.c
new file mode 100644 (file)
index 0000000..81a91be
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * LED Kernel Default ON Trigger
+ *
+ * Copyright 2008 Nick Forbes <nick.forbes@incepta.com>
+ *
+ * Based on Richard Purdie's ledtrig-timer.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+static void defon_trig_activate(struct led_classdev *led_cdev)
+{
+       __led_set_brightness(led_cdev, led_cdev->max_brightness);
+}
+
+static struct led_trigger defon_led_trigger = {
+       .name     = "default-on",
+       .activate = defon_trig_activate,
+};
+
+static int __init defon_trig_init(void)
+{
+       return led_trigger_register(&defon_led_trigger);
+}
+
+static void __exit defon_trig_exit(void)
+{
+       led_trigger_unregister(&defon_led_trigger);
+}
+
+module_init(defon_trig_init);
+module_exit(defon_trig_exit);
+
+MODULE_AUTHOR("Nick Forbes <nick.forbes@incepta.com>");
+MODULE_DESCRIPTION("Default-ON LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c
new file mode 100644 (file)
index 0000000..35812e3
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * ledtrig-gio.c - LED Trigger Based on GPIO events
+ *
+ * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include "../leds.h"
+
+struct gpio_trig_data {
+       struct led_classdev *led;
+       struct work_struct work;
+
+       unsigned desired_brightness;    /* desired brightness when led is on */
+       unsigned inverted;              /* true when gpio is inverted */
+       unsigned gpio;                  /* gpio that triggers the leds */
+};
+
+static irqreturn_t gpio_trig_irq(int irq, void *_led)
+{
+       struct led_classdev *led = _led;
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       /* just schedule_work since gpio_get_value can sleep */
+       schedule_work(&gpio_data->work);
+
+       return IRQ_HANDLED;
+};
+
+static void gpio_trig_work(struct work_struct *work)
+{
+       struct gpio_trig_data *gpio_data = container_of(work,
+                       struct gpio_trig_data, work);
+       int tmp;
+
+       if (!gpio_data->gpio)
+               return;
+
+       tmp = gpio_get_value(gpio_data->gpio);
+       if (gpio_data->inverted)
+               tmp = !tmp;
+
+       if (tmp) {
+               if (gpio_data->desired_brightness)
+                       __led_set_brightness(gpio_data->led,
+                                          gpio_data->desired_brightness);
+               else
+                       __led_set_brightness(gpio_data->led, LED_FULL);
+       } else {
+               __led_set_brightness(gpio_data->led, LED_OFF);
+       }
+}
+
+static ssize_t gpio_trig_brightness_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%u\n", gpio_data->desired_brightness);
+}
+
+static ssize_t gpio_trig_brightness_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned desired_brightness;
+       int ret;
+
+       ret = sscanf(buf, "%u", &desired_brightness);
+       if (ret < 1 || desired_brightness > 255) {
+               dev_err(dev, "invalid value\n");
+               return -EINVAL;
+       }
+
+       gpio_data->desired_brightness = desired_brightness;
+
+       return n;
+}
+static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
+               gpio_trig_brightness_store);
+
+static ssize_t gpio_trig_inverted_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%u\n", gpio_data->inverted);
+}
+
+static ssize_t gpio_trig_inverted_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned long inverted;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &inverted);
+       if (ret < 0)
+               return ret;
+
+       if (inverted > 1)
+               return -EINVAL;
+
+       gpio_data->inverted = inverted;
+
+       /* After inverting, we need to update the LED. */
+       schedule_work(&gpio_data->work);
+
+       return n;
+}
+static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
+               gpio_trig_inverted_store);
+
+static ssize_t gpio_trig_gpio_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%u\n", gpio_data->gpio);
+}
+
+static ssize_t gpio_trig_gpio_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned gpio;
+       int ret;
+
+       ret = sscanf(buf, "%u", &gpio);
+       if (ret < 1) {
+               dev_err(dev, "couldn't read gpio number\n");
+               flush_work(&gpio_data->work);
+               return -EINVAL;
+       }
+
+       if (gpio_data->gpio == gpio)
+               return n;
+
+       if (!gpio) {
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
+               gpio_data->gpio = 0;
+               return n;
+       }
+
+       ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
+                       IRQF_SHARED | IRQF_TRIGGER_RISING
+                       | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
+       if (ret) {
+               dev_err(dev, "request_irq failed with error %d\n", ret);
+       } else {
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
+               gpio_data->gpio = gpio;
+       }
+
+       return ret ? ret : n;
+}
+static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
+
+static void gpio_trig_activate(struct led_classdev *led)
+{
+       struct gpio_trig_data *gpio_data;
+       int ret;
+
+       gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
+       if (!gpio_data)
+               return;
+
+       ret = device_create_file(led->dev, &dev_attr_gpio);
+       if (ret)
+               goto err_gpio;
+
+       ret = device_create_file(led->dev, &dev_attr_inverted);
+       if (ret)
+               goto err_inverted;
+
+       ret = device_create_file(led->dev, &dev_attr_desired_brightness);
+       if (ret)
+               goto err_brightness;
+
+       gpio_data->led = led;
+       led->trigger_data = gpio_data;
+       INIT_WORK(&gpio_data->work, gpio_trig_work);
+       led->activated = true;
+
+       return;
+
+err_brightness:
+       device_remove_file(led->dev, &dev_attr_inverted);
+
+err_inverted:
+       device_remove_file(led->dev, &dev_attr_gpio);
+
+err_gpio:
+       kfree(gpio_data);
+}
+
+static void gpio_trig_deactivate(struct led_classdev *led)
+{
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       if (led->activated) {
+               device_remove_file(led->dev, &dev_attr_gpio);
+               device_remove_file(led->dev, &dev_attr_inverted);
+               device_remove_file(led->dev, &dev_attr_desired_brightness);
+               flush_work(&gpio_data->work);
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
+               kfree(gpio_data);
+               led->activated = false;
+       }
+}
+
+static struct led_trigger gpio_led_trigger = {
+       .name           = "gpio",
+       .activate       = gpio_trig_activate,
+       .deactivate     = gpio_trig_deactivate,
+};
+
+static int __init gpio_trig_init(void)
+{
+       return led_trigger_register(&gpio_led_trigger);
+}
+module_init(gpio_trig_init);
+
+static void __exit gpio_trig_exit(void)
+{
+       led_trigger_unregister(&gpio_led_trigger);
+}
+module_exit(gpio_trig_exit);
+
+MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
+MODULE_DESCRIPTION("GPIO LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
new file mode 100644 (file)
index 0000000..5c8464a
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * LED Heartbeat Trigger
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * Based on Richard Purdie's ledtrig-timer.c and some arch's
+ * CONFIG_HEARTBEAT code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/leds.h>
+#include <linux/reboot.h>
+#include "../leds.h"
+
+static int panic_heartbeats;
+
+struct heartbeat_trig_data {
+       unsigned int phase;
+       unsigned int period;
+       struct timer_list timer;
+};
+
+static void led_heartbeat_function(unsigned long data)
+{
+       struct led_classdev *led_cdev = (struct led_classdev *) data;
+       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+       unsigned long brightness = LED_OFF;
+       unsigned long delay = 0;
+
+       if (unlikely(panic_heartbeats)) {
+               led_set_brightness(led_cdev, LED_OFF);
+               return;
+       }
+
+       /* acts like an actual heart beat -- ie thump-thump-pause... */
+       switch (heartbeat_data->phase) {
+       case 0:
+               /*
+                * The hyperbolic function below modifies the
+                * heartbeat period length in dependency of the
+                * current (1min) load. It goes through the points
+                * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
+                */
+               heartbeat_data->period = 300 +
+                       (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
+               heartbeat_data->period =
+                       msecs_to_jiffies(heartbeat_data->period);
+               delay = msecs_to_jiffies(70);
+               heartbeat_data->phase++;
+               brightness = led_cdev->max_brightness;
+               break;
+       case 1:
+               delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
+               heartbeat_data->phase++;
+               break;
+       case 2:
+               delay = msecs_to_jiffies(70);
+               heartbeat_data->phase++;
+               brightness = led_cdev->max_brightness;
+               break;
+       default:
+               delay = heartbeat_data->period - heartbeat_data->period / 4 -
+                       msecs_to_jiffies(70);
+               heartbeat_data->phase = 0;
+               break;
+       }
+
+       __led_set_brightness(led_cdev, brightness);
+       mod_timer(&heartbeat_data->timer, jiffies + delay);
+}
+
+static void heartbeat_trig_activate(struct led_classdev *led_cdev)
+{
+       struct heartbeat_trig_data *heartbeat_data;
+
+       heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
+       if (!heartbeat_data)
+               return;
+
+       led_cdev->trigger_data = heartbeat_data;
+       setup_timer(&heartbeat_data->timer,
+                   led_heartbeat_function, (unsigned long) led_cdev);
+       heartbeat_data->phase = 0;
+       led_heartbeat_function(heartbeat_data->timer.data);
+       led_cdev->activated = true;
+}
+
+static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               del_timer_sync(&heartbeat_data->timer);
+               kfree(heartbeat_data);
+               led_cdev->activated = false;
+       }
+}
+
+static struct led_trigger heartbeat_led_trigger = {
+       .name     = "heartbeat",
+       .activate = heartbeat_trig_activate,
+       .deactivate = heartbeat_trig_deactivate,
+};
+
+static int heartbeat_reboot_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+       led_trigger_unregister(&heartbeat_led_trigger);
+       return NOTIFY_DONE;
+}
+
+static int heartbeat_panic_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+       panic_heartbeats = 1;
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block heartbeat_reboot_nb = {
+       .notifier_call = heartbeat_reboot_notifier,
+};
+
+static struct notifier_block heartbeat_panic_nb = {
+       .notifier_call = heartbeat_panic_notifier,
+};
+
+static int __init heartbeat_trig_init(void)
+{
+       int rc = led_trigger_register(&heartbeat_led_trigger);
+
+       if (!rc) {
+               atomic_notifier_chain_register(&panic_notifier_list,
+                                              &heartbeat_panic_nb);
+               register_reboot_notifier(&heartbeat_reboot_nb);
+       }
+       return rc;
+}
+
+static void __exit heartbeat_trig_exit(void)
+{
+       unregister_reboot_notifier(&heartbeat_reboot_nb);
+       atomic_notifier_chain_unregister(&panic_notifier_list,
+                                        &heartbeat_panic_nb);
+       led_trigger_unregister(&heartbeat_led_trigger);
+}
+
+module_init(heartbeat_trig_init);
+module_exit(heartbeat_trig_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Heartbeat LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-ide-disk.c b/drivers/leds/trigger/ledtrig-ide-disk.c
new file mode 100644 (file)
index 0000000..2cd7c0c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * LED IDE-Disk Activity Trigger
+ *
+ * Copyright 2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+
+#define BLINK_DELAY 30
+
+DEFINE_LED_TRIGGER(ledtrig_ide);
+static unsigned long ide_blink_delay = BLINK_DELAY;
+
+void ledtrig_ide_activity(void)
+{
+       led_trigger_blink_oneshot(ledtrig_ide,
+                                 &ide_blink_delay, &ide_blink_delay, 0);
+}
+EXPORT_SYMBOL(ledtrig_ide_activity);
+
+static int __init ledtrig_ide_init(void)
+{
+       led_trigger_register_simple("ide-disk", &ledtrig_ide);
+       return 0;
+}
+
+static void __exit ledtrig_ide_exit(void)
+{
+       led_trigger_unregister_simple(ledtrig_ide);
+}
+
+module_init(ledtrig_ide_init);
+module_exit(ledtrig_ide_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-oneshot.c b/drivers/leds/trigger/ledtrig-oneshot.c
new file mode 100644 (file)
index 0000000..cb4c746
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * One-shot LED Trigger
+ *
+ * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
+ *
+ * Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+#define DEFAULT_DELAY 100
+
+struct oneshot_trig_data {
+       unsigned int invert;
+};
+
+static ssize_t led_shot(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       led_blink_set_oneshot(led_cdev,
+                       &led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
+                       oneshot_data->invert);
+
+       /* content is ignored */
+       return size;
+}
+static ssize_t led_invert_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%u\n", oneshot_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       oneshot_data->invert = !!state;
+
+       if (oneshot_data->invert)
+               __led_set_brightness(led_cdev, LED_FULL);
+       else
+               __led_set_brightness(led_cdev, LED_OFF);
+
+       return size;
+}
+
+static ssize_t led_delay_on_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
+}
+
+static ssize_t led_delay_on_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       led_cdev->blink_delay_on = state;
+
+       return size;
+}
+static ssize_t led_delay_off_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
+}
+
+static ssize_t led_delay_off_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       led_cdev->blink_delay_off = state;
+
+       return size;
+}
+
+static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
+static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+static DEVICE_ATTR(shot, 0200, NULL, led_shot);
+
+static void oneshot_trig_activate(struct led_classdev *led_cdev)
+{
+       struct oneshot_trig_data *oneshot_data;
+       int rc;
+
+       oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
+       if (!oneshot_data)
+               return;
+
+       led_cdev->trigger_data = oneshot_data;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
+       if (rc)
+               goto err_out_trig_data;
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
+       if (rc)
+               goto err_out_delayon;
+       rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+       if (rc)
+               goto err_out_delayoff;
+       rc = device_create_file(led_cdev->dev, &dev_attr_shot);
+       if (rc)
+               goto err_out_invert;
+
+       led_cdev->blink_delay_on = DEFAULT_DELAY;
+       led_cdev->blink_delay_off = DEFAULT_DELAY;
+
+       led_cdev->activated = true;
+
+       return;
+
+err_out_invert:
+       device_remove_file(led_cdev->dev, &dev_attr_invert);
+err_out_delayoff:
+       device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+err_out_delayon:
+       device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+err_out_trig_data:
+       kfree(led_cdev->trigger_data);
+}
+
+static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+               device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+               device_remove_file(led_cdev->dev, &dev_attr_invert);
+               device_remove_file(led_cdev->dev, &dev_attr_shot);
+               kfree(oneshot_data);
+               led_cdev->activated = false;
+       }
+
+       /* Stop blinking */
+       led_set_brightness(led_cdev, LED_OFF);
+}
+
+static struct led_trigger oneshot_led_trigger = {
+       .name     = "oneshot",
+       .activate = oneshot_trig_activate,
+       .deactivate = oneshot_trig_deactivate,
+};
+
+static int __init oneshot_trig_init(void)
+{
+       return led_trigger_register(&oneshot_led_trigger);
+}
+
+static void __exit oneshot_trig_exit(void)
+{
+       led_trigger_unregister(&oneshot_led_trigger);
+}
+
+module_init(oneshot_trig_init);
+module_exit(oneshot_trig_exit);
+
+MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
+MODULE_DESCRIPTION("One-shot LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-timer.c b/drivers/leds/trigger/ledtrig-timer.c
new file mode 100644 (file)
index 0000000..8d09327
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * LED Kernel Timer Trigger
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+
+static ssize_t led_delay_on_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
+}
+
+static ssize_t led_delay_on_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       ssize_t ret = -EINVAL;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
+       led_cdev->blink_delay_on = state;
+
+       return size;
+}
+
+static ssize_t led_delay_off_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
+}
+
+static ssize_t led_delay_off_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       ssize_t ret = -EINVAL;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
+       led_cdev->blink_delay_off = state;
+
+       return size;
+}
+
+static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
+static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
+
+static void timer_trig_activate(struct led_classdev *led_cdev)
+{
+       int rc;
+
+       led_cdev->trigger_data = NULL;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
+       if (rc)
+               return;
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
+       if (rc)
+               goto err_out_delayon;
+
+       led_blink_set(led_cdev, &led_cdev->blink_delay_on,
+                     &led_cdev->blink_delay_off);
+       led_cdev->activated = true;
+
+       return;
+
+err_out_delayon:
+       device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+}
+
+static void timer_trig_deactivate(struct led_classdev *led_cdev)
+{
+       if (led_cdev->activated) {
+               device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+               device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+               led_cdev->activated = false;
+       }
+
+       /* Stop blinking */
+       led_set_brightness(led_cdev, LED_OFF);
+}
+
+static struct led_trigger timer_led_trigger = {
+       .name     = "timer",
+       .activate = timer_trig_activate,
+       .deactivate = timer_trig_deactivate,
+};
+
+static int __init timer_trig_init(void)
+{
+       return led_trigger_register(&timer_led_trigger);
+}
+
+static void __exit timer_trig_exit(void)
+{
+       led_trigger_unregister(&timer_led_trigger);
+}
+
+module_init(timer_trig_init);
+module_exit(timer_trig_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("Timer LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/trigger/ledtrig-transient.c b/drivers/leds/trigger/ledtrig-transient.c
new file mode 100644 (file)
index 0000000..e5abc00
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * LED Kernel Transient Trigger
+ *
+ * Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
+ *
+ * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
+ * ledtrig-heartbeat.c
+ * Design and use-case input from Jonas Bonn <jonas@southpole.se> and
+ * Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+/*
+ * Transient trigger allows one shot timer activation. Please refer to
+ * Documentation/leds/ledtrig-transient.txt for details
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+struct transient_trig_data {
+       int activate;
+       int state;
+       int restore_state;
+       unsigned long duration;
+       struct timer_list timer;
+};
+
+static void transient_timer_function(unsigned long data)
+{
+       struct led_classdev *led_cdev = (struct led_classdev *) data;
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       transient_data->activate = 0;
+       __led_set_brightness(led_cdev, transient_data->restore_state);
+}
+
+static ssize_t transient_activate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%d\n", transient_data->activate);
+}
+
+static ssize_t transient_activate_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       if (state != 1 && state != 0)
+               return -EINVAL;
+
+       /* cancel the running timer */
+       if (state == 0 && transient_data->activate == 1) {
+               del_timer(&transient_data->timer);
+               transient_data->activate = state;
+               __led_set_brightness(led_cdev, transient_data->restore_state);
+               return size;
+       }
+
+       /* start timer if there is no active timer */
+       if (state == 1 && transient_data->activate == 0 &&
+           transient_data->duration != 0) {
+               transient_data->activate = state;
+               __led_set_brightness(led_cdev, transient_data->state);
+               transient_data->restore_state =
+                   (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
+               mod_timer(&transient_data->timer,
+                         jiffies + transient_data->duration);
+       }
+
+       /* state == 0 && transient_data->activate == 0
+               timer is not active - just return */
+       /* state == 1 && transient_data->activate == 1
+               timer is already active - just return */
+
+       return size;
+}
+
+static ssize_t transient_duration_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%lu\n", transient_data->duration);
+}
+
+static ssize_t transient_duration_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       transient_data->duration = state;
+       return size;
+}
+
+static ssize_t transient_state_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       int state;
+
+       state = (transient_data->state == LED_FULL) ? 1 : 0;
+       return sprintf(buf, "%d\n", state);
+}
+
+static ssize_t transient_state_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       if (state != 1 && state != 0)
+               return -EINVAL;
+
+       transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
+       return size;
+}
+
+static DEVICE_ATTR(activate, 0644, transient_activate_show,
+                  transient_activate_store);
+static DEVICE_ATTR(duration, 0644, transient_duration_show,
+                  transient_duration_store);
+static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
+
+static void transient_trig_activate(struct led_classdev *led_cdev)
+{
+       int rc;
+       struct transient_trig_data *tdata;
+
+       tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
+       if (!tdata) {
+               dev_err(led_cdev->dev,
+                       "unable to allocate transient trigger\n");
+               return;
+       }
+       led_cdev->trigger_data = tdata;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_activate);
+       if (rc)
+               goto err_out;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_duration);
+       if (rc)
+               goto err_out_duration;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_state);
+       if (rc)
+               goto err_out_state;
+
+       setup_timer(&tdata->timer, transient_timer_function,
+                   (unsigned long) led_cdev);
+       led_cdev->activated = true;
+
+       return;
+
+err_out_state:
+       device_remove_file(led_cdev->dev, &dev_attr_duration);
+err_out_duration:
+       device_remove_file(led_cdev->dev, &dev_attr_activate);
+err_out:
+       dev_err(led_cdev->dev, "unable to register transient trigger\n");
+       led_cdev->trigger_data = NULL;
+       kfree(tdata);
+}
+
+static void transient_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               del_timer_sync(&transient_data->timer);
+               __led_set_brightness(led_cdev, transient_data->restore_state);
+               device_remove_file(led_cdev->dev, &dev_attr_activate);
+               device_remove_file(led_cdev->dev, &dev_attr_duration);
+               device_remove_file(led_cdev->dev, &dev_attr_state);
+               led_cdev->trigger_data = NULL;
+               led_cdev->activated = false;
+               kfree(transient_data);
+       }
+}
+
+static struct led_trigger transient_trigger = {
+       .name     = "transient",
+       .activate = transient_trig_activate,
+       .deactivate = transient_trig_deactivate,
+};
+
+static int __init transient_trig_init(void)
+{
+       return led_trigger_register(&transient_trigger);
+}
+
+static void __exit transient_trig_exit(void)
+{
+       led_trigger_unregister(&transient_trigger);
+}
+
+module_init(transient_trig_init);
+module_exit(transient_trig_exit);
+
+MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
+MODULE_DESCRIPTION("Transient LED trigger");
+MODULE_LICENSE("GPL");
index 0d9b5ee..0287ab2 100644 (file)
@@ -142,6 +142,10 @@ extern void led_set_brightness(struct led_classdev *led_cdev,
 /*
  * LED Triggers
  */
+/* Registration functions for simple triggers */
+#define DEFINE_LED_TRIGGER(x)          static struct led_trigger *x;
+#define DEFINE_LED_TRIGGER_GLOBAL(x)   struct led_trigger *x;
+
 #ifdef CONFIG_LEDS_TRIGGERS
 
 #define TRIG_NAME_MAX 50
@@ -164,9 +168,6 @@ struct led_trigger {
 extern int led_trigger_register(struct led_trigger *trigger);
 extern void led_trigger_unregister(struct led_trigger *trigger);
 
-/* Registration functions for simple triggers */
-#define DEFINE_LED_TRIGGER(x)          static struct led_trigger *x;
-#define DEFINE_LED_TRIGGER_GLOBAL(x)   struct led_trigger *x;
 extern void led_trigger_register_simple(const char *name,
                                struct led_trigger **trigger);
 extern void led_trigger_unregister_simple(struct led_trigger *trigger);
@@ -199,20 +200,30 @@ extern void led_trigger_rename_static(const char *name,
 
 #else
 
-/* Triggers aren't active - null macros */
-#define DEFINE_LED_TRIGGER(x)
-#define DEFINE_LED_TRIGGER_GLOBAL(x)
-#define led_trigger_register_simple(x, y) do {} while(0)
-#define led_trigger_unregister_simple(x) do {} while(0)
-#define led_trigger_event(x, y) do {} while(0)
+/* Trigger has no members */
+struct led_trigger {};
 
-#endif
+/* Trigger inline empty functions */
+static inline void led_trigger_register_simple(const char *name,
+                                       struct led_trigger **trigger) {}
+static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
+static inline void led_trigger_event(struct led_trigger *trigger,
+                               enum led_brightness event) {}
+#endif /* CONFIG_LEDS_TRIGGERS */
 
 /* Trigger specific functions */
 #ifdef CONFIG_LEDS_TRIGGER_IDE_DISK
 extern void ledtrig_ide_activity(void);
 #else
-#define ledtrig_ide_activity() do {} while(0)
+static inline void ledtrig_ide_activity(void) {}
+#endif
+
+#if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE)
+extern void ledtrig_flash_ctrl(bool on);
+extern void ledtrig_torch_ctrl(bool on);
+#else
+static inline void ledtrig_flash_ctrl(bool on) {}
+static inline void ledtrig_torch_ctrl(bool on) {}
 #endif
 
 /*
index 8873e83..e326ae2 100644 (file)
@@ -342,9 +342,7 @@ struct mmc_host {
 
        mmc_pm_flag_t           pm_flags;       /* requested pm features */
 
-#ifdef CONFIG_LEDS_TRIGGERS
        struct led_trigger      *led;           /* activity led */
-#endif
 
 #ifdef CONFIG_REGULATOR
        bool                    regulator_enabled; /* regulator state */
index 1509570..202e290 100644 (file)
 #define LP55XX_CLOCK_INT       1
 #define LP55XX_CLOCK_EXT       2
 
-/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */
-#define LP5521_PWM_HF                  0x40    /* PWM: 0 = 256Hz, 1 = 558Hz */
-#define LP5521_PWRSAVE_EN              0x20    /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF             0       /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS          8       /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5             0x10    /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO            0x18    /* Automatic mode selection */
-#define LP5521_R_TO_BATT               4       /* R out: 0 = CP, 1 = Vbat */
-#define LP5521_CLK_SRC_EXT             0       /* Ext-clk source (CLK_32K) */
-#define LP5521_CLK_INT                 1       /* Internal clock */
-#define LP5521_CLK_AUTO                        2       /* Automatic clock selection */
-
 struct lp55xx_led_config {
        const char *name;
        u8 chan_nr;
@@ -40,9 +28,9 @@ struct lp55xx_led_config {
 };
 
 struct lp55xx_predef_pattern {
-       u8 *r;
-       u8 *g;
-       u8 *b;
+       const u8 *r;
+       const u8 *g;
+       const u8 *b;
        u8 size_r;
        u8 size_g;
        u8 size_b;
@@ -79,9 +67,6 @@ struct lp55xx_platform_data {
        /* Predefined pattern data */
        struct lp55xx_predef_pattern *patterns;
        unsigned int num_patterns;
-
-       /* _CONFIG register */
-       u8 update_config;
 };
 
 #endif /* _LEDS_LP55XX_H */