📄 8255a.c
字号:
/* * 8255a GPIO interface for Linux on Atmel AT91RM9200 * Copyright (c) 2006 Ligang Wang * wangzitan@163.com *//* include some file */ #include <linux/module.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/string.h>#include <linux/poll.h>#include <linux/init.h>#include <asm/io.h>#include <asm/system.h>#include <asm/arch/hardware.h>#include <asm/segment.h>#include "8255a.h"/* define some macro for this module*/#define MAX_DEV_NUM 5 //Max number of 8255 device#define DEV_MEM_SIZE 1024 //define memory size for device#define DEV_MAJOR 252 //major of 8255a//NCS2:8255A0 NCS4:8255A1 NCS5:8255A2//NCS6:8255A3 NCS7:8055A4static unsigned long REG_ADDR_BASE[MAX_DEV_NUM] = {0x30000000,0x50000000,0x60000000,0x70000000,0x80000000};#define PA_REG_V(i) (dev_list[(i)].dev_base_addr_v + 0)#define PB_REG_V(i) (dev_list[(i)].dev_base_addr_v + 1)#define PC_REG_V(i) (dev_list[(i)].dev_base_addr_v + 2)#define CTL_REG_V(i) (dev_list[(i)].dev_base_addr_v + 3)#define CURRENT_PORT_REG (dev_list[current_dev].dev_base_addr_v + dev_list[current_dev].current_port)#define CURRENT_CTL_REG (dev_list[current_dev].dev_base_addr_v + 3)typedef struct device_8255a{ char dev_num; //device id int current_port; //current port char port_state[3]; //state of portA/B/C unsigned long dev_base_addr_v; //base virtual address of device}dev_8255a;static dev_8255a dev_list[MAX_DEV_NUM];static int current_dev;static int set_port(int dev, int port, int io_mode);static int select_port(int dev, int port);AT91PS_SYS AT91_SYS1 = (AT91PS_SYS) AT91C_VA_BASE_SYS;/* * Read funcation for vfs */ssize_t at91_8255A_read(struct file * file, char *buf, size_t count, loff_t * ppos){ int retval; char data; //printk("8255: Read fun\n"); // check current port of current device is input mode if(dev_list[current_dev].port_state[dev_list[current_dev].current_port] != PARM_INPUT) return -EINVAL; // check count arg if (count < sizeof(unsigned char)) return -EINVAL; // read data from 8255a writeb(0x9b,CURRENT_CTL_REG); data = readb(CURRENT_PORT_REG); // copy data to user space retval = copy_to_user(buf,&(data),sizeof(unsigned char)); if (!retval) retval = sizeof(unsigned char); return retval;}/* * Write funcation for vfs */ssize_t at91_8255A_write(struct file * file, const char * buf, size_t count, loff_t * ppos){ int retval; char data; //check current port of current device is input mode if(dev_list[current_dev].port_state[dev_list[current_dev].current_port] != PARM_OUTPUT) return -EINVAL; //copy data from user to kernel retval = copy_from_user(&(data),buf,sizeof(unsigned char)); if (!retval) { //write data to 8255a //printk("8255a:write %d to %x\n",data,CURRENT_PORT_REG); outb(data,CURRENT_PORT_REG); retval = sizeof(unsigned char); } return retval;} /* open a device */static int at91_8255A_open(struct inode* inode,struct file* file){ MOD_INC_USE_COUNT;
return 0; }/* * Handle commands from user-space. */static int at91_825A_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int arg_data = 0; switch(cmd) { //handle select dev and port command case CMD_SELECT_DEV_PORT: copy_from_user(&arg_data,(int *)arg,sizeof(int)); select_port(get_dev(arg_data),get_port(arg_data)); break; //get current dev and port case CMD_GET_CURRENT_DEV_PORT: arg_data |= ((current_dev<<16) | (dev_list[current_dev].current_port << 8)); copy_to_user(&arg_data,(int *)arg,sizeof(int)); break; //set port state case CMD_SET_PORT_STATE: copy_from_user(&arg_data,(int *)arg,sizeof(int)); set_port(get_dev(arg_data), get_port(arg_data), get_io_mod(arg_data)); break; //get poart state case CMD_GET_PORT_STATE: copy_from_user(&arg_data,(int *)arg,sizeof(int)); return (dev_list[get_dev(arg_data)].port_state[get_port(arg_data)]); default: return -EINVAL; } return 0; }/* * set port i/o mode * dev: the dev we will setting * port:the port we will setting * io_mode: i/o mode */ static int set_port(int dev, int port, int io_mode){ char ctl_word; if(dev < 0 || dev > MAX_DEV_NUM || port < 0 || port > 2 || io_mode<0 || io_mode>1) return -1; dev_list[dev].port_state[port] = io_mode; ctl_word = (1<<7) | (dev_list[dev].port_state[PORT_A] << 4) | (dev_list[dev].port_state[PORT_C] << 3) | (dev_list[dev].port_state[PORT_B] << 1) | (dev_list[dev].port_state[PORT_C] << 0); //outb(ctl_word,CTL_REG_V(dev)); //printk("8255a: write control word to %x\n",CTL_REG_V(dev)); //printk("8255a: dev:%d,port:%d,io:%d,control word:%x\n",dev,port,io_mode,ctl_word); return 0;}/* * select a port as current port * char dev: current dev * char port:current port */static int select_port(int dev, int port){ if(dev<0 || dev>MAX_DEV_NUM || port<0 || port>2) return -1; current_dev = dev; dev_list[dev].current_port = port; //printk("8255a:current dev is %d,port is :%d\n",dev,port); return 0;}/* * setup 8255A hardware */ static void at91_8255A_setup(void){ int i; //Setup bus access time width. AT91_SYS1->EBI_SMC2_CSR[2] = (AT91C_SMC2_NWS & 0x8) | AT91C_SMC2_WSEN
| (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; AT91_SYS1->EBI_SMC2_CSR[4] = (AT91C_SMC2_NWS & 0x8) | AT91C_SMC2_WSEN
| (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; AT91_SYS1->EBI_SMC2_CSR[5] = (AT91C_SMC2_NWS & 0x8) | AT91C_SMC2_WSEN
| (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; AT91_SYS1->EBI_SMC2_CSR[6] = (AT91C_SMC2_NWS & 0x8) | AT91C_SMC2_WSEN
| (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; AT91_SYS1->EBI_SMC2_CSR[7] = (AT91C_SMC2_NWS & 0x8) | AT91C_SMC2_WSEN
| (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; //Initialize private data foe 8255a devices //all port of 8255a is input mode //and current 8255a device is 8255a 0,current port is port A; for(i = 0; i<MAX_DEV_NUM; i++) { dev_list[i].dev_num = i; dev_list[i].current_port = PORT_A; set_port(i,PORT_A,PARM_INPUT); set_port(i,PORT_B,PARM_INPUT); set_port(i,PORT_C,PARM_INPUT); } //Set 8255a0 as output mode set_port(DEV8255A0,PORT_A,PARM_OUTPUT); set_port(DEV8255A0,PORT_B,PARM_OUTPUT); set_port(DEV8255A0,PORT_C,PARM_OUTPUT); /**/ outb(0x9b,CTL_REG_V(1)); outb(0x9b,CTL_REG_V(2)); outb(0x9b,CTL_REG_V(3)); outb(0x9b,CTL_REG_V(4)); outb(0x80,CTL_REG_V(0)); current_dev = DEV8255A0; return;}static int at91_8255A_release(struct inode* inode,struct file* file){ MOD_DEC_USE_COUNT; return 0;}struct file_operations at91_8255A_fops = { owner: THIS_MODULE,
write: at91_8255A_write,
read: at91_8255A_read, open: at91_8255A_open, ioctl: at91_825A_ioctl, release: at91_8255A_release,};/* * Initialize and install 8255A driver */static int __init at91_8255A_init(void){ int i; printk("8255A: at91_8255A_init has been called\n"); //remap memory for 8255A*5 for(i = 0; i<MAX_DEV_NUM; i++) { dev_list[i].dev_base_addr_v = (unsigned long)(ioremap(REG_ADDR_BASE[i], DEV_MEM_SIZE)); if (check_region(dev_list[i].dev_base_addr_v, DEV_MEM_SIZE)<0)
{ printk("8255A: Cannot map memory:%08x, memory already in use\n", (int)REG_ADDR_BASE[i]); return -1; } else { printk("8255A: Memory remap:%x => %x\n",REG_ADDR_BASE[i],dev_list[i].dev_base_addr_v); } request_region(dev_list[i].dev_base_addr_v, DEV_MEM_SIZE,"8255a"); } //setup 8255a,initialize hardware at91_8255A_setup(); //register device if(register_chrdev(DEV_MAJOR,"8255A",&at91_8255A_fops))
{ printk("8255A: Can not register 8255A driver \n");
return -1;
}
else { printk("8255A: Registerd the 8255A driver success\n"); } printk("8255A: the 8255A is working now\n"); return 0;}/* * Disable and remove the 8255A driver */static void __exit at91_8255A_exit(void){ printk("8255A: at91_8255A_exit has been called\n"); unregister_chrdev(DEV_MAJOR,"8255A"); return;}module_init(at91_8255A_init);module_exit(at91_8255A_exit);MODULE_AUTHOR("Ligang Wang");MODULE_DESCRIPTION("AT91 rm9200 8255a driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -