一 步骤:
根据上两篇分析,总结下写iic bus 驱动的步骤 :
1.probe:
软件方面:
分配,设置,注册 i2c_adapter结构体。
初始化一些辅助变量。
硬件方面:
获得和使能时钟
获得和映射相关寄存器
初始化iic总线控制器
注册中断
2.实现iic 数据传输的算法
master_xfer
二 代码
由于前面两篇博客分析过,现在直接贴代码:
#include <linux/kernel.h>
#include <linux/module.h>
//#define DEBUG 1 // 打开调试log
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <plat/regs-iic.h>
#include <plat/iic.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
static struct clk *clk;
static struct resource *ioarea;
static spinlock_t iic_bus_lock;
static wait_queue_head_t iic_bus_wait;
struct s5pv210_i2c_xfer_data{
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int byte_ptr;
int state;
};
struct s5pv210_i2c_xfer_data s5pv210_i2c_xfer_data;
struct s5pv210_i2c_regs {
unsigned int iiccon;
unsigned int iicstat;
unsigned int iicadd;
unsigned int iicds;
unsigned int iiclc;
};
/* i2c controller state */
enum s5pv210_i2c_state {
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
};
static struct s5pv210_i2c_regs *s5pv210_i2c_regs;
static inline void s5pv210_i2c_stop(int ret)
{
printk("neo: STOP\n");
/* stop the transfer */
s5pv210_i2c_regs->iicstat &= ~(1<<5) ;
/* 重新设置状态 */
s5pv210_i2c_xfer_data.state = STATE_STOP;
s5pv210_i2c_xfer_data.byte_ptr = 0;
s5pv210_i2c_xfer_data.msg = NULL;
s5pv210_i2c_xfer_data.msg_idx++;
s5pv210_i2c_xfer_data.msg_num = 0;
if (ret)
s5pv210_i2c_xfer_data.msg_idx = ret;
printk("neo: master_complete %d\n", ret);
/* 唤醒应用程序 */
wake_up(&iic_bus_wait);
/* disable irq */
s5pv210_i2c_regs->iiccon &= ~((1<<5));
}
static void s5pv210_i2c_message_start(struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1; // 获得地址
printk("neo: START\n");
// Master receive mode
if (msg->flags & I2C_M_RD)
{
addr= (addr | 1);
s5pv210_i2c_regs->iicstat = 0x90;
}
else// Master transmit mode
{
s5pv210_i2c_regs->iicstat = 0xd0;
}
s5pv210_i2c_regs->iicds = addr;
printk("neo: iicadd = 0x%08x,iiccon = 0x%08x,iicstat =0x%08x,%s,%d\n" ,s5pv210_i2c_regs->iicds ,s5pv210_i2c_regs->iiccon,s5pv210_i2c_regs->iicstat,__FUNCTION__, __LINE__ );
ndelay(50); // 为了使地址能传到 sda线上
/* 重新启动传输 这么写是因为iic 发出stop后会清楚中断 ack使能 所以必须重新启动iic总线
* 另外iiccon 的bit4也必须注意,在中断退出时已经清了中断,此时不需要重新设置该位,否则就会出错
*/
s5pv210_i2c_regs->iiccon |= (1<<7) | (0<<6) | (1<<5) | (11-1);
s5pv210_i2c_regs->iicstat |= (1<<5);
printk("neo: iicadd = 0x%08x,iiccon = 0x%08x,iicstat =0x%08x,%s,%d\n" ,s5pv210_i2c_regs->iicds ,s5pv210_i2c_regs->iiccon,s5pv210_i2c_regs->iicstat,__FUNCTION__, __LINE__ );
}
static inline int is_lastmsg(void)
{
return s5pv210_i2c_xfer_data.msg_idx >= (s5pv210_i2c_xfer_data.msg_num - 1);
}
static inline int is_byteend(void)
{
return s5pv210_i2c_xfer_data.byte_ptr >= s5pv210_i2c_xfer_data.msg->len;
}
static inline int is_bytelast(void)
{
return s5pv210_i2c_xfer_data.byte_ptr == s5pv210_i2c_xfer_data.msg->len-1;
}
static irqreturn_t s5pv210_i2c_irq(int irqno, void *dev_id)
{
printk("neo: state%d\n" ,s5pv210_i2c_xfer_data.state );
printk("neo: %s enter,%d\n" ,__FUNCTION__, __LINE__ );
if (s5pv210_i2c_regs->iicstat & S3C2410_IICSTAT_ARBITR)
{
printk("neo:deal with arbitration loss\n");
return -ENODATA ;
}
switch (s5pv210_i2c_xfer_data.state)
{
printk("neo: state%d\n" ,s5pv210_i2c_xfer_data.state );
case STATE_IDLE:
printk("%s: called in STATE_IDLE\n", __func__);
return ENODEV;
case STATE_START:
/* 没有ack 返回 错误 */
printk("neo: STATE_START \n" );
if (s5pv210_i2c_regs->iicstat & S3C2410_IICSTAT_LASTBIT)
{
printk("neo:ack was not received\n");
s5pv210_i2c_stop(-ENXIO);
break;
}
/* just i2c probe to find devices. */
if (is_lastmsg() && s5pv210_i2c_xfer_data.msg->len == 0)
{
printk("neo:ack was received\n");
s5pv210_i2c_stop(0);
break;
}
/* 下一个状态 */
if (s5pv210_i2c_xfer_data.msg->flags & I2C_M_RD)
s5pv210_i2c_xfer_data.state = STATE_READ;
else
s5pv210_i2c_xfer_data.state = STATE_WRITE;
if (s5pv210_i2c_xfer_data.state == STATE_READ)
goto prepare_read;
case STATE_WRITE:
printk("neo: WRITE START\n");
/* 如果这个消息中还有数据,那么就把该数据写入iic 总线 */
if (!is_byteend())
{
printk("neo: WRITE: Next Byte\n");
s5pv210_i2c_regs->iicds = s5pv210_i2c_xfer_data.msg->buf[s5pv210_i2c_xfer_data.byte_ptr++];
ndelay(50);
break;
}
/* 还有消息 */
else if (!is_lastmsg())
{
printk("neo: WRITE: Next Message\n");
s5pv210_i2c_xfer_data.byte_ptr = 0;
s5pv210_i2c_xfer_data.msg_idx++;
s5pv210_i2c_xfer_data.msg++; // 下个 msg
s5pv210_i2c_message_start(s5pv210_i2c_xfer_data.msg);
s5pv210_i2c_xfer_data.state = STATE_START;
break;
}
/* 没有消息且消息中没有数据,则停止 */
else
{
printk("neo: WRITE: NO Message NO Byte\n");
s5pv210_i2c_stop(0);
break;
}
case STATE_READ:
// 读出数据
s5pv210_i2c_xfer_data.msg->buf[s5pv210_i2c_xfer_data.byte_ptr++] = s5pv210_i2c_regs->iicds;
// 第一次start时 发完地址后并没有数据需要读取
prepare_read:
/* 消息的最后一个字节 */
if (is_bytelast())
{
printk("neo: READ: Last Byte\n");
/* 并且是最后一个消息 此时不发送ack*/
if (is_lastmsg())
printk("neo: READ: Last Message\n");
s5pv210_i2c_regs->iiccon &= ~(1<<7);
}
/* 否则该消息中没有数据了 */
else if (is_byteend())
{
printk("neo: READ: NO Byte\n");
/* 并且是最后一个消息 此时停止*/
if (is_lastmsg())
{
printk("neo: READ: Last Message NO Byte\n");
printk("neo: READ: Send Stop\n");
s5pv210_i2c_stop(0);
}
/* 并且此时还有消息 */
else
{
printk("neo: READ: Next Transfer\n");
s5pv210_i2c_xfer_data.byte_ptr = 0;
s5pv210_i2c_xfer_data.msg_idx++;
s5pv210_i2c_xfer_data.msg++;
}
}
break;
}
// 清中断
s5pv210_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND);
return IRQ_HANDLED;
}
static int s5pv210_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
static int cnt=0;
int ret,timeout;
printk("neo: s5pv210_i2c_xfer cnt = %d\n" , ++cnt);
spin_lock_irq(&iic_bus_lock);
/*初始化 msg */
/* 应用程序会调用算法函数,并且会发来几个msg,驱动需要将这些msg读进来 或者发出去*/
s5pv210_i2c_xfer_data.msg = msgs;
s5pv210_i2c_xfer_data.msg_num = num;
s5pv210_i2c_xfer_data.byte_ptr = 0;
s5pv210_i2c_xfer_data.msg_idx = 0;
s5pv210_i2c_xfer_data.state = STATE_START;
s5pv210_i2c_message_start(msgs);
spin_unlock_irq(&iic_bus_lock);
timeout = wait_event_timeout(iic_bus_wait, s5pv210_i2c_xfer_data.msg_num == 0, HZ * 5);
ret = s5pv210_i2c_xfer_data.msg_idx ;
if (timeout == 0)
{
dev_dbg(NULL, "neo:timeout\n");
return -ETIMEDOUT;
}
else if (ret != num)
dev_dbg(NULL, "neo:incomplete xfer \n");
else
dev_dbg(NULL, "neo:complete xfer \n");
/* ensure the stop has been through the bus */
udelay(10);
return ret;
}
static u32 s5pv210_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
static const struct i2c_algorithm s5pv210_i2c_algo = {
.master_xfer = s5pv210_i2c_xfer,
.functionality = s5pv210_i2c_func,
};
/* 1. 分配/设置i2c_adapter*/
static struct i2c_adapter s5v210_i2c_adapter = {
.name = "s5pv210_i2c",
.algo = &s5pv210_i2c_algo,
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
};
static void s5pv210_i2c_init(void)
{
/* 3.3.1 配置iic相关gpio */
// 将gpd0 gpd1 两个gpio 配置为 scl sda ,并且不需要上拉使能
s3c_gpio_cfgall_range(S5PV210_GPD1(0), 2, S3C_GPIO_SFN(2), S3C_GPIO_PULL_NONE);
/* 3.3.2 初始化iic相关寄存器 */
/* bit[7] = 1, 使能ACK
* bit[6] = 0, IICCLK = PCLK/16
* bit[5] = 1, 使能中断
* bit[3:0] = (11-1), Tx clock = IICCLK/16
* PCLK = 66700kHz, IICCLK = 4168kHz, Tx Clock = 378khz
*/
s5pv210_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (11-1);
s5pv210_i2c_regs->iicadd= 0x10;
s5pv210_i2c_regs->iiclc = (3 << 0) | (1<<2) ;
s5pv210_i2c_regs->iicstat = (1<<4); // I2C串行输出使能(Rx/Tx)
}
static int neo_i2c_bus_s5pv210_init(void)
{
int ret ;
/*2. 辅助变量操作 */
spin_lock_init(&iic_bus_lock);
init_waitqueue_head(&iic_bus_wait);
/* 3. 相关硬件操作 */
/* 3.1 使能相关时钟 */
//clk = clk_get(NULL, "i2c");
clk = clk_get(NULL, "i2c");
printk("neo: clock source %p\n", clk);
clk_enable(clk);
/* 3.2 映射相关寄存器 */
// ioarea = request_mem_region(0xE1800000,sizeof(struct s5pv210_i2c_regs), "s5pv210-i2c");
s5pv210_i2c_regs = ioremap(0xE1800000 , sizeof(struct s5pv210_i2c_regs)); // 这里只实现 iic控制器 0 的驱动
printk( "neo:registers %p \n",s5pv210_i2c_regs);
/* 3.3 初始化iic */
s5pv210_i2c_init();
/* 3.4 注册中断 */
ret = request_irq(IRQ_IIC, s5pv210_i2c_irq, IRQF_DISABLED,"s5pv210-iic", NULL);
if(ret < 0)
{
dev_err(NULL, "cannot claim IRQ %d\n", IRQ_IIC);
free_irq(IRQ_IIC, NULL);
}
/* 4 注册i2c_adapter */
i2c_add_numbered_adapter(&s5v210_i2c_adapter);
printk("neo: init over\n");
return 0 ;
}
static void neo_i2c_bus_s5pv210_exit(void)
{
i2c_del_adapter(&s5v210_i2c_adapter);
free_irq(IRQ_IIC, NULL);
iounmap(s5pv210_i2c_regs);
clk_disable(clk);
clk_put(clk);
}
module_init(neo_i2c_bus_s5pv210_init);
module_exit(neo_i2c_bus_s5pv210_exit);
MODULE_LICENSE("GPL");
三 调试
1.ioremap 相关寄存器之后无法写进寄存器。
一开始做实验时出现中断无法进入,后来就打印寄存器的值,发现都是0(即是reset value)。 后来就百度了下,发现百度有人说寄存器值写不进去可能跟没有获得clk 有关,我当时就纳闷了,检查了一遍代码 clk = clk_get(NULL, "i2c"); clk_enable(clk); 尼玛没问题啊??? 后来就硬着头皮加了句 printk("neo: clock source %p\n", clk);果然,打印出来的值为0xfffffffe ,一看就有问题,后来就跟进去看了下clk_get的源码:
查到了原因:
原来clk_get函数是定义在arch\arm\plat-samsung\Clock.c中的,这个文件应该是三星自己添加的,和内核发布的不一样,当clk_get 的第一个参数是NULL时会返回-1,这样就会得到错误的时钟源,此时呢我就将计就计,将//idno = -1; 改为idno = 0;这样就能获得正确的时钟源了!当获得正确的时钟源之后,寄存器就能正常读写,此时就可以进入中断了。
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p;
struct clk *clk = ERR_PTR(-ENOENT);
int idno;
if (dev == NULL || !dev_is_platform_device(dev))
//idno = -1;
idno = 0; // 这边改下 改为 0
else
idno = to_platform_device(dev)->id;
spin_lock(&clocks_lock);
list_for_each_entry(p, &clocks, list) {
if (p->id == idno &&
strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;
break;
}
}
/* check for the case where a device was supplied, but the
* clock that was being searched for is not device specific */
if (IS_ERR(clk)) {
list_for_each_entry(p, &clocks, list) {
if (p->id == -1 && strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;
break;
}
}
}
spin_unlock(&clocks_lock);
return clk;
}
2. ./i2c_usr_test /dev/i2c/0 0x50 w 0 0x11 和 ./i2c_usr_test /dev/i2c/0 0x50 r 0
用应用程序写0地址的值,再读0 地址的值,两个值不一样:
利用内核自带的I2c-dev.c 的驱动测试, 先向at24cxx的0地址写一个字节的数据,再从0地址读出一个字节,发现读出来的值有误,通过log 看到iic发出读命令时只发出了一个start信号,不应该啊,应该有两次才对,通过内核自带的i2c-s3c2410.c 打印出第二次start信号下为 iicds = 0x000000a1,iiccon = 0x000000ba,iicstat =0x000000b0,很明显iiccon 控制器需要在s5pv210_i2c_message_start函数中再重新设置一下,因为在s5pv210_i2c_stop中会disable ack和传输,所以必须重新设置。另外中断位由于在中断传输结束后会清0,所以在s5pv210_i2c_message_start中设置iiccon时就不用重新设置中断位了,否则会出错。
3. 最后附上打印log
[root@FriendlyARM /]# ./i2c_usr_test /dev/i2c/0 0x50 w 0 0x11
[ 30.517035] neo: s5pv210_i2c_xfer cnt = 32
[ 30.517078] neo: START
[ 30.517105] neo: iicadd = 0x000000a0,iiccon = 0x0000000a,iicstat =0x000000d1,s5pv210_i2c_message_start,105
[ 30.517186] neo: iicadd = 0x000000a0,iiccon = 0x000000aa,iicstat =0x000000f1,s5pv210_i2c_message_start,110
[ 30.520517] neo: state1
[ 30.522940] neo: s5pv210_i2c_irq enter,132
[ 30.527013] neo: STATE_START
[ 30.529959] neo: WRITE START
[ 30.532818] neo: WRITE: Next Byte
[ 30.536138] neo: state3
[ 30.538539] neo: s5pv210_i2c_irq enter,132
[ 30.542610] neo: WRITE START
[ 30.545470] neo: WRITE: Next Byte
[ 30.548898] neo: state3
[ 30.551190] neo: s5pv210_i2c_irq enter,132
[ 30.555262] neo: WRITE START
[ 30.558122] neo: WRITE: NO Message NO Byte
[ 30.562194] neo: STOP
[ 30.564448] neo: master_complete 0
[root@FriendlyARM /]# ./i2c_usr_test /dev/i2c/0 0x50 r 0
[ 89.173515] neo: s5pv210_i2c_xfer cnt = 33
[ 89.173557] neo: START
[ 89.173584] neo: iicadd = 0x000000a0,iiccon = 0x0000008a,iicstat =0x000000d0,s5pv210_i2c_message_start,105
[ 89.173666] neo: iicadd = 0x000000a0,iiccon = 0x000000aa,iicstat =0x000000f0,s5pv210_i2c_message_start,110
[ 89.177016] neo: state1
[ 89.179420] neo: s5pv210_i2c_irq enter,132
[ 89.183492] neo: STATE_START
[ 89.186439] neo: WRITE START
[ 89.189298] neo: WRITE: Next Byte
[ 89.192618] neo: state3
[ 89.195018] neo: s5pv210_i2c_irq enter,132
[ 89.199090] neo: WRITE START
[ 89.201950] neo: WRITE: Next Message
[ 89.205503] neo: START
[ 89.207845] neo: iicadd = 0x000000a1,iiccon = 0x000000ba,iicstat =0x000000b0,s5pv210_i2c_message_start,105
[ 89.217465] neo: iicadd = 0x000000a1,iiccon = 0x000000ba,iicstat =0x000000b0,s5pv210_i2c_message_start,110
[ 89.227110] neo: state1
[ 89.229508] neo: s5pv210_i2c_irq enter,132
[ 89.233580] neo: STATE_START
[ 89.236553] neo: state2
[ 89.238953] neo: s5pv210_i2c_irq enter,132
[ 89.243026] neo: READ: Last Byte
[ 89.246232] neo: READ: Last Message
[ 89.249724] neo: state2
[ 89.252125] neo: s5pv210_i2c_irq enter,132
[ 89.256197] neo: READ: NO Byte
[ 89.259230] neo: READ: Last Message NO Byte
[ 89.263476] neo: READ: Send Stop
[ 89.266682] neo: STOP
[ 89.268936] neo: master_complete 0
data: , 17, 0x11