⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxa2xx_i2c_misc.c

📁 基于linux xcale平台linux系统下I2c驱动程序
💻 C
字号:
/*
	created by hzh, a simple i2c driver, don't support slave and multi-master mode
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/pm.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/interrupt.h>

//#include <asm/semaphore.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>

#define I2C_MINOR		18

#define I2C_SLAVE		0x0703	/* Change slave address			*/
#define	I2C_BUS_MODE	0x0780

struct i2c_inf {
	wait_queue_head_t	int_wait;
	volatile int		bus_error;
	volatile int		tx_done;
	volatile int		rx_done;
	int					saddr;
	int					use_count;
	spinlock_t			lock;
} pxa2xx_i2c;

static __inline int _i2c_wr(struct i2c_inf *i2c, char *buf, int count, int stop)
{
	int i, flag;
	unsigned long save_flags;
//	DECLARE_WAITQUEUE(wait, current);
//	unsigned long timeout;

	for(i=0; i<count; i++) {
		i2c->bus_error = 0;
		i2c->tx_done   = 0;
		i2c->rx_done   = 0;
		ISR = 0x7f0;	//clear interrupt source
		flag = ICR & ~(ICR_MA | ICR_START | ICR_STOP | ICR_ACKNAK);
		if(i==0)
			flag |= ICR_START;
		if((i+1)==count&&stop)
			flag |= ICR_STOP;
		IDBR = buf[i];
		ICR = ICR_BEIE | ICR_ITEIE | ICR_TB | flag;
		save_and_cli(save_flags);
		if(i2c->tx_done==0) {
			interruptible_sleep_on_timeout(&i2c->int_wait, 2);
		/*	add_wait_queue(&i2c->int_wait, &wait);
			set_current_state(TASK_INTERRUPTIBLE);
			if(signal_pending(current))
				set_current_state(TASK_RUNNING);
			else
				timeout = schedule_timeout(2);
			remove_wait_queue(&i2c->int_wait, &wait);*/
		}
		restore_flags(save_flags);
		ICR &= ~(ICR_BEIE | ICR_ITEIE | ICR_START | ICR_STOP | ICR_ACKNAK);
		if(!i2c->tx_done||signal_pending(current)) {
			ICR |= ICR_MA;
			break;
		}
	}
//printk("wr %d bytes\n", i);
	return i;
}

static __inline int _i2c_rd(struct i2c_inf *i2c, char *buf, int count)
{
	int i, flag;
	unsigned long save_flags;

	for(i=0; i<count; i++) {
		i2c->bus_error = 0;
		i2c->tx_done   = 0;
		i2c->rx_done   = 0;
		ISR = 0x7f0;	//clear interrupt source
		flag = ICR & ~(ICR_MA | ICR_START | ICR_STOP | ICR_ACKNAK);
		if((i+1)==count)
			flag |= ICR_STOP | ICR_ACKNAK;
		ICR = ICR_BEIE | ICR_IRFIE | ICR_TB | flag;
		save_and_cli(save_flags);
		if(i2c->rx_done==0) {
			interruptible_sleep_on_timeout(&i2c->int_wait, 2);
		}
		restore_flags(save_flags);
		ICR &= ~(ICR_BEIE | ICR_IRFIE | ICR_START | ICR_STOP | ICR_ACKNAK);
		if(!i2c->rx_done||signal_pending(current)) {
			ICR |= ICR_MA;
			break;
		}
		buf[i] = IDBR;
	}

	return i;
}

static ssize_t pxa2xx_i2c_write(struct file *filp, const char *buf, size_t count, loff_t *l)
{
	char data[256];	//max 255
	struct i2c_inf *i2c = (struct i2c_inf *)filp->private_data;

	if(count>sizeof(data))
		return -EINVAL;
	if(!count)
		return 0;
	if(copy_from_user(data+1, buf, count))
			return -EFAULT;
	
	data[0] = i2c->saddr<<1;
	if(_i2c_wr(i2c, data, count+1, 1)==count+1)
		return count;
	return -ENXIO;
}

static ssize_t pxa2xx_i2c_read(struct file *filp, char *buf, size_t count, loff_t *l)
{
	u8 data[256];	//max 255
	struct i2c_inf *i2c = (struct i2c_inf *)filp->private_data;

	if(count>sizeof(data))
		return -EINVAL;
	if(!count)
		return 0;

	data[0] = (i2c->saddr<<1) | 1;
	if(_i2c_wr(i2c, data, 1, 0)!=1)
		return -ENXIO;
	if(_i2c_rd(i2c, data+1, count)!=count)
		return -ENXIO;
	if(copy_to_user(buf, data+1, count))
		return -EFAULT;

	return count;
}

static int pxa2xx_i2c_ioctl(struct inode * inode, struct file * filp,
				unsigned int cmd, unsigned long arg)
{
	struct i2c_inf *i2c = (struct i2c_inf *)filp->private_data;

	switch(cmd) {
	case I2C_SLAVE:
		i2c->saddr = arg;
		break;
	case I2C_BUS_MODE:
		if(arg)
			ICR |= 1<<15;
		else
			ICR &= ~(1<<15);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int pxa2xx_i2c_open(struct inode * inode, struct file * filp)
{
	struct i2c_inf *i2c;
	int minor = MINOR(inode->i_rdev);

	if (minor != I2C_MINOR)
		return -ENODEV;

	spin_lock_irq(&pxa2xx_i2c.lock);
	if(pxa2xx_i2c.use_count) {
		spin_unlock_irq(&pxa2xx_i2c.lock);
		return -EBUSY;
	}
	pxa2xx_i2c.use_count++;
	CKEN |= CKEN14_I2C;
	spin_unlock_irq(&pxa2xx_i2c.lock);

	filp->private_data = i2c = &pxa2xx_i2c;

	ICR = ICR_UR;
	schedule_timeout(HZ/50);
	ICR = ICR_IUE|ICR_SCLE;

	i2c->bus_error = i2c->tx_done = i2c->rx_done = 0;

	return 0;
}

static int pxa2xx_i2c_release(struct inode * inode, struct file * filp)
{
	struct i2c_inf *i2c = (struct i2c_inf *)filp->private_data;

	if (MINOR(inode->i_rdev) != I2C_MINOR)
		return -ENXIO;

	spin_lock_irq(&i2c->lock);
	i2c->use_count--;
	if(!i2c->use_count)
		CKEN &= ~CKEN14_I2C;
	spin_unlock_irq(&pxa2xx_i2c.lock);

	return 0;
}

static struct file_operations i2c_fops = {
	owner:		THIS_MODULE,
	write:		pxa2xx_i2c_write,
	read:		pxa2xx_i2c_read,
//	poll:		pxa2xx_i2c_poll,
	ioctl:		pxa2xx_i2c_ioctl,
	open:		pxa2xx_i2c_open,
	release:	pxa2xx_i2c_release,
};

static struct miscdevice i2c_misc = { 
	minor:	I2C_MINOR,
	name:	"i2c",
	fops:	&i2c_fops,
};

static irqreturn_t pxa2xx_i2c_irq(int irq, void *dev_id, struct pt_regs *regs)
{
	int status, wake=0;
	struct i2c_inf *i2c = dev_id;

	status = ISR;
//printk("S:%x,M:%x\n", status, ICR);
	if(status&ISR_BED) {
		ISR |= ISR_BED;
		i2c->bus_error = 1;
		wake = 1;
	}
	if(status&ISR_ITE) {
		ISR |= ISR_ITE;
		i2c->tx_done = 1;
		wake = 1;
	}
	if(status&ISR_IRF) {
		ISR |= ISR_IRF;
		i2c->rx_done = 1;
		wake = 1;
	}

	if(wake)
		wake_up(&i2c->int_wait);		
	else
		printk("unexpected irq, isr %x, icr %x\n", status, ICR);
	
	return IRQ_HANDLED;
}

static int pxa2xx_i2c_probe(struct device *dev)
{
	printk(KERN_INFO "PXA2XX IIC Controller Simple Driver.\n");
	
	/* register our misc device */
	if (misc_register(&i2c_misc) < 0) {
		printk(KERN_ERR "can't register misc device");
		return -EBUSY;
	}

	init_waitqueue_head(&pxa2xx_i2c.int_wait);
	spin_lock_init(&pxa2xx_i2c.lock);
	pxa_gpio_mode(GPIO117_I2CSCL_MD);
	pxa_gpio_mode(GPIO118_I2CSDA_MD);
	if(request_irq(IRQ_I2C, pxa2xx_i2c_irq, SA_INTERRUPT, "PXA-I2C", &pxa2xx_i2c)) {
		printk("fail to request irq for i2c device!\n");
		misc_deregister(&i2c_misc);
		return -EBUSY;
	}

	return 0;
}

static int  pxa2xx_i2c_remove(struct device *dev)
{
	misc_deregister(&i2c_misc);
	free_irq(IRQ_I2C, &pxa2xx_i2c);

	return 0;
}

/*
 * Suspend & Resume the IIC Controller.
 */
static int pxa2xx_i2c_suspend(struct device * dev,  u32 state, u32 level)
{
   switch(level){
   case SUSPEND_POWER_DOWN:
	break;
   }
   
   return 0;
}

static int pxa2xx_i2c_resume(struct device * dev, u32 level)
{
   switch(level){
   case RESUME_POWER_ON:
	break;
   } 

   return 0;
}

static struct device_driver pxa2xx_i2c_driver = {
	.name		=	"pxa2xx-i2c",
	.probe		=	pxa2xx_i2c_probe,
	.remove		=   pxa2xx_i2c_remove,	
	.bus		=	&platform_bus_type,	
	.suspend 	=	pxa2xx_i2c_suspend,
	.resume 	=   pxa2xx_i2c_resume,
};

static struct platform_device *pxa2xx_i2c_device = NULL;

static int __devinit pxa2xx_i2c_init(void)
{
	pxa2xx_i2c_device = platform_device_register_simple("pxa2xx-i2c", -1, NULL, 0);
	return driver_register(&pxa2xx_i2c_driver);
}

static void __exit pxa2xx_i2c_exit(void)
{
	driver_unregister(&pxa2xx_i2c_driver);
	platform_device_unregister(pxa2xx_i2c_device);
}


module_init(pxa2xx_i2c_init);
module_exit(pxa2xx_i2c_exit);

MODULE_AUTHOR("antiscle <hzh12@tom.com>");
MODULE_DESCRIPTION("pxa2xx iic simple driver");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -