/* * linux/derivers/led/led-edukit-s3c2410.c * led driver for Embest EduKit II * Copyright (C) 2005 Embest < */ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h> // error codes
#include <linux/types.h> // size_t
#include <linux/delay.h> // mdelay
#include <linux/proc_fs.h> #include <asm/uaccess.h> // to copy to/from userspace
#include <asm/hardware.h> #include <asm/arch-s3c2410/S3C2410.h> #include <asm/leds.h>
#ifdef LED_DEBUG // if want to debug, define it while compile this function
#define DEBUG(str, args...) printk("led: " str, ## args) #else #define DEBUG(str, args...) #endif
#undef u32 #define u32 unsigned
static int nLedMajor = 0; /* allow to alloc the major number for led*/ #define LED_DEVNAME "led"
#define GPF_MASK (0xF<<4) /* GPF4-7: LED1-LED4 */ #define GET_DATA(f) ((u8)(~f>>4)) #define SET_DATA(t, f) ( f = (~t&0x0F)<<4 )
#define LED_LOCK(u) down(&u->lock); #define LED_UNLOCK(u) up(&u->lock);
#define GPF_CTL_BASE io_p2v(0x56000050)/* get the virtual address map to GPF */ #define S3C2410_GPFCON (GPF_CTL_BASE + 0x0) #define S3C2410_GPFDAT (GPF_CTL_BASE + 0x4) #define S3C2410_GPFUP (GPF_CTL_BASE + 0x8)
unsigned long flags; static char *version = "Embest EdukitII-2410 led driver version 1.0 (2005-06-18) <\n";
struct unit { struct semaphore lock; u32 *GPF_CON; /* GPFCON register */ u32 *GPF_DAT; /* GPFDAT register */ u32 *GPF_UP; /* GPFUP register */ u32 f; /* LEDs value (bit4-7:LED1-LED4)*/ }; static struct unit led_unit = { .GPF_CON = (u32 *)S3C2410_GPFCON, .GPF_DAT = (u32 *)S3C2410_GPFDAT, .GPF_UP = (u32 *)S3C2410_GPFUP, .f = 0x00 // turn on all LEDs
};
#ifdef CONFIG_PROC_FS static int led_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *p = page; int len; struct unit led_u; len = led_u.f; p += sprintf(p, "%s\n LED1: %s LED2: %s LED3: %s LED4: %s \n", version, len&(1<<4)?"ON":"OFF", len&(1<<5)?"ON":"OFF", len&(1<<6)?"ON":"OFF", len&(1<<7)?"ON":"OFF" );
len = (p - page) - off; if (len < 0) len = 0;
*eof = (len <= count) ? 1 : 0; *start = page + off;
return len; } #endif
static void led_set_value(struct unit *unit, u8 val) { u32 temp;
SET_DATA(val, unit->f); temp = *unit->GPF_DAT; temp &= ~GPF_MASK; temp |= unit->f; *unit->GPF_DAT = temp; DEBUG("write to GPFDAT:0x%x.\n", temp); } static u8 led_get_value(struct unit *unit) { u8 temp = GET_DATA(unit->f);
return temp; }
static int led_open(struct inode *inode, struct file *file) { DEBUG("open\n");
leds_event(led_stop); file->private_data = &led_unit; MOD_INC_USE_COUNT;
return 0; }
static int led_release_f(struct inode *inode, struct file *file) { DEBUG("release\n"); MOD_DEC_USE_COUNT;
leds_event(led_start); return 0; }
static ssize_t led_read(struct file *file, char *buf, size_t count, loff_t *offset) { u8 temp; int ret; struct unit *unit = (struct unit *)file->private_data;
DEBUG("read\n"); if(count > 1) count = 1; LED_LOCK(unit); temp = led_get_value(unit); DEBUG("Read from unit value field:0x%x.\n", temp); ret = copy_to_user(buf, &temp, count) ? -EFAULT : count; LED_UNLOCK(unit);
return ret; }
static ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *offset) { u8 temp; int ret; char *tmp; struct unit *unit = (struct unit *)file->private_data;
DEBUG("write\n"); if(count > 1) count = 1; LED_LOCK(unit); ret = copy_from_user(&temp, buf, count) ? -EFAULT : count; DEBUG("led0 writing %d bytes:0x%x.\n", ret,temp); if(ret) led_set_value(unit, temp); LED_UNLOCK(unit);
return ret; }
static struct file_operations led_ops = { owner: THIS_MODULE, read: led_read, write: led_write, open: led_open, release: led_release_f, };
/* * led device init */ static void __init led_init(struct unit *unit) { u32 temp;
/* init device lock */ init_MUTEX(&unit->lock);
/* init io port */ temp = *unit->GPF_CON; temp &= ~(0xF<<4); temp |= ((1<<14) | (1<<12) | (1<<10) | (1<<8));// GPF4,5,6,7 as Output
*unit->GPF_CON = temp;
temp = *unit->GPF_UP; temp |= (0xF<<8);// pull up all GPF4,5,6,7
*unit->GPF_UP = temp; /* init data and turn on led, bit0-3:LED1-4*/ led_set_value(unit, 0x0f);
/* delay some time */ mdelay(100);
/* turn off led */ led_set_value(unit, 0x00); }
/* * module init */ static devfs_handle_t devfs_handle,devfs_led_dir; #ifdef MODULE int init_module(void) #else int __init led_init_module(void) #endif { int res;
DEBUG("init_module\n"); /* print version information */ printk(KERN_INFO "%s", version);
/* register led device, character device: /proc/devices*/ #ifdef CONFIG_DEVFS_FS res = devfs_register_chrdev(0, LED_DEVNAME, &led_ops); if(res < 0) { printk("led-edukit-s3c2410.o: unable to get major for led device.\n"); return res; }
/* create the devfs: /driver/led/0 */ devfs_led_dir = devfs_mk_dir(NULL, LED_DEVNAME, NULL); devfs_handle = devfs_register(devfs_led_dir, "0", DEVFS_FL_DEFAULT, res, 0,S_IFCHR | S_IRUSR | S_IWUSR,&led_ops, NULL); #else res = register_chrdev(nLedMajor, LED_DEVNAME, &led_ops); #endif
/* add a profile to /proc/driver/led */ #ifdef CONFIG_PROC_FS create_proc_read_entry ("driver/led", 0, 0, led_proc, NULL); #endif
/* get the major number */ if( nLedMajor == 0 ) { nLedMajor = res; printk("Led major number = %d\n",nLedMajor); }
/* then call led_init() */ led_init(&led_unit);
return 0; }
/* * module cleanup */ #ifdef MODULE void cleanup_module(void) #else void __exit led_cleanup(void) #endif { int res;
DEBUG("cleanup\n"); /* unregister led device */ res = unregister_chrdev(nLedMajor, LED_DEVNAME); if(res < 0) printk("led-edukit-s3c2410.o: unable to release major %d for led device.\n", nLedMajor);
#ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_handle); devfs_unregister(devfs_led_dir); #endif
/* remove the profile /proc/driver/led */ #ifdef CONFIG_PROC_FS remove_proc_entry("driver/led", NULL); #endif }
#ifndef MODULE /* declare a module init entry */ module_init(led_init_module); /* declare a module exit entry */ module_exit(led_cleanup); #endif
MODULE_DESCRIPTION("EduKitII-2410 led driver"); MODULE_AUTHOR("Embest tech&info Co.,Ltd. <"); MODULE_LICENSE("GPL");
|