android_kernel_modules_leno.../drivers/leds/leds-lp5560.c

498 lines
12 KiB
C

/*
* =====================================================================================
*
* Filename: drivers/leds/leds-lp5560.c
*
* Description: lp5560 led driver
*
* Version: 0.1
* Created: 06/07/2013 04:28:25 PM
* Revision: none
* Compiler: gcc
*
* Author: LENOVO
* Company: LENOVO Inc.
*
* =====================================================================================
*/
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/hrtimer.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/tablet_config.h>
#include <asm/io.h>
#include "leds-lp5560.h"
//#define USE_HRTIMER
#if defined(BLADE2_8) || defined(BLADE2_10)
#define LED_CTRL_GPIO 112
#elif defined(BLADE2_13)
#define LED_CTRL_GPIO 51
#endif
#ifndef USE_HRTIMER
DEFINE_SPINLOCK(lp5560_lock);
#endif
/* ---------------------------------------------------------------------------- */
#define LED_DEBUG
#if defined(LED_DEBUG)
#define LED_TAG "[LP5560] "
#define LED_FUN(f) printk(KERN_INFO LED_TAG"%s\n", __FUNCTION__)
#define LED_ERR(fmt, args...) printk(KERN_ERR LED_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
#define LED_LOG(fmt, args...) printk(KERN_ERR LED_TAG fmt, ##args)
#define LED_DBG(fmt, args...) printk(KERN_INFO LED_TAG fmt, ##args)
#else
#define LED_FUN(f)
#define LED_ERR(fmt, args...) printk(KERN_ERR LED_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
#define LED_LOG(fmt, args...)
#define LED_DBG(fmt, args...)
#endif
#define CALI_LENGHT 500
#define MAX_CUST_TIME 32
u16 delay_onms[MAX_CUST_TIME] = {13, 26, 52, 105, 158, 211, 264, 316, 369, 435,
501, 594, 699, 805, 910, 1016,1122,1227,1353,1478,
1603,1729,1854,1980,2105,2230,2356,2481,2613,2745,
2877,3009};
u16 delay_offms[MAX_CUST_TIME] = {26, 52, 105, 211, 316, 422, 528, 633, 739, 871,
1001,1188,1399,1610,1821,2032,2244,2455,2706,2956,
3207,3458,3709,3960,4210,4461,4712,4963,5227,5491,
5755,6019};
union u_full_train common_train = {
.train.reset_start[0] = {
.high = 50, .low = 50,
},
.train.reset_start[1] = {
.high = 50, .low = 50 + 800,
},
.train.reset_end[0 ... 2] = {
.high = 50, .low = 50,
},
.train.train_start[0] = {
.high = 50, .low = 50,
},
.train.train_start[1] = {
.high = 50, .low = 50 + 800,
},
.train.calib = {
.high = CALI_LENGHT, .low = 50,
},
.train.cur = {
.high = CALI_LENGHT * 7,
.low = CALI_LENGHT * 7,
},
.train.train_seq[0 ... 2] = {
.rise = CALI_LENGHT * 5,
.on = CALI_LENGHT * 8,
.fall = CALI_LENGHT * 5,
.off = CALI_LENGHT * 8,
},
.train.train_end[0 ... 2] = {
.high = 50, .low = 50,
},
};
#ifdef USE_HRTIMER
static struct hrtimer lp5560_hrtimer;
#endif
static int g_array_i = 0;
u16 g_array_max = 0;
u32 g_array[MAX_COUNT * 2] = {0};
static unsigned long onMS;
static unsigned long offMS;
static unsigned char on_pulse;
static unsigned char off_pulse;
static unsigned char blink;
static int led_blink_flag = 0;
static ssize_t show_delay_on(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%ld\n", onMS);
}
static ssize_t store_delay_on(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
if (strict_strtoul(buf, 0, &onMS))
return -EINVAL;
if (onMS > delay_onms[MAX_CUST_TIME - 1]) {
onMS = delay_onms[MAX_CUST_TIME - 1];
}
return len;
}
static ssize_t show_delay_off(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%ld\n", offMS);
}
static ssize_t store_delay_off(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
if (strict_strtoul(buf, 0, &offMS))
return -EINVAL;
if (offMS > delay_offms[MAX_CUST_TIME - 1]) {
offMS = delay_offms[MAX_CUST_TIME - 1];
}
return len;
}
/*
static ssize_t show_blink(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%ld\n", blink);
}
static ssize_t store_blink(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
LED_LOG("offMS : %s\n", buf);
if (strict_strtoul(buf, 0, &blink))
return -EINVAL;
onMS = 900;
offMS = 500;
lp5560_mode_set(LED_FLASH);
return len;
}
static LP5560_ATTR_RW(delay_on);
static LP5560_ATTR_RW(delay_off);
static LP5560_ATTR_RW(blink);
static struct attribute *lp5560_led_attributes[] = {
&dev_attr_delay_on.attr,
&dev_attr_delay_off.attr,
&dev_attr_blink.attr,
NULL,
};
static struct attribute_group lp5560_led_attribute_group = {
.attrs = lp5560_led_attributes
};
*/
#ifdef USE_HRTIMER
static enum hrtimer_restart lp5560_callback(struct hrtimer *timer)
{
if (g_array_i < g_array_max) {
if (g_array_i % 2 == 0)
gpio_set_value(LED_CTRL_GPIO, 1);
else
gpio_set_value(LED_CTRL_GPIO, 0);
hrtimer_add_expires_ns(&lp5560_hrtimer, 1000 * g_array[g_array_i++]);
return HRTIMER_RESTART;
}
gpio_set_value(LED_CTRL_GPIO, 1);
return HRTIMER_NORESTART;
}
#endif
static void lp5560_mode_set(u16 mode)
{
int i;
int ms1;
int off = 0;
/* standby mode */
gpio_set_value(LED_CTRL_GPIO, 0);
msleep(1);
switch(mode) {
case LED_TURN_ON:
common_train.train.reset_end[2].low = 5000; /* reset done. delay 5ms */
for (i = 0; i < 3; i++) {
common_train.train.train_seq[i].rise = 0;
common_train.train.train_seq[i].on = 0;
common_train.train.train_seq[i].fall = 0;
common_train.train.train_seq[i].off = 0;
}
common_train.train.train_seq[0].rise = CALI_LENGHT * 8;
common_train.train.train_seq[0].on = CALI_LENGHT * 8;
common_train.train.train_end[2].low = 5000; /* command done. delay 5ms */
break;
case LED_TURN_OFF:
off = 1;
gpio_set_value(LED_CTRL_GPIO, 0);
break;
case LED_FLASH:
common_train.train.reset_end[2].low = 5000;
for (i = 0; i < 3; i++) {
common_train.train.train_seq[i].rise = CALI_LENGHT * 5;
common_train.train.train_seq[i].on = CALI_LENGHT * on_pulse + CALI_LENGHT/2;
common_train.train.train_seq[i].fall = CALI_LENGHT * 5;
common_train.train.train_seq[i].off = CALI_LENGHT * off_pulse + CALI_LENGHT/2;
}
common_train.train.train_end[2].low = 5000; /* command down. delay 5ms */
break;
case LED_RESET:
break;
case LED_SET_CURRENT:
break;
default:
break;
}
if (mode != LED_TURN_OFF)
{
g_array_max = 0;
for (i = 0; i < MAX_COUNT * 2; i++) {
if (common_train.train_array[i] != 0)
g_array[g_array_max++] = common_train.train_array[i];
}
#ifdef USE_HRTIMER
g_array_i = 0;
hrtimer_cancel(&lp5560_hrtimer);
hrtimer_start(&lp5560_hrtimer, ktime_set(0, 0), HRTIMER_MODE_REL);
#else
spin_lock(&lp5560_lock);
for (i = 0; i < g_array_max; i++) {
if (i % 2 == 0)
gpio_set_value(LED_CTRL_GPIO,1);
else
gpio_set_value(LED_CTRL_GPIO, 0);
ms1 = g_array[i];
while(ms1 > 2000)
{
udelay(2000);
ms1 -= 2000;
}
udelay(ms1);
if(off){
gpio_set_value(LED_CTRL_GPIO, 0);
break;
}
}
spin_unlock(&lp5560_lock);
if (mode == LED_RESET)
gpio_set_value(LED_CTRL_GPIO, 0);
else
gpio_set_value(LED_CTRL_GPIO, 1);
#endif
}
}
static ssize_t show_blink(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%ld\n", blink);
}
static ssize_t store_blink(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
if (strict_strtoul(buf, 0, &blink))
return -EINVAL;
printk("set led blink = %d.\n", blink);
if(blink != 0){
led_blink_flag = 1;
lp5560_mode_set(LED_FLASH);
} else {
led_blink_flag = 0;
lp5560_mode_set(LED_TURN_OFF);
}
return len;
}
static LP5560_ATTR_RW(delay_on);
static LP5560_ATTR_RW(delay_off);
static LP5560_ATTR_RW(blink);
static struct attribute *lp5560_led_attributes[] = {
&dev_attr_delay_on.attr,
&dev_attr_delay_off.attr,
&dev_attr_blink.attr,
NULL,
};
static struct attribute_group lp5560_led_attribute_group = {
.attrs = lp5560_led_attributes
};
static void lp5560_led_white_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
int i;
if(led_blink_flag == 1){
printk("set led brightness = %d.\n", value);
return;
}
printk("set led brightness = %d.\n", value);
common_train.train.cur.high = (64 / 32) * CALI_LENGHT + CALI_LENGHT/2;
common_train.train.cur.low = (64 / 32) * CALI_LENGHT + CALI_LENGHT/2;
switch (value) {
case 0:
lp5560_mode_set(LED_TURN_OFF);
break;
case 255:
lp5560_mode_set(LED_TURN_ON);
break;
case 128:
onMS = 1980;
offMS = 4210;
on_pulse = 7;
off_pulse = 22;
lp5560_mode_set(LED_FLASH);
break;
case 64:
onMS = 1016;
offMS = 2032;
on_pulse = 7;
off_pulse = 13;
lp5560_mode_set(LED_FLASH);
break;
default:
lp5560_mode_set(LED_TURN_ON);
break;
printk("set led error\n");
/* if (onMS == 0)
lp5560_mode_set(LED_TURN_OFF);
else if (offMS == 0)
lp5560_mode_set(LED_TURN_ON);
else {
for (i = 0; i < MAX_CUST_TIME; i++) {
if (onMS <= delay_onms[i]) {
on_pulse = i;
break;
}
}
for (i = 0; i < MAX_CUST_TIME; i++) {
if (offMS <= delay_offms[i]) {
off_pulse = i;
break;
}
}
lp5560_mode_set(LED_FLASH);
}
break;
*/
}
}
static struct led_classdev lp5560_white_led = {
.name = "white",
.default_trigger = "ide-disk",
.brightness_set = lp5560_led_white_set,
};
static int lp5560_led_probe(struct platform_device *pdev)
{
int ret;
ret = led_classdev_register(&pdev->dev, &lp5560_white_led);
if (ret < 0)
led_classdev_unregister(&lp5560_white_led);
ret = sysfs_create_group(&lp5560_white_led.dev->kobj,
&lp5560_led_attribute_group);
if (ret) {
led_classdev_unregister(&lp5560_white_led);
return -1;
}
/* GPIO init */
gpio_request(LED_CTRL_GPIO, "led-ctrl");
gpio_direction_output(LED_CTRL_GPIO, 0);
gpio_set_value(LED_CTRL_GPIO, 0);
#ifdef USE_HRTIMER
hrtimer_init(&lp5560_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
lp5560_hrtimer.function=lp5560_callback;
#endif
return ret;
}
static int lp5560_led_remove(struct platform_device *pdev)
{
#ifdef USE_HRTIMER
hrtimer_cancel(&lp5560_hrtimer);
#endif
sysfs_remove_group(&lp5560_white_led.dev->kobj, &lp5560_led_attribute_group);
led_classdev_unregister(&lp5560_white_led);
return 0;
}
static struct platform_driver lp5560_led_driver= {
.probe = lp5560_led_probe,
.remove = lp5560_led_remove,
.driver = {
.name = "leds-lp5560",
.owner = THIS_MODULE,
},
};
static struct platform_device led_pdev = {
.name= "leds-lp5560",
.id= -1,
};
MODULE_ALIAS("platform:leds-blade");
static int __init gpio_led_init(void)
{
platform_device_register(&led_pdev);
return platform_driver_register(&lp5560_led_driver);
}
static void __exit gpio_led_exit(void)
{
platform_device_unregister(&led_pdev);
platform_driver_unregister(&lp5560_led_driver);
}
module_init(gpio_led_init);
module_exit(gpio_led_exit);
MODULE_AUTHOR("LENOVO");
MODULE_DESCRIPTION("LED driver for LP5560 controllers");
MODULE_LICENSE("GPL");