📄 pxa2xx_i2c_misc.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 + -