📄 xup_v2pro_dip.c
字号:
/*
File: xup_v2pro_dip.c
Description: DIP driver for Xilinx XUP Virtex-II Pro board
Author:
Date:
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/devfs_fs_kernel.h>
//#include <linux/spinlock.h>
#include <linux/proc_fs.h>
//#include <linux/delay.h>
#define XUP_V2PRO_DIP_DEVICENAME "xup_v2pro_dip"
#define XUP_V2PRO_DIP_REG_BASE_ADDR 0x40020000
#define DIP_CHANNEL1_DATA_REG (*(volatile unsigned long *)(XUP_V2PRO_DIP_REG_BASE_ADDR+0x00))
#define DIP_CHANNEL1_3STATE_REG (*(volatile unsigned long *)(XUP_V2PRO_DIP_REG_BASE_ADDR+0x04))
#ifdef DIP_DUAL_CHANNEL
#define DIP_CHANNEL2_DATA_REG (*(volatile unsigned long *)(XUP_V2PRO_DIP_REG_BASE_ADDR+0x08))
#define DIP_CHANNEL2_3STATE_REG (*(volatile unsigned long *)(XUP_V2PRO_DIP_REG_BASE_ADDR+0x0C))
#endif
#define DEBUG
#ifdef DEBUG
#define DIP_DEBUG(fmt,args...) printk("%s: " fmt, __FUNCTION__, ## args)
#else
#define DIP_DEBUG(fmt,args...)
#endif
// #define XUP_V2PRO_DIP_SET_TIMEOUT _IOWR('L', 0x80, unsigned long)
#define XUP_V2PRO_DIP_START_TIMER _IOWR('L', 0x81, unsigned long)
#define XUP_V2PRO_DIP_STOP_TIMER _IOWR('L', 0x82, unsigned long)
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_xup_v2pro_dip = NULL;
#endif
#define DIP_TIMER_INTERVALS 1 //500 // 0.5s
// dip timer flag
#define DIP_TIMER_FLAG_ON 0x80
#define DIP_TIMER_FLAG_OFF 0x81
static struct timer_list dip_timer;
static int dip_timer_flag = 0;
static spinlock_t dip_timer_lock;
static struct proc_dir_entry * dip_proc_dir_entry = NULL;
static int xup_v2pro_dip_major;
#if 0
static void xup_v2pro_dip_set_on( int bit )
{
volatile unsigned long data;
data = DIP_CHANNEL1_3STATE_REG;
data = data & ( ~( 1<< (bit-1) ) );
DIP_CHANNEL1_3STATE_REG = data;
DIP_DEBUG( "data=0x%x,bit=0x%x\n", data, bit );
}
static void xup_v2pro_dip_set_off( int bit )
{
volatile unsigned long data;
data = DIP_CHANNEL1_3STATE_REG;
data = data | ( 1 << bit );
DIP_CHANNEL1_3STATE_REG = data;
DIP_DEBUG( "data=0x%x,bit=0x%x\n", data, bit );
}
#endif
static void xup_v2pro_dip_timer_routine( void * pbit )
{
unsigned long expire;
volatile unsigned long dip_data;
unsigned long bit;
DIP_DEBUG( "Entering\n" );
expire = jiffies + (DIP_TIMER_INTERVALS) * HZ ;/// 1000;
// read current value
dip_data = DIP_CHANNEL1_3STATE_REG;
bit = ( unsigned long)pbit;
DIP_DEBUG( "1. bit=0x%x, pbit=0x%x,dip_data=0x%x\n", bit, (unsigned long)pbit, dip_data );
if( bit == 0 ){
dip_data = ~(dip_data & 0xf );
}
else{
if( ( dip_data & ( 1 << (bit-1) ) ) == 0 ){
DIP_DEBUG( "2. bit=0x%x, dip_data=0x%x\n", bit, dip_data );
dip_data = dip_data | ( 1 << (bit-1) );
}
else{
DIP_DEBUG( "3. bit=0x%x, dip_data=0x%x\n", bit, dip_data );
dip_data = dip_data & ( 0xf - ( 1 << ( bit-1 ) ) ); //( 0 << (bit-1) );
}
}
DIP_DEBUG( "4. after modified: dip_data=0x%x\n", dip_data );
DIP_CHANNEL1_3STATE_REG = dip_data;
mod_timer( &dip_timer, expire );
DIP_DEBUG( "Leaving\n" );
}
static void xup_v2pro_dip_timer_init( int bit )
{
DIP_DEBUG( "Entering\n" );
if( bit == 0 ){
DIP_CHANNEL1_3STATE_REG = 0xf;
}
init_timer(&dip_timer);
dip_timer.function = xup_v2pro_dip_timer_routine;
dip_timer.data = bit;
dip_timer.expires = jiffies + HZ * DIP_TIMER_INTERVALS; // / 1000;
add_timer( &dip_timer );
DIP_DEBUG( "Leaving\n" );
}
static void xup_v2pro_dip_timer_exit( void )
{
del_timer(&dip_timer);
}
static void xup_v2pro_dip_hardware_init( void )
{
// set all dip to turn off
DIP_CHANNEL1_3STATE_REG = (volatile unsigned long)0x0;
}
static int xup_v2pro_dip_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
DIP_DEBUG( "Entering\n" );
switch( cmd ){
case XUP_V2PRO_DIP_START_TIMER:
DIP_DEBUG( "Starting a new timer\n" );
spin_lock( &dip_timer_lock );
// if timer had already started, stop it, and restart a new timer
if( dip_timer_flag == DIP_TIMER_FLAG_ON ){
xup_v2pro_dip_timer_exit( );
// dip_timer_flag = DIP_TIMER_FLAG_OFF;
}
xup_v2pro_dip_timer_init( arg );
dip_timer_flag = DIP_TIMER_FLAG_ON;
spin_unlock( &dip_timer_lock );
// xup_v2pro_dip_timer_init(arg);
break;
case XUP_V2PRO_DIP_STOP_TIMER:
DIP_DEBUG( "Stoping timer\n" );
spin_lock( &dip_timer_lock );
if( dip_timer_flag == DIP_TIMER_FLAG_ON ){
xup_v2pro_dip_timer_exit( );
dip_timer_flag = DIP_TIMER_FLAG_OFF;
}
spin_unlock( &dip_timer_lock );
break;
}
DIP_DEBUG( "Leaving\n" );
return 0;
}
static ssize_t xup_v2pro_dip_read(struct file *filp, char *buf, size_t count, loff_t * f_pos)
{
buf[0] = (char) (0xf-(DIP_CHANNEL1_3STATE_REG & 0xf));
DIP_DEBUG( "buffer:0x%x,0x%x\n", buf[0], buf[1] );
DIP_DEBUG( "register value:0x%x\n", DIP_CHANNEL1_3STATE_REG );
return 1;
}
static ssize_t xup_v2pro_dip_write(struct file *filp, char *buf, size_t count, loff_t * f_pos)
{
#if 0
int i;
DIP_CHANNEL1_3STATE_REG = (volatile unsigned long)(0xF);
for( i=0; i<1000; i++ );
#endif
spin_lock( &dip_timer_lock );
if( dip_timer_flag == DIP_TIMER_FLAG_ON ){
xup_v2pro_dip_timer_exit( );
dip_timer_flag = DIP_TIMER_FLAG_OFF;
}
spin_unlock( &dip_timer_lock );
DIP_CHANNEL1_3STATE_REG = (volatile unsigned long)(buf[0]);
DIP_DEBUG( "buffer:0x%x, count=0x%x\n", buf[0], count );
DIP_DEBUG( "register 3-state:0x%x, data:0x%x\n", DIP_CHANNEL1_3STATE_REG, DIP_CHANNEL1_DATA_REG );
return 1;
}
static int xup_v2pro_dip_open( struct inode * inode, struct file * file )
{
MOD_INC_USE_COUNT;
return 0;
}
static int xup_v2pro_dip_close( struct inode * inode, struct file * file )
{
MOD_DEC_USE_COUNT;
return 0;
}
static struct file_operations xup_v2pro_dip_fops = {
owner: THIS_MODULE,
ioctl: xup_v2pro_dip_ioctl,
read: xup_v2pro_dip_read,
write: xup_v2pro_dip_write,
open: xup_v2pro_dip_open,
release: xup_v2pro_dip_close,
};
static int __init xup_v2pro_dip_init( void )
{
xup_v2pro_dip_major = register_chrdev( 0, XUP_V2PRO_DIP_DEVICENAME, &xup_v2pro_dip_fops );
if( xup_v2pro_dip_major < 0 ){
printk( "register faidip!\n" );
return -1;
}
#ifdef CONFIG_DEVFS_FS
devfs_xup_v2pro_dip = devfs_register(NULL, XUP_V2PRO_DIP_DEVICENAME, DEVFS_FL_DEFAULT,
xup_v2pro_dip_major, 1, S_IFCHR | S_IRUGO | S_IWUSR,
&xup_v2pro_dip_fops, NULL);
#endif
dip_proc_dir_entry = create_proc_entry( XUP_V2PRO_DIP_DEVICENAME, 0, NULL );
if( dip_proc_dir_entry != NULL )
dip_proc_dir_entry->proc_fops = &xup_v2pro_dip_fops;
xup_v2pro_dip_hardware_init( );
spin_lock( &dip_timer_lock );
dip_timer_flag = DIP_TIMER_FLAG_OFF;
spin_unlock( &dip_timer_lock );
return 0;
}
static void __exit xup_v2pro_dip_exit( void )
{
spin_lock( &dip_timer_lock );
if( dip_timer_flag == DIP_TIMER_FLAG_ON ){
xup_v2pro_dip_timer_exit( );
dip_timer_flag = DIP_TIMER_FLAG_OFF;
}
spin_unlock( &dip_timer_lock );
remove_proc_entry( XUP_V2PRO_DIP_DEVICENAME, NULL );
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_xup_v2pro_dip);
#endif
unregister_chrdev( xup_v2pro_dip_major, XUP_V2PRO_DIP_DEVICENAME );
}
//EXPORT_NO_SYMBOLS;
/* EXPORTED FUNCTIONS */
//EXPORT_SYMBOL(xup_v2pro_dip_set_on);
//EXPORT_SYMBOL(xup_v2pro_dip_set_off);
MODULE_AUTHOR("Cactus Software Labs.");
MODULE_LICENSE("GPL");
module_init(xup_v2pro_dip_init);
module_exit(xup_v2pro_dip_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -