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. Android 开机启动

    创建一个Receiver,用来监听开机完毕: public class MyReceiver extends BroadcastReceiver { static final String actio ...

  2. ASCII编码表

    最初的编码:ASCII 补充: ASCII表是适用于美国的,共128位:ISO-8859-1是适用于欧洲的,共256位 :GB2312是适用于中文系统的:UTF-8是万国码,适用于绝大部分语言:ANS ...

  3. Python之反射

    一.引言 有时候我们会碰到类似这样的需求,就是想要执行类的某个方法,或者需要对对象的某个参数赋值,而方法名或参数名已经包装在类中并不能去顶,需要通过参数传递字符串的形式输入.在这样的情况你会选择什么样 ...

  4. java中的23中设计模式(转载的,有时间一定要熟读)

    设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  5. python操作TexturePacker批量打包资源plist png

    import os,sys imagedir = 'D:\\PackerImg\\imgDir' outplistdir = 'D:\\PackerImg\\outDir' comend = 'Tex ...

  6. (6) 深入理解Java Class文件格式(五)

    前情回顾 本专栏的前几篇博文, 对class文件中的常量池进行了详细的解释. 前文讲解了常量池中的7种数据项, 它们分别是: CONSTANT_Utf8_info CONSTANT_NameAndTy ...

  7. 【Redis】Redis的基本安装及使用

    在Linux上安装Redis Redis的安装很简单.基本上是下载.解压.运行安装脚本.我用的Redis版本是3.2.1. [nicchagil@localhost app]$ wget -q htt ...

  8. iOS常用第三方

    名称 作用 说明 AFNetworking 基于HTTP协议联网   SDWebImage 图片缓存和异步加载   YYWebImage 图片缓存和异步加载   Ono XML解析   Rapture ...

  9. (转) C/C++中const关键字详解

    文章转自  http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html 为什么使用const?采用符号常量写出的代码更容易维 ...

  10. 物联网安全拔“牙”实战——低功耗蓝牙(BLE)初探

    物联网安全拔“牙”实战——低功耗蓝牙(BLE)初探 唐朝实验室 · 2015/10/30 10:22 Author: FengGou 0x00 目录 0x00 目录 0x01 前言 0x02 BLE概 ...