mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
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:
commit
0512c04a2b
32 changed files with 1803 additions and 1787 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
523
drivers/leds/leds-lp55xx-common.c
Normal file
523
drivers/leds/leds-lp55xx-common.c
Normal 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 <[email protected]>");
|
||||
MODULE_DESCRIPTION("LP55xx Common Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
134
drivers/leds/leds-lp55xx-common.h
Normal file
134
drivers/leds/leds-lp55xx-common.h
Normal 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 */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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 ([email protected])");
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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", ®);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue