/*********************************************************************************
|
* Copyright: (C) 2017 LingYun IoT Studio <www.iot-yun.com>
|
* All rights reserved.
|
*
|
* Filename: s3c_led.c
|
* Description: This file
|
*
|
* Version: 1.0.0(07/26/2012~)
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "07/26/2012 10:03:40 PM"
|
*
|
********************************************************************************/
|
|
#include <linux/module.h> /* Every Linux kernel module must include this head */
|
#include <linux/init.h> /* Every Linux kernel module must include this head */
|
#include <linux/kernel.h> /* printk() */
|
#include <linux/fs.h> /* struct fops */
|
#include <linux/errno.h> /* error codes */
|
#include <linux/cdev.h> /* cdev_alloc() */
|
#include <asm/io.h> /* ioremap() */
|
#include <linux/ioport.h> /* request_mem_region() */
|
|
#include <asm/ioctl.h> /* Linux kernel space head file for macro _IO() to generate ioctl command */
|
#include <linux/printk.h> /* Define log level KERN_DEBUG */
|
|
#define LED_NUM 4
|
|
#define DISABLE 0
|
#define ENABLE 1
|
|
#define GPIO_INPUT 0x00
|
#define GPIO_OUTPUT 0x01
|
|
|
#define PLATDRV_MAGIC 0x60
|
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
|
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
|
|
#define S3C_GPB_BASE 0x56000010
|
#define S3C_GPB_LEN 0x10 /* 0x56000010~0x56000020 */
|
#define GPBCON_OFFSET 0
|
#define GPBDAT_OFFSET 4
|
#define GPBUP_OFFSET 8
|
|
static void __iomem *gpbbase = NULL;
|
|
#define read_reg32(addr) *(volatile unsigned int *)(addr)
|
#define write_reg32(addr, val) *(volatile unsigned int *)(addr) = (val)
|
|
int led[LED_NUM] = {5,6,8,10}; /* Four LEDs use GPB5,GPB6,GPB8,GPB10 */
|
int dev_count = ARRAY_SIZE(led);
|
|
int dev_major = 0;
|
int dev_minor = 0;
|
int debug = DISABLE;
|
|
static struct cdev *led_cdev;
|
|
|
static int s3c_hw_init(void)
|
{
|
int i;
|
unsigned int regval;
|
|
if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led"))
|
{
|
printk(KERN_ERR "request_mem_region failure!\n");
|
return -EBUSY;
|
}
|
|
if( !(gpbbase=(unsigned int *)ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )
|
{
|
release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);
|
printk(KERN_ERR "release_mem_region failure!\n");
|
return -ENOMEM;
|
}
|
|
|
for(i=0; i<dev_count; i++)
|
{
|
/* Set GPBCON register, set correspond GPIO port as input or output mode */
|
regval = read_reg32(gpbbase+GPBCON_OFFSET);
|
regval &= ~(0x3<<(2*led[i])); /* Clear the currespond LED GPIO configure register */
|
regval |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode */
|
write_reg32(gpbbase+GPBCON_OFFSET, regval);
|
|
/* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable */
|
regval = read_reg32(gpbbase+GPBUP_OFFSET);
|
regval |= (0x1<<led[i]); /* Disable pull up resister */
|
write_reg32(gpbbase+GPBUP_OFFSET, regval);
|
|
/* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
|
regval = read_reg32(gpbbase+GPBDAT_OFFSET);
|
regval |= (0x1<<led[i]); /* This port set to high level, then turn LED off */
|
write_reg32(gpbbase+GPBDAT_OFFSET, regval);
|
}
|
|
return 0;
|
}
|
|
|
static void turn_led(int which, unsigned int cmd)
|
{
|
unsigned int regval;
|
|
regval = read_reg32(gpbbase+GPBDAT_OFFSET);
|
|
if(LED_ON == cmd)
|
{
|
regval &= ~(0x1<<led[which]); /* Turn LED On */
|
}
|
else if(LED_OFF == cmd)
|
{
|
regval |= (0x1<<led[which]); /* Turn LED off */
|
}
|
|
write_reg32(gpbbase+GPBDAT_OFFSET, regval);
|
}
|
|
static void s3c_hw_term(void)
|
{
|
int i;
|
unsigned int regval;
|
|
for(i=0; i<dev_count; i++)
|
{
|
regval = read_reg32(gpbbase+GPBDAT_OFFSET);
|
regval |= (0x1<<led[i]); /* Turn LED off */
|
write_reg32(gpbbase+GPBDAT_OFFSET, regval);
|
}
|
|
release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);
|
iounmap(gpbbase);
|
}
|
|
|
static int led_open(struct inode *inode, struct file *file)
|
{
|
int minor = iminor(inode);
|
|
file->private_data = (void *)minor;
|
|
printk(KERN_DEBUG "/dev/led%d opened.\n", minor);
|
return 0;
|
}
|
|
static int led_release(struct inode *inode, struct file *file)
|
{
|
printk(KERN_DEBUG "/dev/led%d closed.\n", iminor(inode));
|
|
return 0;
|
}
|
|
static void print_help(void)
|
{
|
printk("Follow is the ioctl() commands for led driver:\n");
|
printk("Turn LED on command : %u\n", LED_ON);
|
printk("Turn LED off command : %u\n", LED_OFF);
|
|
return;
|
}
|
|
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
{
|
int which = (int)file->private_data;
|
|
switch (cmd)
|
{
|
case LED_ON:
|
|
turn_led(which, LED_ON);
|
break;
|
|
case LED_OFF:
|
turn_led(which, LED_OFF);
|
break;
|
|
default:
|
printk(KERN_ERR "LED driver don't support ioctl command=%d\n", cmd);
|
print_help();
|
break;
|
}
|
|
return 0;
|
}
|
|
|
static struct file_operations led_fops =
|
{
|
.owner = THIS_MODULE,
|
.open = led_open,
|
.release = led_release,
|
.unlocked_ioctl = led_ioctl,
|
};
|
|
static int __init s3c_led_init(void)
|
{
|
int result;
|
dev_t devno;
|
|
if( 0 != s3c_hw_init() )
|
{
|
printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
|
return -ENODEV;
|
}
|
|
/* Alloc the device for driver */
|
if (0 != dev_major) /* Static */
|
{
|
devno = MKDEV(dev_major, 0);
|
result = register_chrdev_region (devno, dev_count, "led");
|
}
|
else
|
{
|
result = alloc_chrdev_region(&devno, dev_minor, dev_count, "led");
|
dev_major = MAJOR(devno);
|
}
|
|
/* Alloc for device major failure */
|
if (result < 0)
|
{
|
printk(KERN_ERR "S3C led driver can't use major %d\n", dev_major);
|
return -ENODEV;
|
}
|
printk(KERN_DEBUG "S3C led driver use major %d\n", dev_major);
|
|
if(NULL == (led_cdev=cdev_alloc()) )
|
{
|
printk(KERN_ERR "S3C led driver can't alloc for the cdev.\n");
|
unregister_chrdev_region(devno, dev_count);
|
return -ENOMEM;
|
}
|
|
led_cdev->owner = THIS_MODULE;
|
cdev_init(led_cdev, &led_fops);
|
|
result = cdev_add(led_cdev, devno, dev_count);
|
if (0 != result)
|
{
|
printk(KERN_INFO "S3C led driver can't reigster cdev: result=%d\n", result);
|
goto ERROR;
|
}
|
|
|
printk(KERN_ERR "S3C led driver[major=%d] version 1.0.0 installed successfully!\n", dev_major);
|
return 0;
|
|
|
ERROR:
|
printk(KERN_ERR "S3C led driver installed failure.\n");
|
cdev_del(led_cdev);
|
unregister_chrdev_region(devno, dev_count);
|
return result;
|
}
|
|
static void __exit s3c_led_exit(void)
|
{
|
dev_t devno = MKDEV(dev_major, dev_minor);
|
|
s3c_hw_term();
|
|
cdev_del(led_cdev);
|
unregister_chrdev_region(devno, dev_count);
|
|
printk(KERN_ERR "S3C led driver version 1.0.0 removed!\n");
|
|
return ;
|
}
|
|
|
/* These two functions defined in <linux/init.h> */
|
module_init(s3c_led_init);
|
module_exit(s3c_led_exit);
|
|
module_param(debug, int, S_IRUGO);
|
module_param(dev_major, int, S_IRUGO);
|
|
MODULE_AUTHOR("guowenxue@LingYun IoT Studio");
|
MODULE_DESCRIPTION("FL2440 LED linux lowlevel API driver");
|
MODULE_LICENSE("GPL");
|