📄 i2c.c
字号:
#define IN_I2C
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
//#include <linux/malloc.h> /* kmalloc() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include <asm/io.h>
#include "i2c.h"
#define I2C_MAJOR_NR 122
#define MAJOR_NR major
#define DEVICE_NAME "I2C"
#define i2cStart() outl(I2CON_STA, I2CONSET)
#define i2cStop() outl(I2CON_STO, I2CONSET)
#define i2c_enInt() outl(1 << IRQ_I2C, VICIntEnable)
#define i2c_disInt() outl(1 << IRQ_I2C, VICIntEnClr)
static int i2c_open(struct inode *inode, struct file *filp);
static int i2c_release(struct inode *inode, struct file *filp);
static ssize_t i2c_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static ssize_t i2c_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);
static void i2c_irq_handle(int irq, void *dev_id, struct pt_regs *regs);
int i2c_init(void);
void i2c_cleanup(void);
module_init(i2c_init);
module_exit(i2c_cleanup);
MODULE_PARM(major, "i");
MODULE_LICENSE("Proprietary");
MODULE_DESCRIPTION("");
MODULE_SUPPORTED_DEVICE("uClinux2.4.x LPC2200 i2c");
MODULE_AUTHOR("WuWengang");
static int major = I2C_MAJOR_NR;
static u16 i2c_opentimes = 0;
static u32 pinsel0Save;
static u8 i2c_addr;
static volatile size_t i2c_count;
static size_t i2c_max;
static u8 *i2c_buf;
static struct semaphore i2c_devSem;
static struct semaphore i2c_intSem;
static struct file_operations i2c_fops =
{
owner: THIS_MODULE,
#if 0
llseek: i2c_llseek,
#endif
read: i2c_read,
write: i2c_write,
ioctl: i2c_ioctl,
open: i2c_open,
release: i2c_release,
};
int i2c_init(void)
{
int result;
result = register_chrdev(MAJOR_NR, DEVICE_NAME, &i2c_fops);
if(result < 0)
{
printk(KERN_ERR DEVICE_NAME ":Unable to get major %d):\n", MAJOR_NR);
return result;
}
if(0 == MAJOR_NR)
{
MAJOR_NR = result;
}
printk(KERN_INFO DEVICE_NAME "wwg's i2c driver:init OK(: \n");
return 0;
}
void i2c_cleanup(void)
{
unregister_chrdev(MAJOR_NR, DEVICE_NAME);
}
static int i2c_open(struct inode *inode, struct file *filp)
{
unsigned long flag;
local_irq_save(flag);
if(0 == i2c_opentimes++)
{
request_irq(IRQ_I2C, i2c_irq_handle, SA_INTERRUPT, DEVICE_NAME, NULL);
sema_init(&i2c_devSem, 1);
pinsel0Save = (inl(PINSEL0) & 0x000000f0);
outl((inl(PINSEL0)&(~0x000000f0))|0x00000050, PINSEL0);
outl(0x2c, I2CONCLR);
outl(I2CON_I2EN, I2CONSET);
}
local_irq_restore(flag);
filp->private_data = (void *)MINOR(inode->i_rdev);
MOD_INC_USE_COUNT;
printk(KERN_INFO "open i2c address(%d)\n", (u32)(filp->private_data));
return 0;
}
static int i2c_release(struct inode *inode, struct file *filp)
{
unsigned long flag;
MOD_DEC_USE_COUNT;
local_irq_save(flag);
if(0 == --i2c_opentimes)
{
outl((inl(PINSEL0)&(~0x000000f0))|pinsel0Save, PINSEL0);
free_irq(IRQ_I2C, NULL);
}
local_irq_restore(flag);
return 0;
}
static ssize_t i2c_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
ssize_t retVal;
if(!access_ok(VERIFY_READ, (void *)buf, count))
{
return -EFAULT;
}
down_interruptible(&i2c_devSem);
i2c_buf = (void *)buf;
i2c_addr = ((u32)filp->private_data) & 0xfe;
i2c_max = count;
i2c_count = 0;
sema_init(&i2c_intSem, 0);
i2c_enInt();
i2cStart();
if(down_interruptible(&i2c_intSem))
{
up(&i2c_devSem);
return -ERESTARTSYS;
}
retVal = i2c_count;
up(&i2c_devSem);
return retVal;
}
static ssize_t i2c_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
ssize_t retVal;
if(!access_ok(VERIFY_WRITE, (void *)buf, count))
{
return -EFAULT;
}
down_interruptible(&i2c_devSem);
i2c_buf = buf;
i2c_addr = ((u32)filp->private_data) | 0x01;
i2c_max = count;
i2c_count = 0;
sema_init(&i2c_intSem, 0);
i2c_enInt();
i2cStart();
if(down_interruptible(&i2c_intSem))
{
return -ERESTARTSYS;
}
retVal = i2c_count;
up(&i2c_devSem);
return retVal;
}
static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
if (arg < 4)
{
arg = 4;
}
switch(cmd)
{
case I2C_SET_CLH:
outl(arg, I2SCLH);
break;
case I2C_SET_CLL:
outl(arg, I2SCLL);
break;
default:
return -ENOTTY;
}
return 0;
}
static void i2c_irq_handle(int irq, void *dev_id, struct pt_regs *regs)
{
u8 data;
data = inl(I2STAT);
switch(data)
{
case 0x08: /* 已发送起始条件,与0x18相同处理 */
//break;
case 0x10: /* 已发送重复起始条件 */
outl(i2c_addr, I2DAT);
outl(I2CON_STA, I2CONCLR);
break;
case 0x18: /* 已发送SLA+W,并已接收应答 */
case 0x28: /* 已发送I2C数据,并接收到应答 */
if(i2c_count < i2c_max)
{
get_user(data, &i2c_buf[i2c_count++]);
outl(data, I2DAT);
break;
}
case 0x20: /* 已发送SLA+W;已接收非ACK, 与0x48处理相同 */
case 0x30: /* 已发送I2DAT中的数据字节;已接收非ACK, 与0x48处理相同 */
case 0x48: /* 已发送SLA+R;已接收非ACK */
i2cStop();
case 0x38: /* 在SLA+R/W或数据字节中丢失仲裁 */
i2c_disInt();
outl(I2CON_STA, I2CONCLR);
up(&i2c_intSem);
break;
case 0x40: /* 已发送SLA+R;已接收ACK */
if(i2c_max > 1)
{
outl(I2CON_AA, I2CONSET);
}
else
{
outl(I2CON_AA, I2CONCLR);
}
break;
case 0x50: /* 已接收数据字节;已发送ACK */
data = inl(I2DAT);
put_user(data, &i2c_buf[i2c_count++]);
if(i2c_count < (i2c_max - 1))
{
outl(I2CON_AA, I2CONSET);
}
else
{
outl(I2CON_AA, I2CONCLR);
}
break;
case 0x58: /* 已接收数据字节;已发送非ACK */
data = inl(I2DAT);
put_user(data, &i2c_buf[i2c_count++]);
i2cStop();
i2c_disInt();
up(&i2c_intSem);
break;
default:
break;
}
outl(I2CON_SI, I2CONCLR);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -