/* * 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"); |