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

Pull LED subsystem update from Bryan Wu.

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (61 commits)
  leds: leds-sunfire: use dev_err()/pr_err() instead of printk()
  leds: 88pm860x: Add missing of_node_put()
  leds: tca6507: Use of_get_child_count()
  leds: leds-pwm: make it depend on PWM and not HAVE_PWM
  Documentation: leds: update LP55xx family devices
  leds-lp55xx: fix problem on removing LED attributes
  leds-lp5521/5523: add author and copyright description
  leds-lp5521/5523: use new lp55xx common header
  leds-lp55xx: clean up headers
  leds-lp55xx: clean up definitions
  leds-lp55xx: clean up unused data and functions
  leds-lp55xx: clean up _remove()
  leds-lp55xx: add new function for removing device attribtues
  leds-lp55xx: code refactoring on selftest function
  leds-lp55xx: use common device attribute driver function
  leds-lp55xx: support device specific attributes
  leds-lp5523: use generic firmware interface
  leds-lp5521: use generic firmware interface
  leds-lp55xx: support firmware interface
  leds-lp55xx: add new lp55xx_register_sysfs() for the firmware interface
  ...
This commit is contained in:
Linus Torvalds 2013-02-26 09:29:02 -08:00
commit 0512c04a2b
32 changed files with 1845 additions and 1829 deletions

View File

@ -0,0 +1,48 @@
LED connected to PWM
Required properties:
- compatible : should be "pwm-leds".
Each LED is represented as a sub-node of the pwm-leds device. Each
node's name represents the name of the corresponding LED.
LED sub-node properties:
- pwms : PWM property to point to the PWM device (phandle)/port (id) and to
specify the period time to be used: <&phandle id period_ns>;
- pwm-names : (optional) Name to be used by the PWM subsystem for the PWM device
For the pwms and pwm-names property please refer to:
Documentation/devicetree/bindings/pwm/pwm.txt
- max-brightness : Maximum brightness possible for the LED
- label : (optional)
see Documentation/devicetree/bindings/leds/common.txt
- linux,default-trigger : (optional)
see Documentation/devicetree/bindings/leds/common.txt
Example:
twl_pwm: pwm {
/* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
compatible = "ti,twl6030-pwm";
#pwm-cells = <2>;
};
twl_pwmled: pwmled {
/* provides one PWM (id 0 for Charing indicator LED) */
compatible = "ti,twl6030-pwmled";
#pwm-cells = <2>;
};
pwmleds {
compatible = "pwm-leds";
kpad {
label = "omap4::keypad";
pwms = <&twl_pwm 0 7812500>;
max-brightness = <127>;
};
charging {
label = "omap4:green:chrg";
pwms = <&twl_pwmled 0 7812500>;
max-brightness = <255>;
};
};

View File

@ -0,0 +1,33 @@
LEDs conected to tca6507
Required properties:
- compatible : should be : "ti,tca6507".
Each led is represented as a sub-node of the ti,tca6507 device.
LED sub-node properties:
- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
- reg : number of LED line (could be from 0 to 6)
- linux,default-trigger : (optional)
see Documentation/devicetree/bindings/leds/common.txt
Examples:
tca6507@45 {
compatible = "ti,tca6507";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x45>;
led0: red-aux@0 {
label = "red:aux";
reg = <0x0>;
};
led1: green-aux@1 {
label = "green:aux";
reg = <0x5>;
linux,default-trigger = "default-on";
};
};

View File

@ -6,5 +6,7 @@ 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-lp55xx.txt
- description about lp55xx common driver.
leds-lm3556.txt
- notes on how to use the leds-lm3556 driver.

View File

@ -17,19 +17,8 @@ lp5521:channelx, where x is 0 .. 2
All three channels can be also controlled using the engine micro programs.
More details of the instructions can be found from the public data sheet.
Control interface for the engines:
x is 1 .. 3
enginex_mode : disabled, load, run
enginex_load : store program (visible only in engine load mode)
Example (start to blink the channel 2 led):
cd /sys/class/leds/lp5521:channel2/device
echo "load" > engine3_mode
echo "037f4d0003ff6000" > engine3_load
echo "run" > engine3_mode
stop the engine:
echo "disabled" > engine3_mode
LP5521 has the internal program memory for running various LED patterns.
For the details, please refer to 'firmware' section in leds-lp55xx.txt
sysfs contains a selftest entry.
The test communicates with the chip and checks that
@ -47,7 +36,7 @@ The name of each channel can be configurable.
If the name field is not defined, the default name will be set to 'xxxx:channelN'
(XXXX : pdata->label or i2c client name, N : channel number)
static struct lp5521_led_config lp5521_led_config[] = {
static struct lp55xx_led_config lp5521_led_config[] = {
{
.name = "red",
.chan_nr = 0,
@ -81,10 +70,10 @@ static void lp5521_enable(bool state)
/* Control of chip enable signal */
}
static struct lp5521_platform_data lp5521_platform_data = {
static struct lp55xx_platform_data lp5521_platform_data = {
.led_config = lp5521_led_config,
.num_channels = ARRAY_SIZE(lp5521_led_config),
.clock_mode = LP5521_CLOCK_EXT,
.clock_mode = LP55XX_CLOCK_EXT,
.setup_resources = lp5521_setup,
.release_resources = lp5521_release,
.enable = lp5521_enable,
@ -105,47 +94,9 @@ example of update_config :
LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \
LP5521_CLK_INT)
static struct lp5521_platform_data lp5521_pdata = {
static struct lp55xx_platform_data lp5521_pdata = {
.led_config = lp5521_led_config,
.num_channels = ARRAY_SIZE(lp5521_led_config),
.clock_mode = LP5521_CLOCK_INT,
.clock_mode = LP55XX_CLOCK_INT,
.update_config = LP5521_CONFIGS,
};
LED patterns : LP5521 has autonomous operation without external control.
Pattern data can be defined in the platform data.
example of led pattern data :
/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */
static u8 pattern_red[] = {
0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00,
};
static u8 pattern_green[] = {
0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00,
};
static struct lp5521_led_pattern board_led_patterns[] = {
{
.r = pattern_red,
.g = pattern_green,
.size_r = ARRAY_SIZE(pattern_red),
.size_g = ARRAY_SIZE(pattern_green),
},
};
static struct lp5521_platform_data lp5521_platform_data = {
.led_config = lp5521_led_config,
.num_channels = ARRAY_SIZE(lp5521_led_config),
.clock_mode = LP5521_CLOCK_EXT,
.patterns = board_led_patterns,
.num_patterns = ARRAY_SIZE(board_led_patterns),
};
Then predefined led pattern(s) can be executed via the sysfs.
To start the pattern #1,
# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern
(xxxx : i2c bus & slave address)
To end the pattern,
# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern

View File

@ -27,25 +27,8 @@ c) Default
If both fields are NULL, 'lp5523' is used by default.
/sys/class/leds/lp5523:channelN (N: 0 ~ 8)
The chip provides 3 engines. Each engine can control channels without
interaction from the main CPU. Details of the micro engine code can be found
from the public data sheet. Leds can be muxed to different channels.
Control interface for the engines:
x is 1 .. 3
enginex_mode : disabled, load, run
enginex_load : microcode load (visible only in load mode)
enginex_leds : led mux control (visible only in load mode)
cd /sys/class/leds/lp5523:channel2/device
echo "load" > engine3_mode
echo "9d80400004ff05ff437f0000" > engine3_load
echo "111111111" > engine3_leds
echo "run" > engine3_mode
sysfs contains a selftest entry. It measures each channel
voltage level and checks if it looks reasonable. If the level is too high,
the led is missing; if the level is too low, there is a short circuit.
LP5523 has the internal program memory for running various LED patterns.
For the details, please refer to 'firmware' section in leds-lp55xx.txt
Selftest uses always the current from the platform data.
@ -58,7 +41,7 @@ Example platform data:
Note - chan_nr can have values between 0 and 8.
static struct lp5523_led_config lp5523_led_config[] = {
static struct lp55xx_led_config lp5523_led_config[] = {
{
.name = "D1",
.chan_nr = 0,
@ -88,10 +71,10 @@ static void lp5523_enable(bool state)
/* Control chip enable signal */
}
static struct lp5523_platform_data lp5523_platform_data = {
static struct lp55xx_platform_data lp5523_platform_data = {
.led_config = lp5523_led_config,
.num_channels = ARRAY_SIZE(lp5523_led_config),
.clock_mode = LP5523_CLOCK_EXT,
.clock_mode = LP55XX_CLOCK_EXT,
.setup_resources = lp5523_setup,
.release_resources = lp5523_release,
.enable = lp5523_enable,

View File

@ -0,0 +1,118 @@
LP5521/LP5523/LP55231 Common Driver
===================================
Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
Description
-----------
LP5521, LP5523/55231 have common features as below.
Register access via the I2C
Device initialization/deinitialization
Create LED class devices for multiple output channels
Device attributes for user-space interface
Program memory for running LED patterns
The LP55xx common driver provides these features using exported functions.
lp55xx_init_device() / lp55xx_deinit_device()
lp55xx_register_leds() / lp55xx_unregister_leds()
lp55xx_regsister_sysfs() / lp55xx_unregister_sysfs()
( Driver Structure Data )
In lp55xx common driver, two different data structure is used.
o lp55xx_led
control multi output LED channels such as led current, channel index.
o lp55xx_chip
general chip control such like the I2C and platform data.
For example, LP5521 has maximum 3 LED channels.
LP5523/55231 has 9 output channels.
lp55xx_chip for LP5521 ... lp55xx_led #1
lp55xx_led #2
lp55xx_led #3
lp55xx_chip for LP5523 ... lp55xx_led #1
lp55xx_led #2
.
.
lp55xx_led #9
( Chip Dependent Code )
To support device specific configurations, special structure
'lpxx_device_config' is used.
Maximum number of channels
Reset command, chip enable command
Chip specific initialization
Brightness control register access
Setting LED output current
Program memory address access for running patterns
Additional device specific attributes
( Firmware Interface )
LP55xx family devices have the internal program memory for running
various LED patterns.
This pattern data is saved as a file in the user-land or
hex byte string is written into the memory through the I2C.
LP55xx common driver supports the firmware interface.
LP55xx chips have three program engines.
To load and run the pattern, the programming sequence is following.
(1) Select an engine number (1/2/3)
(2) Mode change to load
(3) Write pattern data into selected area
(4) Mode change to run
The LP55xx common driver provides simple interfaces as below.
select_engine : Select which engine is used for running program
run_engine : Start program which is loaded via the firmware interface
firmware : Load program data
For example, run blinking pattern in engine #1 of LP5521
echo 1 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp5521/loading
echo "4000600040FF6000" > /sys/class/firmware/lp5521/data
echo 0 > /sys/class/firmware/lp5521/loading
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
For example, run blinking pattern in engine #3 of LP55231
echo 3 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp55231/loading
echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data
echo 0 > /sys/class/firmware/lp55231/loading
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
To start blinking patterns in engine #2 and #3 simultaneously,
for idx in 2 3
do
echo $idx > /sys/class/leds/red/device/select_engine
sleep 0.1
echo 1 > /sys/class/firmware/lp5521/loading
echo "4000600040FF6000" > /sys/class/firmware/lp5521/data
echo 0 > /sys/class/firmware/lp5521/loading
done
echo 1 > /sys/class/leds/red/device/run_engine
Here is another example for LP5523.
echo 2 > /sys/bus/i2c/devices/xxxx/select_engine
echo 1 > /sys/class/firmware/lp5523/loading
echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data
echo 0 > /sys/class/firmware/lp5523/loading
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
As soon as 'loading' is set to 0, registered callback is called.
Inside the callback, the selected engine is loaded and memory is updated.
To run programmed pattern, 'run_engine' attribute should be enabled.
( 'run_engine' and 'firmware_cb' )
The sequence of running the program data is common.
But each device has own specific register addresses for commands.
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.

View File

@ -40,7 +40,7 @@
#include <sound/tpa6130a2-plat.h>
#include <media/radio-si4713.h>
#include <media/si4713.h>
#include <linux/leds-lp5523.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/platform_data/tsl2563.h>
#include <linux/lis3lv02d.h>
@ -160,7 +160,7 @@ static struct tsl2563_platform_data rx51_tsl2563_platform_data = {
#endif
#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
static struct lp5523_led_config rx51_lp5523_led_config[] = {
static struct lp55xx_led_config rx51_lp5523_led_config[] = {
{
.name = "lp5523:kb1",
.chan_nr = 0,
@ -216,10 +216,10 @@ static void rx51_lp5523_enable(bool state)
gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state);
}
static struct lp5523_platform_data rx51_lp5523_platform_data = {
static struct lp55xx_platform_data rx51_lp5523_platform_data = {
.led_config = rx51_lp5523_led_config,
.num_channels = ARRAY_SIZE(rx51_lp5523_led_config),
.clock_mode = LP5523_CLOCK_AUTO,
.clock_mode = LP55XX_CLOCK_AUTO,
.setup_resources = rx51_lp5523_setup,
.release_resources = rx51_lp5523_release,
.enable = rx51_lp5523_enable,

View File

@ -28,7 +28,7 @@
#include <linux/mfd/tps6105x.h>
#include <linux/mfd/abx500/ab8500-gpio.h>
#include <linux/mfd/abx500/ab8500-codec.h>
#include <linux/leds-lp5521.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/input.h>
#include <linux/smsc911x.h>
#include <linux/gpio_keys.h>
@ -301,7 +301,7 @@ static struct tc3589x_platform_data mop500_tc35892_data = {
.irq_base = MOP500_EGPIO_IRQ_BASE,
};
static struct lp5521_led_config lp5521_pri_led[] = {
static struct lp55xx_led_config lp5521_pri_led[] = {
[0] = {
.chan_nr = 0,
.led_current = 0x2f,
@ -319,14 +319,14 @@ static struct lp5521_led_config lp5521_pri_led[] = {
},
};
static struct lp5521_platform_data __initdata lp5521_pri_data = {
static struct lp55xx_platform_data __initdata lp5521_pri_data = {
.label = "lp5521_pri",
.led_config = &lp5521_pri_led[0],
.num_channels = 3,
.clock_mode = LP5521_CLOCK_EXT,
.clock_mode = LP55XX_CLOCK_EXT,
};
static struct lp5521_led_config lp5521_sec_led[] = {
static struct lp55xx_led_config lp5521_sec_led[] = {
[0] = {
.chan_nr = 0,
.led_current = 0x2f,
@ -344,11 +344,11 @@ static struct lp5521_led_config lp5521_sec_led[] = {
},
};
static struct lp5521_platform_data __initdata lp5521_sec_data = {
static struct lp55xx_platform_data __initdata lp5521_sec_data = {
.label = "lp5521_sec",
.led_config = &lp5521_sec_led[0],
.num_channels = 3,
.clock_mode = LP5521_CLOCK_EXT,
.clock_mode = LP55XX_CLOCK_EXT,
};
static struct i2c_board_info __initdata mop500_i2c0_devices[] = {

View File

@ -193,9 +193,18 @@ config LEDS_LP3944
To compile this driver as a module, choose M here: the
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
select FW_LOADER
help
This option supports common operations for LP5521 and LP5523/55231
devices.
config LEDS_LP5521
tristate "LED Support for N.S. LP5521 LED driver chip"
depends on LEDS_CLASS && I2C
select LEDS_LP55XX_COMMON
help
If you say yes here you get support for the National Semiconductor
LP5521 LED driver. It is 3 channel chip with programmable engines.
@ -205,6 +214,7 @@ config LEDS_LP5521
config LEDS_LP5523
tristate "LED Support for TI/National LP5523/55231 LED driver chip"
depends on LEDS_CLASS && I2C
select LEDS_LP55XX_COMMON
help
If you say yes here you get support for TI/National Semiconductor
LP5523/55231 LED driver.
@ -310,7 +320,7 @@ config LEDS_DAC124S085
config LEDS_PWM
tristate "PWM driven LED Support"
depends on LEDS_CLASS
depends on HAVE_PWM
depends on PWM
help
This option enables support for pwm driven LEDs

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
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_LP8788) += leds-lp8788.o

View File

@ -128,8 +128,10 @@ static void pm860x_led_set(struct led_classdev *cdev,
static int pm860x_led_dt_init(struct platform_device *pdev,
struct pm860x_led *data)
{
struct device_node *nproot = pdev->dev.parent->of_node, *np;
struct device_node *nproot, *np;
int iset = 0;
nproot = of_node_get(pdev->dev.parent->of_node);
if (!nproot)
return -ENODEV;
nproot = of_find_node_by_name(nproot, "leds");
@ -145,6 +147,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
break;
}
}
of_node_put(nproot);
return 0;
}
#else

View File

@ -187,6 +187,40 @@ static void lm3530_als_configure(struct lm3530_platform_data *pdata,
(pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
}
static int lm3530_led_enable(struct lm3530_data *drvdata)
{
int ret;
if (drvdata->enable)
return 0;
ret = regulator_enable(drvdata->regulator);
if (ret) {
dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret);
return ret;
}
drvdata->enable = true;
return 0;
}
static void lm3530_led_disable(struct lm3530_data *drvdata)
{
int ret;
if (!drvdata->enable)
return;
ret = regulator_disable(drvdata->regulator);
if (ret) {
dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n",
ret);
return;
}
drvdata->enable = false;
}
static int lm3530_init_registers(struct lm3530_data *drvdata)
{
int ret = 0;
@ -245,15 +279,9 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
if (!drvdata->enable) {
ret = regulator_enable(drvdata->regulator);
if (ret) {
dev_err(&drvdata->client->dev,
"Enable regulator failed\n");
return ret;
}
drvdata->enable = true;
}
ret = lm3530_led_enable(drvdata);
if (ret)
return ret;
for (i = 0; i < LM3530_REG_MAX; i++) {
/* do not update brightness register when pwm mode */
@ -305,13 +333,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
else
drvdata->brightness = brt_val;
if (brt_val == 0) {
err = regulator_disable(drvdata->regulator);
if (err)
dev_err(&drvdata->client->dev,
"Disable regulator failed\n");
drvdata->enable = false;
}
if (brt_val == 0)
lm3530_led_disable(drvdata);
break;
case LM3530_BL_MODE_ALS:
break;
@ -458,8 +481,7 @@ static int lm3530_remove(struct i2c_client *client)
device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
if (drvdata->enable)
regulator_disable(drvdata->regulator);
lm3530_led_disable(drvdata);
led_classdev_unregister(&drvdata->led_dev);
return 0;
}

View File

@ -380,7 +380,7 @@ static void lm355x_indicator_brightness_set(struct led_classdev *cdev,
/* indicator pattern only for lm3556*/
static ssize_t lm3556_indicator_pattern_store(struct device *dev,
struct device_attribute *devAttr,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t ret;

View File

@ -176,7 +176,7 @@ out:
/* torch pin config for lm3642*/
static ssize_t lm3642_torch_pin_store(struct device *dev,
struct device_attribute *devAttr,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t ret;
@ -233,7 +233,7 @@ static void lm3642_torch_brightness_set(struct led_classdev *cdev,
/* strobe pin config for lm3642*/
static ssize_t lm3642_strobe_pin_store(struct device *dev,
struct device_attribute *devAttr,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t ret;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,523 @@
/*
* LP5521/LP5523/LP55231 Common Driver
*
* Copyright 2012 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.
*
* Derived from leds-lp5521.c, leds-lp5523.c
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_data/leds-lp55xx.h>
#include "leds-lp55xx-common.h"
static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
{
return container_of(cdev, struct lp55xx_led, cdev);
}
static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
{
return cdev_to_lp55xx_led(dev_get_drvdata(dev));
}
static void lp55xx_reset_device(struct lp55xx_chip *chip)
{
struct lp55xx_device_config *cfg = chip->cfg;
u8 addr = cfg->reset.addr;
u8 val = cfg->reset.val;
/* no error checking here because no ACK from the device after reset */
lp55xx_write(chip, addr, val);
}
static int lp55xx_detect_device(struct lp55xx_chip *chip)
{
struct lp55xx_device_config *cfg = chip->cfg;
u8 addr = cfg->enable.addr;
u8 val = cfg->enable.val;
int ret;
ret = lp55xx_write(chip, addr, val);
if (ret)
return ret;
usleep_range(1000, 2000);
ret = lp55xx_read(chip, addr, &val);
if (ret)
return ret;
if (val != cfg->enable.val)
return -ENODEV;
return 0;
}
static int lp55xx_post_init_device(struct lp55xx_chip *chip)
{
struct lp55xx_device_config *cfg = chip->cfg;
if (!cfg->post_init_device)
return 0;
return cfg->post_init_device(chip);
}
static ssize_t lp55xx_show_current(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
return sprintf(buf, "%d\n", led->led_current);
}
static ssize_t lp55xx_store_current(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
struct lp55xx_chip *chip = led->chip;
unsigned long curr;
if (kstrtoul(buf, 0, &curr))
return -EINVAL;
if (curr > led->max_current)
return -EINVAL;
if (!chip->cfg->set_led_current)
return len;
mutex_lock(&chip->lock);
chip->cfg->set_led_current(led, (u8)curr);
mutex_unlock(&chip->lock);
return len;
}
static ssize_t lp55xx_show_max_current(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
return sprintf(buf, "%d\n", led->max_current);
}
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
lp55xx_store_current);
static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL);
static struct attribute *lp55xx_led_attributes[] = {
&dev_attr_led_current.attr,
&dev_attr_max_current.attr,
NULL,
};
static struct attribute_group lp55xx_led_attr_group = {
.attrs = lp55xx_led_attributes
};
static void lp55xx_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
led->brightness = (u8)brightness;
schedule_work(&led->brightness_work);
}
static int lp55xx_init_led(struct lp55xx_led *led,
struct lp55xx_chip *chip, int chan)
{
struct lp55xx_platform_data *pdata = chip->pdata;
struct lp55xx_device_config *cfg = chip->cfg;
struct device *dev = &chip->cl->dev;
char name[32];
int ret;
int max_channel = cfg->max_channel;
if (chan >= max_channel) {
dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
return -EINVAL;
}
if (pdata->led_config[chan].led_current == 0)
return 0;
led->led_current = pdata->led_config[chan].led_current;
led->max_current = pdata->led_config[chan].max_current;
led->chan_nr = pdata->led_config[chan].chan_nr;
if (led->chan_nr >= max_channel) {
dev_err(dev, "Use channel numbers between 0 and %d\n",
max_channel - 1);
return -EINVAL;
}
led->cdev.brightness_set = lp55xx_set_brightness;
if (pdata->led_config[chan].name) {
led->cdev.name = pdata->led_config[chan].name;
} else {
snprintf(name, sizeof(name), "%s:channel%d",
pdata->label ? : chip->cl->name, chan);
led->cdev.name = name;
}
/*
* register led class device for each channel and
* add device attributes
*/
ret = led_classdev_register(dev, &led->cdev);
if (ret) {
dev_err(dev, "led register err: %d\n", ret);
return ret;
}
ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group);
if (ret) {
dev_err(dev, "led sysfs err: %d\n", ret);
led_classdev_unregister(&led->cdev);
return ret;
}
return 0;
}
static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
{
struct lp55xx_chip *chip = context;
struct device *dev = &chip->cl->dev;
if (!fw) {
dev_err(dev, "firmware request failed\n");
goto out;
}
/* handling firmware data is chip dependent */
mutex_lock(&chip->lock);
chip->fw = fw;
if (chip->cfg->firmware_cb)
chip->cfg->firmware_cb(chip);
mutex_unlock(&chip->lock);
out:
/* firmware should be released for other channel use */
release_firmware(chip->fw);
}
static int lp55xx_request_firmware(struct lp55xx_chip *chip)
{
const char *name = chip->cl->name;
struct device *dev = &chip->cl->dev;
return request_firmware_nowait(THIS_MODULE, true, name, dev,
GFP_KERNEL, chip, lp55xx_firmware_loaded);
}
static ssize_t lp55xx_show_engine_select(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
struct lp55xx_chip *chip = led->chip;
return sprintf(buf, "%d\n", chip->engine_idx);
}
static ssize_t lp55xx_store_engine_select(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;
unsigned long val;
int ret;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
/* select the engine to be run */
switch (val) {
case LP55XX_ENGINE_1:
case LP55XX_ENGINE_2:
case LP55XX_ENGINE_3:
mutex_lock(&chip->lock);
chip->engine_idx = val;
ret = lp55xx_request_firmware(chip);
mutex_unlock(&chip->lock);
break;
default:
dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
return -EINVAL;
}
if (ret) {
dev_err(dev, "request firmware err: %d\n", ret);
return ret;
}
return len;
}
static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
{
if (chip->cfg->run_engine)
chip->cfg->run_engine(chip, start);
}
static ssize_t lp55xx_store_engine_run(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;
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
/* run or stop the selected engine */
if (val <= 0) {
lp55xx_run_engine(chip, false);
return len;
}
mutex_lock(&chip->lock);
lp55xx_run_engine(chip, true);
mutex_unlock(&chip->lock);
return len;
}
static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
lp55xx_show_engine_select, lp55xx_store_engine_select);
static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
static struct attribute *lp55xx_engine_attributes[] = {
&dev_attr_select_engine.attr,
&dev_attr_run_engine.attr,
NULL,
};
static const struct attribute_group lp55xx_engine_attr_group = {
.attrs = lp55xx_engine_attributes,
};
int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
{
return i2c_smbus_write_byte_data(chip->cl, reg, val);
}
EXPORT_SYMBOL_GPL(lp55xx_write);
int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
{
s32 ret;
ret = i2c_smbus_read_byte_data(chip->cl, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
EXPORT_SYMBOL_GPL(lp55xx_read);
int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
{
int ret;
u8 tmp;
ret = lp55xx_read(chip, reg, &tmp);
if (ret)
return ret;
tmp &= ~mask;
tmp |= val & mask;
return lp55xx_write(chip, reg, tmp);
}
EXPORT_SYMBOL_GPL(lp55xx_update_bits);
int lp55xx_init_device(struct lp55xx_chip *chip)
{
struct lp55xx_platform_data *pdata;
struct lp55xx_device_config *cfg;
struct device *dev = &chip->cl->dev;
int ret = 0;
WARN_ON(!chip);
pdata = chip->pdata;
cfg = chip->cfg;
if (!pdata || !cfg)
return -EINVAL;
if (pdata->setup_resources) {
ret = pdata->setup_resources();
if (ret < 0) {
dev_err(dev, "setup resoure err: %d\n", ret);
goto err;
}
}
if (pdata->enable) {
pdata->enable(0);
usleep_range(1000, 2000); /* Keep enable down at least 1ms */
pdata->enable(1);
usleep_range(1000, 2000); /* 500us abs min. */
}
lp55xx_reset_device(chip);
/*
* Exact value is not available. 10 - 20ms
* appears to be enough for reset.
*/
usleep_range(10000, 20000);
ret = lp55xx_detect_device(chip);
if (ret) {
dev_err(dev, "device detection err: %d\n", ret);
goto err;
}
/* chip specific initialization */
ret = lp55xx_post_init_device(chip);
if (ret) {
dev_err(dev, "post init device err: %d\n", ret);
goto err_post_init;
}
return 0;
err_post_init:
lp55xx_deinit_device(chip);
err:
return ret;
}
EXPORT_SYMBOL_GPL(lp55xx_init_device);
void lp55xx_deinit_device(struct lp55xx_chip *chip)
{
struct lp55xx_platform_data *pdata = chip->pdata;
if (pdata->enable)
pdata->enable(0);
if (pdata->release_resources)
pdata->release_resources();
}
EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
{
struct lp55xx_platform_data *pdata = chip->pdata;
struct lp55xx_device_config *cfg = chip->cfg;
int num_channels = pdata->num_channels;
struct lp55xx_led *each;
u8 led_current;
int ret;
int i;
if (!cfg->brightness_work_fn) {
dev_err(&chip->cl->dev, "empty brightness configuration\n");
return -EINVAL;
}
for (i = 0; i < num_channels; i++) {
/* do not initialize channels that are not connected */
if (pdata->led_config[i].led_current == 0)
continue;
led_current = pdata->led_config[i].led_current;
each = led + i;
ret = lp55xx_init_led(each, chip, i);
if (ret)
goto err_init_led;
INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
chip->num_leds++;
each->chip = chip;
/* setting led current at each channel */
if (cfg->set_led_current)
cfg->set_led_current(each, led_current);
}
return 0;
err_init_led:
lp55xx_unregister_leds(led, chip);
return ret;
}
EXPORT_SYMBOL_GPL(lp55xx_register_leds);
void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
{
int i;
struct lp55xx_led *each;
for (i = 0; i < chip->num_leds; i++) {
each = led + i;
led_classdev_unregister(&each->cdev);
flush_work(&each->brightness_work);
}
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
int lp55xx_register_sysfs(struct lp55xx_chip *chip)
{
struct device *dev = &chip->cl->dev;
struct lp55xx_device_config *cfg = chip->cfg;
int ret;
if (!cfg->run_engine || !cfg->firmware_cb)
goto dev_specific_attrs;
ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
if (ret)
return ret;
dev_specific_attrs:
return cfg->dev_attr_group ?
sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
}
EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
{
struct device *dev = &chip->cl->dev;
struct lp55xx_device_config *cfg = chip->cfg;
if (cfg->dev_attr_group)
sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
MODULE_DESCRIPTION("LP55xx Common Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,134 @@
/*
* LP55XX Common Driver Header
*
* Copyright (C) 2012 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.
*
* Derived from leds-lp5521.c, leds-lp5523.c
*/
#ifndef _LEDS_LP55XX_COMMON_H
#define _LEDS_LP55XX_COMMON_H
enum lp55xx_engine_index {
LP55XX_ENGINE_INVALID,
LP55XX_ENGINE_1,
LP55XX_ENGINE_2,
LP55XX_ENGINE_3,
};
struct lp55xx_led;
struct lp55xx_chip;
/*
* struct lp55xx_reg
* @addr : Register address
* @val : Register value
*/
struct lp55xx_reg {
u8 addr;
u8 val;
};
/*
* struct lp55xx_device_config
* @reset : Chip specific reset command
* @enable : Chip specific enable command
* @max_channel : Maximum number of channels
* @post_init_device : Chip specific initialization code
* @brightness_work_fn : Brightness work function
* @set_led_current : LED current set function
* @firmware_cb : Call function when the firmware is loaded
* @run_engine : Run internal engine for pattern
* @dev_attr_group : Device specific attributes
*/
struct lp55xx_device_config {
const struct lp55xx_reg reset;
const struct lp55xx_reg enable;
const int max_channel;
/* define if the device has specific initialization process */
int (*post_init_device) (struct lp55xx_chip *chip);
/* access brightness register */
void (*brightness_work_fn)(struct work_struct *work);
/* current setting function */
void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
/* access program memory when the firmware is loaded */
void (*firmware_cb)(struct lp55xx_chip *chip);
/* used for running firmware LED patterns */
void (*run_engine) (struct lp55xx_chip *chip, bool start);
/* additional device specific attributes */
const struct attribute_group *dev_attr_group;
};
/*
* struct lp55xx_chip
* @cl : I2C communication for access registers
* @pdata : Platform specific data
* @lock : Lock for user-space interface
* @num_leds : Number of registered LEDs
* @cfg : Device specific configuration data
* @engine_idx : Selected engine number
* @fw : Firmware data for running a LED pattern
*/
struct lp55xx_chip {
struct i2c_client *cl;
struct lp55xx_platform_data *pdata;
struct mutex lock; /* lock for user-space interface */
int num_leds;
struct lp55xx_device_config *cfg;
enum lp55xx_engine_index engine_idx;
const struct firmware *fw;
};
/*
* struct lp55xx_led
* @chan_nr : Channel number
* @cdev : LED class device
* @led_current : Current setting at each led channel
* @max_current : Maximun current at each led channel
* @brightness_work : Workqueue for brightness control
* @brightness : Brightness value
* @chip : The lp55xx chip data
*/
struct lp55xx_led {
int chan_nr;
struct led_classdev cdev;
u8 led_current;
u8 max_current;
struct work_struct brightness_work;
u8 brightness;
struct lp55xx_chip *chip;
};
/* register access */
extern int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val);
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);
/* common device init/deinit functions */
extern int lp55xx_init_device(struct lp55xx_chip *chip);
extern void lp55xx_deinit_device(struct lp55xx_chip *chip);
/* common LED class device functions */
extern int lp55xx_register_leds(struct lp55xx_led *led,
struct lp55xx_chip *chip);
extern void lp55xx_unregister_leds(struct lp55xx_led *led,
struct lp55xx_chip *chip);
/* common device attributes functions */
extern int lp55xx_register_sysfs(struct lp55xx_chip *chip);
extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip);
#endif /* _LEDS_LP55XX_COMMON_H */

View File

@ -130,9 +130,10 @@ static int lp8788_led_probe(struct platform_device *pdev)
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
struct lp8788_led_platform_data *led_pdata;
struct lp8788_led *led;
struct device *dev = &pdev->dev;
int ret;
led = devm_kzalloc(lp->dev, sizeof(struct lp8788_led), GFP_KERNEL);
led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL);
if (!led)
return -ENOMEM;
@ -154,13 +155,13 @@ static int lp8788_led_probe(struct platform_device *pdev)
ret = lp8788_led_init_device(led, led_pdata);
if (ret) {
dev_err(lp->dev, "led init device err: %d\n", ret);
dev_err(dev, "led init device err: %d\n", ret);
return ret;
}
ret = led_classdev_register(lp->dev, &led->led_dev);
ret = led_classdev_register(dev, &led->led_dev);
if (ret) {
dev_err(lp->dev, "led register err: %d\n", ret);
dev_err(dev, "led register err: %d\n", ret);
return ret;
}

View File

@ -186,7 +186,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
int err = 0;
if (*delay_on == 0 && *delay_off == 0) {
/* led subsystem ask us for a blink rate */
/* led subsystem ask us for a blink rate */
*delay_on = 1000;
*delay_off = 1000;
}
@ -311,7 +311,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
break;
case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) {
input_unregister_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL;
}
@ -382,7 +381,7 @@ static int pca9532_configure(struct i2c_client *client,
BUG_ON(data->idev);
led->state = PCA9532_PWM1;
pca9532_setled(led);
data->idev = input_allocate_device();
data->idev = devm_input_allocate_device(&client->dev);
if (data->idev == NULL) {
err = -ENOMEM;
goto exit;
@ -401,7 +400,6 @@ static int pca9532_configure(struct i2c_client *client,
INIT_WORK(&data->work, pca9532_input_work);
err = input_register_device(data->idev);
if (err) {
input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL;
goto exit;

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/fb.h>
#include <linux/leds.h>
#include <linux/err.h>
@ -30,6 +31,11 @@ struct led_pwm_data {
unsigned int period;
};
struct led_pwm_priv {
int num_leds;
struct led_pwm_data leds[0];
};
static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@ -47,88 +53,152 @@ static void led_pwm_set(struct led_classdev *led_cdev,
}
}
static int led_pwm_probe(struct platform_device *pdev)
static inline size_t sizeof_pwm_leds_priv(int num_leds)
{
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
struct led_pwm *cur_led;
struct led_pwm_data *leds_data, *led_dat;
int i, ret = 0;
return sizeof(struct led_pwm_priv) +
(sizeof(struct led_pwm_data) * num_leds);
}
if (!pdata)
return -EBUSY;
static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *child;
struct led_pwm_priv *priv;
int count, ret;
leds_data = devm_kzalloc(&pdev->dev,
sizeof(struct led_pwm_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
/* count LEDs in this device, so we know how much to allocate */
count = of_get_child_count(node);
if (!count)
return NULL;
for (i = 0; i < pdata->num_leds; i++) {
cur_led = &pdata->leds[i];
led_dat = &leds_data[i];
priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count),
GFP_KERNEL);
if (!priv)
return NULL;
led_dat->pwm = pwm_request(cur_led->pwm_id,
cur_led->name);
for_each_child_of_node(node, child) {
struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
led_dat->cdev.name = of_get_property(child, "label",
NULL) ? : child->name;
led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
if (IS_ERR(led_dat->pwm)) {
ret = PTR_ERR(led_dat->pwm);
dev_err(&pdev->dev, "unable to request PWM %d\n",
cur_led->pwm_id);
dev_err(&pdev->dev, "unable to request PWM for %s\n",
led_dat->cdev.name);
goto err;
}
/* Get the period from PWM core when n*/
led_dat->period = pwm_get_period(led_dat->pwm);
led_dat->cdev.default_trigger = of_get_property(child,
"linux,default-trigger", NULL);
of_property_read_u32(child, "max-brightness",
&led_dat->cdev.max_brightness);
led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->active_low = cur_led->active_low;
led_dat->period = cur_led->pwm_period_ns;
led_dat->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.max_brightness = cur_led->max_brightness;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0) {
pwm_free(led_dat->pwm);
dev_err(&pdev->dev, "failed to register for %s\n",
led_dat->cdev.name);
of_node_put(child);
goto err;
}
priv->num_leds++;
}
platform_set_drvdata(pdev, leds_data);
return priv;
err:
while (priv->num_leds--)
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
return NULL;
}
static int led_pwm_probe(struct platform_device *pdev)
{
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
struct led_pwm_priv *priv;
int i, ret = 0;
if (pdata && pdata->num_leds) {
priv = devm_kzalloc(&pdev->dev,
sizeof_pwm_leds_priv(pdata->num_leds),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
struct led_pwm *cur_led = &pdata->leds[i];
struct led_pwm_data *led_dat = &priv->leds[i];
led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
if (IS_ERR(led_dat->pwm)) {
ret = PTR_ERR(led_dat->pwm);
dev_err(&pdev->dev,
"unable to request PWM for %s\n",
cur_led->name);
goto err;
}
led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->active_low = cur_led->active_low;
led_dat->period = cur_led->pwm_period_ns;
led_dat->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.max_brightness = cur_led->max_brightness;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0)
goto err;
}
priv->num_leds = pdata->num_leds;
} else {
priv = led_pwm_create_of(pdev);
if (!priv)
return -ENODEV;
}
platform_set_drvdata(pdev, priv);
return 0;
err:
if (i > 0) {
for (i = i - 1; i >= 0; i--) {
led_classdev_unregister(&leds_data[i].cdev);
pwm_free(leds_data[i].pwm);
}
}
while (i--)
led_classdev_unregister(&priv->leds[i].cdev);
return ret;
}
static int led_pwm_remove(struct platform_device *pdev)
{
struct led_pwm_priv *priv = platform_get_drvdata(pdev);
int i;
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
struct led_pwm_data *leds_data;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++) {
led_classdev_unregister(&leds_data[i].cdev);
pwm_free(leds_data[i].pwm);
}
for (i = 0; i < priv->num_leds; i++)
led_classdev_unregister(&priv->leds[i].cdev);
return 0;
}
static const struct of_device_id of_pwm_leds_match[] = {
{ .compatible = "pwm-leds", },
{},
};
MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
static struct platform_driver led_pwm_driver = {
.probe = led_pwm_probe,
.remove = led_pwm_remove,
.driver = {
.name = "leds_pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_pwm_leds_match),
},
};

View File

@ -133,24 +133,24 @@ static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness)
rate = clk_get_rate(p->clk);
/* pick the lowest acceptable rate */
for (k = 0; k < ARRAY_SIZE(prescaler); k++)
if ((rate / prescaler[k]) < p->min_rate)
for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--)
if ((rate / prescaler[k]) >= p->min_rate)
break;
if (!k) {
if (k < 0) {
dev_err(&p->pdev->dev, "clock rate mismatch\n");
goto err0;
}
dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n",
rate, prescaler[k - 1]);
rate, prescaler[k]);
/* clear TCNT on TGRB match, count on rising edge, set prescaler */
r_tpu_write(p, TCR, 0x0040 | (k - 1));
r_tpu_write(p, TCR, 0x0040 | k);
/* output 0 until TGRA, output 1 until TGRB */
r_tpu_write(p, TIOR, 0x0002);
rate /= prescaler[k - 1] * p->refresh_rate;
rate /= prescaler[k] * p->refresh_rate;
r_tpu_write(p, TGRB, rate);
dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate);

View File

@ -63,8 +63,7 @@ MODULE_LICENSE("GPL");
/*
* PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
*/
static const struct pci_device_id ich7_lpc_pci_id[] =
{
static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },

View File

@ -3,6 +3,8 @@
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@ -14,9 +16,6 @@
#include <asm/fhc.h>
#include <asm/upa.h>
#define DRIVER_NAME "leds-sunfire"
#define PFX DRIVER_NAME ": "
MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
MODULE_DESCRIPTION("Sun Fire LED driver");
MODULE_LICENSE("GPL");
@ -130,14 +129,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
int i, err;
if (pdev->num_resources != 1) {
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n",
pdev->num_resources);
return -EINVAL;
}
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (!p) {
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n");
return -ENOMEM;
}
@ -152,7 +151,7 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
err = led_classdev_register(&pdev->dev, lp);
if (err) {
printk(KERN_ERR PFX "Could not register %s LED\n",
dev_err(&pdev->dev, "Could not register %s LED\n",
lp->name);
for (i--; i >= 0; i--)
led_classdev_unregister(&p->leds[i].led_cdev);
@ -188,7 +187,7 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = {
{
.name = "clockboard-right",
.handler = clockboard_right_set,
.default_trigger= "heartbeat",
.default_trigger = "heartbeat",
},
};
@ -209,7 +208,7 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = {
{
.name = "fhc-right",
.handler = fhc_right_set,
.default_trigger= "heartbeat",
.default_trigger = "heartbeat",
},
};
@ -244,13 +243,13 @@ static int __init sunfire_leds_init(void)
int err = platform_driver_register(&sunfire_clockboard_led_driver);
if (err) {
printk(KERN_ERR PFX "Could not register clock board LED driver\n");
pr_err("Could not register clock board LED driver\n");
return err;
}
err = platform_driver_register(&sunfire_fhc_led_driver);
if (err) {
printk(KERN_ERR PFX "Could not register FHC LED driver\n");
pr_err("Could not register FHC LED driver\n");
platform_driver_unregister(&sunfire_clockboard_led_driver);
}

View File

@ -667,8 +667,68 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca)
}
#endif /* CONFIG_GPIOLIB */
#ifdef CONFIG_OF
static struct tca6507_platform_data *
tca6507_led_dt_init(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node, *child;
struct tca6507_platform_data *pdata;
struct led_info *tca_leds;
int count;
count = of_get_child_count(np);
if (!count || count > NUM_LEDS)
return ERR_PTR(-ENODEV);
tca_leds = devm_kzalloc(&client->dev,
sizeof(struct led_info) * count, GFP_KERNEL);
if (!tca_leds)
return ERR_PTR(-ENOMEM);
for_each_child_of_node(np, child) {
struct led_info led;
u32 reg;
int ret;
led.name =
of_get_property(child, "label", NULL) ? : child->name;
led.default_trigger =
of_get_property(child, "linux,default-trigger", NULL);
ret = of_property_read_u32(child, "reg", &reg);
if (ret != 0)
continue;
tca_leds[reg] = led;
}
pdata = devm_kzalloc(&client->dev,
sizeof(struct tca6507_platform_data), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->leds.leds = tca_leds;
pdata->leds.num_leds = count;
return pdata;
}
static const struct of_device_id of_tca6507_leds_match[] = {
{ .compatible = "ti,tca6507", },
{},
};
#else
static struct tca6507_platform_data *
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,
const struct i2c_device_id *id)
const struct i2c_device_id *id)
{
struct tca6507_chip *tca;
struct i2c_adapter *adapter;
@ -683,9 +743,12 @@ static int tca6507_probe(struct i2c_client *client,
return -EIO;
if (!pdata || pdata->leds.num_leds != NUM_LEDS) {
dev_err(&client->dev, "Need %d entries in platform-data list\n",
NUM_LEDS);
return -ENODEV;
pdata = tca6507_led_dt_init(client);
if (IS_ERR(pdata)) {
dev_err(&client->dev, "Need %d entries in platform-data list\n",
NUM_LEDS);
return PTR_ERR(pdata);
}
}
tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL);
if (!tca)
@ -750,6 +813,7 @@ static struct i2c_driver tca6507_driver = {
.driver = {
.name = "leds-tca6507",
.owner = THIS_MODULE,
.of_match_table = of_tca6507_leds_match,
},
.probe = tca6507_probe,
.remove = tca6507_remove,

View File

@ -157,7 +157,7 @@ static int wm831x_status_blink_set(struct led_classdev *led_cdev,
return ret;
}
static const char *led_src_texts[] = {
static const char * const led_src_texts[] = {
"otp",
"power",
"charger",

View File

@ -471,7 +471,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
}
/**
* of_pwm_request() - request a PWM via the PWM framework
* of_pwm_get() - request a PWM via the PWM framework
* @np: device node to get the PWM from
* @con_id: consumer name
*
@ -486,8 +486,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
* becomes mandatory for devices that look up the PWM device via the con_id
* parameter.
*/
static struct pwm_device *of_pwm_request(struct device_node *np,
const char *con_id)
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
{
struct pwm_device *pwm = NULL;
struct of_phandle_args args;
@ -545,6 +544,7 @@ put:
return pwm;
}
EXPORT_SYMBOL_GPL(of_pwm_get);
/**
* pwm_add_table() - register PWM device consumers
@ -587,7 +587,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
/* look up via DT first */
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
return of_pwm_request(dev->of_node, con_id);
return of_pwm_get(dev->of_node, con_id);
/*
* We look up the provider in the static table typically provided by
@ -708,6 +708,36 @@ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)
}
EXPORT_SYMBOL_GPL(devm_pwm_get);
/**
* devm_of_pwm_get() - resource managed of_pwm_get()
* @dev: device for PWM consumer
* @np: device node to get the PWM from
* @con_id: consumer name
*
* This function performs like of_pwm_get() but the acquired PWM device will
* automatically be released on driver detach.
*/
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
const char *con_id)
{
struct pwm_device **ptr, *pwm;
ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
pwm = of_pwm_get(np, con_id);
if (!IS_ERR(pwm)) {
*ptr = pwm;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return pwm;
}
EXPORT_SYMBOL_GPL(devm_of_pwm_get);
static int devm_pwm_match(struct device *dev, void *res, void *data)
{
struct pwm_device **p = res;

View File

@ -1,73 +0,0 @@
/*
* LP5521 LED chip driver.
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Samu Onkalo <samu.p.onkalo@nokia.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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef __LINUX_LP5521_H
#define __LINUX_LP5521_H
/* See Documentation/leds/leds-lp5521.txt */
struct lp5521_led_config {
char *name;
u8 chan_nr;
u8 led_current; /* mA x10, 0 if led is not connected */
u8 max_current;
};
struct lp5521_led_pattern {
u8 *r;
u8 *g;
u8 *b;
u8 size_r;
u8 size_g;
u8 size_b;
};
#define LP5521_CLOCK_AUTO 0
#define LP5521_CLOCK_INT 1
#define LP5521_CLOCK_EXT 2
/* Bits in 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 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 lp5521_platform_data {
struct lp5521_led_config *led_config;
u8 num_channels;
u8 clock_mode;
int (*setup_resources)(void);
void (*release_resources)(void);
void (*enable)(bool state);
const char *label;
u8 update_config;
struct lp5521_led_pattern *patterns;
int num_patterns;
};
#endif /* __LINUX_LP5521_H */

View File

@ -1,49 +0,0 @@
/*
* LP5523 LED Driver
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Samu Onkalo <samu.p.onkalo@nokia.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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef __LINUX_LP5523_H
#define __LINUX_LP5523_H
/* See Documentation/leds/leds-lp5523.txt */
struct lp5523_led_config {
const char *name;
u8 chan_nr;
u8 led_current; /* mA x10, 0 if led is not connected */
u8 max_current;
};
#define LP5523_CLOCK_AUTO 0
#define LP5523_CLOCK_INT 1
#define LP5523_CLOCK_EXT 2
struct lp5523_platform_data {
struct lp5523_led_config *led_config;
u8 num_channels;
u8 clock_mode;
int (*setup_resources)(void);
void (*release_resources)(void);
void (*enable)(bool state);
const char *label;
};
#endif /* __LINUX_LP5523_H */

View File

@ -7,7 +7,7 @@
struct led_pwm {
const char *name;
const char *default_trigger;
unsigned pwm_id;
unsigned pwm_id __deprecated;
u8 active_low;
unsigned max_brightness;
unsigned pwm_period_ns;

View File

@ -0,0 +1,87 @@
/*
* LP55XX Platform Data Header
*
* Copyright (C) 2012 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.
*
* Derived from leds-lp5521.h, leds-lp5523.h
*/
#ifndef _LEDS_LP55XX_H
#define _LEDS_LP55XX_H
/* Clock configuration */
#define LP55XX_CLOCK_AUTO 0
#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;
u8 led_current; /* mA x10, 0 if led is not connected */
u8 max_current;
};
struct lp55xx_predef_pattern {
u8 *r;
u8 *g;
u8 *b;
u8 size_r;
u8 size_g;
u8 size_b;
};
/*
* struct lp55xx_platform_data
* @led_config : Configurable led class device
* @num_channels : Number of LED channels
* @label : Used for naming LEDs
* @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT
* @setup_resources : Platform specific function before enabling the chip
* @release_resources : Platform specific function after disabling the chip
* @enable : EN pin control by platform side
* @patterns : Predefined pattern data for RGB channels
* @num_patterns : Number of patterns
* @update_config : Value of CONFIG register
*/
struct lp55xx_platform_data {
/* LED channel configuration */
struct lp55xx_led_config *led_config;
u8 num_channels;
const char *label;
/* Clock configuration */
u8 clock_mode;
/* Platform specific functions */
int (*setup_resources)(void);
void (*release_resources)(void);
void (*enable)(bool state);
/* Predefined pattern data */
struct lp55xx_predef_pattern *patterns;
unsigned int num_patterns;
/* _CONFIG register */
u8 update_config;
};
#endif /* _LEDS_LP55XX_H */

View File

@ -174,10 +174,13 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args);
struct pwm_device *pwm_get(struct device *dev, const char *consumer);
struct pwm_device *pwm_get(struct device *dev, const char *con_id);
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id);
void pwm_put(struct pwm_device *pwm);
struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer);
struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
const char *con_id);
void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
#else
static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
@ -213,6 +216,12 @@ static inline struct pwm_device *pwm_get(struct device *dev,
return ERR_PTR(-ENODEV);
}
static inline struct pwm_device *of_pwm_get(struct device_node *np,
const char *con_id)
{
return ERR_PTR(-ENODEV);
}
static inline void pwm_put(struct pwm_device *pwm)
{
}
@ -223,6 +232,13 @@ static inline struct pwm_device *devm_pwm_get(struct device *dev,
return ERR_PTR(-ENODEV);
}
static inline struct pwm_device *devm_of_pwm_get(struct device *dev,
struct device_node *np,
const char *con_id)
{
return ERR_PTR(-ENODEV);
}
static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm)
{
}