s3c2440的i2c控制器驱动(精简DIY),直接上代码,注释很详细:

 #include <linux/kernel.h>
#include <linux/module.h> #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/pm_runtime.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of_i2c.h>
#include <linux/of_gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h> #include <asm/irq.h> #include <plat/regs-iic.h>
#include <plat/iic.h> //#define PRINTK printk
#define PRINTK(...) enum s3c24xx_i2c_state {
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
}; //i2c控制器寄存器
struct s3c2440_i2c_regs {
unsigned int iiccon;
unsigned int iicstat;
unsigned int iicadd;
unsigned int iicds;
unsigned int iiclc;
}; //i2c数据传输载体
struct s3c2440_i2c_xfer_data {
struct i2c_msg *msgs;
int msn_num;
int cur_msg;
int cur_ptr;
int state;
int err;
wait_queue_head_t wait;
}; static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data; static struct s3c2440_i2c_regs *s3c2440_i2c_regs; static void s3c2440_i2c_start(void)
{
s3c2440_i2c_xfer_data.state = STATE_START; if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
{
s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << ; 
s3c2440_i2c_regs->iicstat = 0xb0; // 主机接收,启动
}
else /* 写 */
{
s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << ;
s3c2440_i2c_regs->iicstat = 0xf0; // 主机发送,启动
}
} static void s3c2440_i2c_stop(int err)
{
s3c2440_i2c_xfer_data.state = STATE_STOP;
s3c2440_i2c_xfer_data.err = err; PRINTK("STATE_STOP, err = %d\n", err); if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
{
// 下面两行恢复I2C操作,发出P信号
s3c2440_i2c_regs->iicstat = 0x90;
s3c2440_i2c_regs->iiccon = 0xaf;
ndelay(); // 等待一段时间以便P信号已经发出
}
else /* 写 */
{
// 下面两行用来恢复I2C操作,发出P信号
s3c2440_i2c_regs->iicstat = 0xd0;
s3c2440_i2c_regs->iiccon = 0xaf;
ndelay(); // 等待一段时间以便P信号已经发出
} /* 唤醒 */
wake_up(&s3c2440_i2c_xfer_data.wait); } //i2c总线数据传输处理函数
static int s3c2440_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
unsigned long timeout; /* 把num个msg的I2C数据发送出去/读进来 */
s3c2440_i2c_xfer_data.msgs = msgs;
s3c2440_i2c_xfer_data.msn_num = num;
s3c2440_i2c_xfer_data.cur_msg = ;
s3c2440_i2c_xfer_data.cur_ptr = ;
s3c2440_i2c_xfer_data.err = -ENODEV; //确认是否有ack应答 s3c2440_i2c_start(); //发出start信号,判断read or write /* 休眠-等待i2c读写状态改变 */
timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * ); //等待状态成立或5s
if ( == timeout)
{
printk("s3c2440_i2c_xfer time out\n");
return -ETIMEDOUT;
}
else
{
return s3c2440_i2c_xfer_data.err;
}
} static u32 s3c2440_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
} static const struct i2c_algorithm s3c2440_i2c_algo = {
// .smbus_xfer = , //smbus是i2c传输的一个子集,支持的话可以在这里指定处理函数
.master_xfer = s3c2440_i2c_xfer, //传输函数
.functionality = s3c2440_i2c_func,
}; /* 1. 分配/设置i2c_adapter
*/
static struct i2c_adapter s3c2440_i2c_adapter = {
.name = "s3c2440_sheldon",
.algo = &s3c2440_i2c_algo, //算法函数
.owner = THIS_MODULE,
}; static int isLastMsg(void)
{
return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - );
} static int isEndData(void)
{
return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len);
} static int isLastData(void)
{
return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - );
} static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id)
{
unsigned int iicSt; iicSt = s3c2440_i2c_regs->iicstat; //读取i2c控制器的状态寄存器,判断是否读写成功 if(iicSt & 0x8){ printk("Bus arbitration failed\n\r"); } switch (s3c2440_i2c_xfer_data.state)
{
case STATE_START : /* 发出S和设备地址后,产生中断 */
{
PRINTK("Start\n");
/* 如果没有ACK, 返回错误 */
if (iicSt & S3C2410_IICSTAT_LASTBIT)
{
s3c2440_i2c_stop(-ENODEV);
break;
} if (isLastMsg() && isEndData())
{
s3c2440_i2c_stop();
break;
} /* 进入下一个状态 */
if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
{
s3c2440_i2c_xfer_data.state = STATE_READ;
goto next_read;
}
else
{
s3c2440_i2c_xfer_data.state = STATE_WRITE;
}
} case STATE_WRITE:
{
PRINTK("STATE_WRITE\n");
/* 如果没有ACK, 返回错误 */
if (iicSt & S3C2410_IICSTAT_LASTBIT)
{
s3c2440_i2c_stop(-ENODEV);
break;
} if (!isEndData()) /* 如果当前msg还有数据要发送 */
{
s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr];
s3c2440_i2c_xfer_data.cur_ptr++; // 将数据写入IICDS后,需要一段时间才能出现在SDA线上
ndelay(); s3c2440_i2c_regs->iiccon = 0xaf; // 恢复I2C传输
break;
}
else if (!isLastMsg())
{
/* 开始处理下一个消息 */
s3c2440_i2c_xfer_data.msgs++;
s3c2440_i2c_xfer_data.cur_msg++;
s3c2440_i2c_xfer_data.cur_ptr = ;
s3c2440_i2c_xfer_data.state = STATE_START;
/* 发出START信号和发出设备地址 */
s3c2440_i2c_start();
break;
}
else
{
/* 是最后一个消息的最后一个数据 */
s3c2440_i2c_stop();
break;
} break;
} case STATE_READ:
{
PRINTK("STATE_READ\n");
/* 读出数据 */
s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds;
s3c2440_i2c_xfer_data.cur_ptr++;
next_read:
if (!isEndData()) /* 如果数据没读写, 继续发起读操作 */
{
if (isLastData()) /* 如果即将读的数据是最后一个, 不发ack */
{
s3c2440_i2c_regs->iiccon = 0x2f; // 恢复I2C传输,接收到下一数据时无ACK
}
else
{
s3c2440_i2c_regs->iiccon = 0xaf; // 恢复I2C传输,接收到下一数据时发出ACK
}
break;
}
else if (!isLastMsg())
{
/* 开始处理下一个消息 */
s3c2440_i2c_xfer_data.msgs++;
s3c2440_i2c_xfer_data.cur_msg++;
s3c2440_i2c_xfer_data.cur_ptr = ;
s3c2440_i2c_xfer_data.state = STATE_START;
/* 发出START信号和发出设备地址 */
s3c2440_i2c_start();
break;
}
else
{
/* 是最后一个消息的最后一个数据 */
s3c2440_i2c_stop();
break;
}
break;
} default: break;
} /* 清中断 */
s3c2440_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND); return IRQ_HANDLED;
} /*
* I2C初始化
*/
static void s3c2440_i2c_init(void)
{
struct clk *clk; clk = clk_get(NULL, "i2c");
clk_enable(clk); // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL
    s3c_gpio_cfgpin(S3C2410_GPE(), S3C2410_GPE14_IICSCL);
s3c_gpio_cfgpin(S3C2410_GPE(), S3C2410_GPE15_IICSDA); /* bit[7] = 1, 使能ACK
* bit[6] = 0, IICCLK = PCLK/16
* bit[5] = 1, 使能中断
* bit[3:0] = 0xf, Tx clock = IICCLK/16
* PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
*/
s3c2440_i2c_regs->iiccon = (<<) | (<<) | (<<) | (0xf); // 0xaf s3c2440_i2c_regs->iicadd = 0x10; // S3C24xx slave address = [7:1]
s3c2440_i2c_regs->iicstat = 0x10; // I2C串行输出使能(Rx/Tx)
} static int i2c_bus_s3c2440_init(void)
{
/* 2. 硬件相关的设置 */
s3c2440_i2c_regs = ioremap(0x54000000, sizeof(struct s3c2440_i2c_regs));//映射功能寄存器 s3c2440_i2c_init(); //初始化i2c控制器 request_irq(IRQ_IIC, s3c2440_i2c_xfer_irq, , "s3c2440-i2c", NULL); //申请中断源,加载中断处理函数-s3c2440_i2c_xfer_irq init_waitqueue_head(&s3c2440_i2c_xfer_data.wait); //初始化一个等待队列头 /* 3. 注册i2c_adapter */
i2c_add_adapter(&s3c2440_i2c_adapter); return ;
} static void i2c_bus_s3c2440_exit(void)
{
i2c_del_adapter(&s3c2440_i2c_adapter);
free_irq(IRQ_IIC, NULL);
iounmap(s3c2440_i2c_regs);
} module_init(i2c_bus_s3c2440_init);
module_exit(i2c_bus_s3c2440_exit);
MODULE_LICENSE("GPL");

附一份测试程序:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "i2c-dev.h" /* i2c_usr_test </dev/i2c-0> <dev_addr> r addr
* i2c_usr_test </dev/i2c-0> <dev_addr> w addr val
*/ void print_usage(char *file)
{
printf("%s </dev/i2c-0> <dev_addr> r addr\n", file);
printf("%s </dev/i2c-0> <dev_addr> w addr val\n", file);
} int main(int argc, char **argv)
{
int fd;
unsigned char addr, data;
int dev_addr; if ((argc != ) && (argc != ))
{
print_usage(argv[]);
return -;
} fd = open(argv[], O_RDWR);
if (fd < )
{
printf("can't open %s\n", argv[]);
return -;
} dev_addr = strtoul(argv[], NULL, );
if (ioctl(fd, I2C_SLAVE, dev_addr) < )
{
/* ERROR HANDLING; you can check errno to see what went wrong */
printf("set addr error!\n");
return -;
} if (strcmp(argv[], "r") == )
{
addr = strtoul(argv[], NULL, ); data = i2c_smbus_read_word_data(fd, addr); printf("data: %c, %d, 0x%2x\n", data, data, data);
}
else if ((strcmp(argv[], "w") == ) && (argc == ))
{
addr = strtoul(argv[], NULL, );
data = strtoul(argv[], NULL, );
i2c_smbus_write_byte_data(fd, addr, data);
}
else
{
print_usage(argv[]);
return -;
} return ;
}

Make File:

KERN_DIR = /work/system/linux-3.4.2

all:
    make -C $(KERN_DIR) M=`pwd` modules clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order obj-m    += i2c_bus_s3c2440.o

Linux I2C总线控制器驱动(S3C2440)的更多相关文章

  1. Linux I2C总线设备驱动模型分析(ov7740)

    1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_r ...

  2. Linux+I2C总线分析(主要是probe的方式)

    Linux I2C 总线浅析 ㈠ Overview Linux的I2C体系结构分为3个组成部分: ·I2C核心: I2C核心提供了I2C总线驱动和设备驱动的注册.注销方法,I2C通信方法(即“algo ...

  3. Linux的总线设备驱动模型

    裸机编写驱动比较自由,按照手册实现其功能即可,每个人写出来都有很大不同: 而Linux中还需要按照Linux的驱动模型来编写,也就是需要按照"模板"来写,写出来的驱动就比较统一. ...

  4. 芯灵思SinlinxA33开发板 Linux平台总线设备驱动

    1.什么是platform(平台)总线? 相对于USB.PCI.I2C.SPI等物理总线来说,platform总线是一种虚拟.抽象出来的总线,实际中并不存在这样的总线. 那为什么需要platform总 ...

  5. I2C总线和S5PV210的I2C总线控制器

    一.什么是I2C通信协议? 1.物理接口:SCL + SDA (1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道. (2)SDA(serial ...

  6. Linux中总线设备驱动模型及平台设备驱动实例

    本文将简要地介绍Linux总线设备驱动模型及其实现方式,并不会过多地涉及其在内核中的具体实现,最后,本文将会以平台总线为例介绍设备和驱动程序的实现过程. 目录: 一.总线设备驱动模型总体介绍及其实现方 ...

  7. Linux平台总线设备驱动

    1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性. 2. 平台总 ...

  8. Linux I2C核心、总线和设备驱动

    目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...

  9. Linux设备驱动模型之I2C总线

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...

随机推荐

  1. Azure Table storage 之改进DynamicTableEntity类为其添加动态语言扩展

    在之前的一篇文章中提到,storage类库中包含一个可以用来动态获取Azure table storage 表结构的类-DynamicTableEntity. 我们可以通过这个类,我们无需为每一个表提 ...

  2. [课程设计]Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)

    Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)  1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅 ...

  3. IOS new Date() 时间转换失败问题以及其他问题

    一: ios下必须标准格式用'/'分割格式 转换没问题:  new Date('2016/5/30 12:15:20') 二:ie9兼容模式以及以下ie版本 console用前需判断 if(windo ...

  4. (原创) cocos2dx使用Curl连接网络(客户端)

    0. 环境: winxpsp3, vs2010, cocos2dx@2.1.4 1. 新建一个Helloworld工程 2. HelloworldScene.h里面重写virtual bool ccT ...

  5. LA 3713 宇航员分组

    题目链接:http://vjudge.net/contest/142615#problem/B 题意:有A,B,C三个人物要分配个N个宇航员,每个宇航员恰好要分配一个任务,设平均年龄为X,只有年龄大于 ...

  6. Android中插件开发篇之----动态加载Activity(免安装运行程序)

    一.前言 又到周末了,时间过的很快,今天我们来看一下Android中插件开发篇的最后一篇文章的内容:动态加载Activity(免安装运行程序),在上一篇文章中说道了,如何动态加载资源(应用换肤原理解析 ...

  7. [问题2014S07] 复旦高等代数II(13级)每周一题(第七教学周)

    [问题2014S07]  设 \(A\in M_n(\mathbb{K})\) 在数域 \(\mathbb{K}\) 上的初等因子组为 \(P_1(\lambda)^{e_1},P_2(\lambda ...

  8. 统计单词个数及词频(C++实现)

    #include<iostream> #include<fstream> #include<string> using namespace std; struct ...

  9. Delphi 使用之dll文件生成与调用

    DLL是Dynamic-Link Libraries(动态链接库)的缩写,库里面是一些可执行的模块以及资源(如位图.图标等).可以认为DLL和EXE基本上是一回事,只是DLL不能直接执行,而必须由应用 ...

  10. Java Performance - 如何调查解决 CPU 问题

    随着硬件的发展,往往服务器会配置足够的 CPUs, Java Server/服务器不太有 CPU 问题:但是偶尔因为 代码海量循环 或者 线程安全性(thread safe), 还是会带来 CPU 问 ...