/*********************************************************************************
|
* Copyright: (C) 2017 LingYun IoT Studio <www.iot-yun.com>
|
* All rights reserved.
|
*
|
* Filename: platdrv_led.c
|
* Description: This is the common LED driver runs on S3C24XX.
|
*
|
* Version: 1.0.0(10/27/2011~)
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "10/27/2011 11:39:10 AM"
|
*
|
********************************************************************************/
|
|
#include <linux/module.h>
|
#include <linux/init.h>
|
#include <linux/kernel.h>
|
#include <linux/device.h>
|
#include <linux/fs.h>
|
#include <linux/cdev.h>
|
#include <linux/version.h>
|
|
#include "platdev_led.h"
|
|
#define TIMER_TIMEOUT 40
|
|
#define PLATDRV_MAGIC 0x60
|
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
|
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
|
#define LED_BLINK _IO (PLATDRV_MAGIC, 0x20)
|
|
static int dev_major = 0;
|
|
struct led_device
|
{
|
struct s3c_led_platform_data *data;
|
struct cdev cdev;
|
struct class *dev_class;
|
struct timer_list blink_timer;
|
} led_device;
|
|
|
void led_timer_handler(unsigned long data)
|
{
|
int i;
|
struct s3c_led_platform_data *pdata = (struct s3c_led_platform_data *)data;
|
|
for(i=0; i<pdata->nleds; i++)
|
{
|
if(ON == pdata->leds[i].status)
|
{
|
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
|
}
|
else
|
{
|
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
|
}
|
|
if(ENABLE == pdata->leds[i].blink ) /* LED should blink */
|
{
|
/* Switch status between 0 and 1 to turn LED ON or off */
|
pdata->leds[i].status = pdata->leds[i].status ^ 0x01;
|
}
|
|
mod_timer(&(led_device.blink_timer), jiffies + TIMER_TIMEOUT);
|
}
|
}
|
|
|
static int led_open(struct inode *inode, struct file *file)
|
{
|
struct led_device *pdev ;
|
struct s3c_led_platform_data *pdata;
|
|
pdev = container_of(inode->i_cdev,struct led_device, cdev);
|
pdata = pdev->data;
|
|
file->private_data = pdata;
|
|
return 0;
|
}
|
|
|
static int led_release(struct inode *inode, struct file *file)
|
{
|
return 0;
|
}
|
|
static void print_led_help(void)
|
{
|
printk("Follow is the ioctl() command for LED driver:\n");
|
printk("Turn LED on command : %u\n", LED_ON);
|
printk("Turn LED off command : %u\n", LED_OFF);
|
printk("Turn LED blink command : %u\n", LED_BLINK);
|
}
|
|
/* compatible with kernel version >=2.6.38*/
|
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
{
|
struct s3c_led_platform_data *pdata = file->private_data;
|
|
switch (cmd)
|
{
|
case LED_OFF:
|
if(pdata->nleds <= arg)
|
{
|
printk("LED%ld doesn't exist\n", arg);
|
return -ENOTTY;
|
}
|
pdata->leds[arg].status = OFF;
|
pdata->leds[arg].blink = DISABLE;
|
break;
|
|
case LED_ON:
|
if(pdata->nleds <= arg)
|
{
|
printk("LED%ld doesn't exist\n", arg);
|
return -ENOTTY;
|
}
|
pdata->leds[arg].status = ON;
|
pdata->leds[arg].blink = DISABLE;
|
break;
|
|
case LED_BLINK:
|
if(pdata->nleds <= arg)
|
{
|
printk("LED%ld doesn't exist\n", arg);
|
return -ENOTTY;
|
}
|
pdata->leds[arg].blink = ENABLE;
|
pdata->leds[arg].status = ON;
|
break;
|
|
default:
|
printk("LED driver don't support ioctl command=%d\n", cmd);
|
print_led_help();
|
return -EINVAL;
|
|
}
|
return 0;
|
}
|
|
|
static struct file_operations led_fops = {
|
.owner = THIS_MODULE,
|
.open = led_open,
|
.release = led_release,
|
.unlocked_ioctl = led_ioctl, /* compatible with kernel version >=2.6.38*/
|
};
|
|
|
static int s3c_led_probe(struct platform_device *dev)
|
{
|
struct s3c_led_platform_data *pdata = dev->dev.platform_data;
|
int result = 0;
|
int i;
|
dev_t devno;
|
|
/* Initialize the LED status */
|
for(i=0; i<pdata->nleds; i++)
|
{
|
s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT);
|
if(ON == pdata->leds[i].status)
|
{
|
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
|
}
|
else
|
{
|
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
|
}
|
}
|
|
/* Alloc the device for driver */
|
if (0 != dev_major)
|
{
|
devno = MKDEV(dev_major, 0);
|
result = register_chrdev_region(devno, 1, "led");
|
}
|
else
|
{
|
result = alloc_chrdev_region(&devno, 0, 1, "led");
|
dev_major = MAJOR(devno);
|
}
|
|
/* Alloc for device major failure */
|
if (result < 0)
|
{
|
printk("LED driver can't get major %d\n", dev_major);
|
return result;
|
}
|
|
/* Initialize button structure and register cdev*/
|
memset(&led_device, 0, sizeof(led_device));
|
led_device.data = dev->dev.platform_data;
|
cdev_init (&(led_device.cdev), &led_fops);
|
led_device.cdev.owner = THIS_MODULE;
|
|
result = cdev_add (&(led_device.cdev), devno , 1);
|
if (result)
|
{
|
printk (KERN_NOTICE "error %d add led device", result );
|
goto ERROR;
|
}
|
|
led_device.dev_class = class_create(THIS_MODULE, "led");
|
if(IS_ERR(led_device.dev_class))
|
{
|
printk("LED driver create class failure\n");
|
result = -ENOMEM;
|
goto ERROR;
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
|
device_create(led_device.dev_class, NULL, devno, NULL, "led");
|
#else
|
device_create (led_device.dev_class, NULL, devno, "led");
|
#endif
|
|
/* Initial the LED blink timer */
|
init_timer(&(led_device.blink_timer));
|
led_device.blink_timer.function = led_timer_handler;
|
led_device.blink_timer.data = (unsigned long)pdata;
|
led_device.blink_timer.expires = jiffies + TIMER_TIMEOUT;
|
add_timer(&(led_device.blink_timer));
|
|
printk("S3C led driver version 1.0.0 initiliazed.\n");
|
|
return 0;
|
|
|
ERROR:
|
printk("S3C led driver version 1.0.0 install failure.\n");
|
cdev_del(&(led_device.cdev));
|
|
unregister_chrdev_region(devno, 1);
|
return result;
|
|
}
|
|
static int s3c_led_remove(struct platform_device *dev)
|
{
|
dev_t devno = MKDEV(dev_major, 0);
|
|
del_timer(&(led_device.blink_timer));
|
|
cdev_del(&(led_device.cdev));
|
device_destroy(led_device.dev_class, devno);
|
class_destroy(led_device.dev_class);
|
|
unregister_chrdev_region(devno, 1);
|
printk("S3C led driver removed\n" );
|
|
return 0;
|
}
|
|
|
static struct platform_driver s3c_led_driver = {
|
.probe = s3c_led_probe,
|
.remove = s3c_led_remove,
|
.driver = {
|
.name = "s3c_led",
|
.owner = THIS_MODULE,
|
},
|
};
|
|
|
static int __init platdrv_led_init(void)
|
{
|
int rv = 0;
|
|
rv = platform_driver_register(&s3c_led_driver);
|
if(rv)
|
{
|
printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, rv);
|
return rv;
|
}
|
printk("Regist S3C LED Platform Driver successfully.\n");
|
|
return 0;
|
}
|
|
|
static void platdrv_led_exit(void)
|
{
|
printk("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);
|
platform_driver_unregister(&s3c_led_driver);
|
}
|
|
module_init(platdrv_led_init);
|
module_exit(platdrv_led_exit);
|
|
module_param(dev_major, int, S_IRUGO);
|
|
MODULE_AUTHOR("GuoWenxue<guowenxue@gmail.com>");
|
MODULE_DESCRIPTION("FL2440 LED driver platform driver");
|
MODULE_LICENSE("GPL");
|
MODULE_ALIAS("platform:s3c_platdrv_led");
|