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. SQL2005中的事务与锁定(九)- 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

  2. [2]R语言在数据处理上的禀赋之——可视化技术

    本文目录 Java的可视化技术 R的可视化技术 二维做图利器plot的参数配置 *权限机制 *plot独有的参数 *plot的type介绍 *title介绍 *公共参数集合--par *par的权限机 ...

  3. OAuth2.0概述

    OAuth2.0较1.0相比,整个授权验证流程更简单更安全,也是未来最主要的用户身份验证和授权方式. 关于OAuth2.0协议的授权流程可以参考下面的流程图,其中Client指第三方应用,Resour ...

  4. T-SQL 的简单查询语句

    通配符: “_”: 代表匹配一个字符 “%”: 代表匹配多个字符: []:表示范围,可以包含多个数据 [^] 表示取反 “-“  表示范围 逻辑与 and 逻辑或 or  逻辑非 not 聚会函数 : ...

  5. session失效后,登录页面嵌入iframe框架

    在登录页面的onload方法中加入以下代码解决: //防止登录页面嵌入iframe框架 if (top.location != self.location){ top.location=self.lo ...

  6. 关于JQ的$.deferred函数。参考网络文档

    由于jQuery版本问题对Deferred对象的实现有所不同,具体请参照jQuery api:   jQuery.Deferred()基于Promises/A规范实现,因为jQuery本身的设计风格, ...

  7. js广告弹窗

    生活中我们经常遇到一些烦人的广告页面,比方说弹窗,悬浮等等各种广告.有的同事甚至都下一个屏蔽广告插件到浏览器上.这样就防止了广告的干扰. 但是我们学前端的必须是要知道广告弹窗这个做的过程,甚至是它的原 ...

  8. [问题2014A12] 复旦高等代数 I(14级)每周一题(第十四教学周)

    [问题2014A12]  设 \(A,B\) 是 \(n\) 阶方阵且满足 \(AB=BA=0\), \(\mathrm{r}(A)=\mathrm{r}(A^2)\), 证明: \[\mathrm{ ...

  9. faster alter table add column

    Create a new table (using the structure of the current table) with the new column(s) included. execu ...

  10. Linux(centos)的常用基本命令

    Linux的常用基本命令. 首先启动Linux.启动完毕后需要进行用户的登录,选择登陆的用户不同自然权限也不一样,其中“系统管理员”拥有最高权限. 在启动Linux后屏幕出现如下界面显示: …… Re ...