s3c2440的i2c控制器驱动(精简DIY),直接上代码,注释很详细:
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 4 #include <linux/i2c.h> 5 #include <linux/init.h> 6 #include <linux/time.h> 7 #include <linux/interrupt.h> 8 #include <linux/delay.h> 9 #include <linux/errno.h> 10 #include <linux/err.h> 11 #include <linux/platform_device.h> 12 #include <linux/pm_runtime.h> 13 #include <linux/clk.h> 14 #include <linux/cpufreq.h> 15 #include <linux/slab.h> 16 #include <linux/io.h> 17 #include <linux/of_i2c.h> 18 #include <linux/of_gpio.h> 19 #include <plat/gpio-cfg.h> 20 #include <mach/regs-gpio.h> 21 22 #include <asm/irq.h> 23 24 #include <plat/regs-iic.h> 25 #include <plat/iic.h> 26 27 //#define PRINTK printk 28 #define PRINTK(...) 29 30 enum s3c24xx_i2c_state { 31 STATE_IDLE, 32 STATE_START, 33 STATE_READ, 34 STATE_WRITE, 35 STATE_STOP 36 }; 37 38 //i2c控制器寄存器 39 struct s3c2440_i2c_regs { 40 unsigned int iiccon; 41 unsigned int iicstat; 42 unsigned int iicadd; 43 unsigned int iicds; 44 unsigned int iiclc; 45 }; 46 47 //i2c数据传输载体 48 struct s3c2440_i2c_xfer_data { 49 struct i2c_msg *msgs; 50 int msn_num; 51 int cur_msg; 52 int cur_ptr; 53 int state; 54 int err; 55 wait_queue_head_t wait; 56 }; 57 58 static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data; 59 60 61 static struct s3c2440_i2c_regs *s3c2440_i2c_regs; 62 63 64 static void s3c2440_i2c_start(void) 65 { 66 s3c2440_i2c_xfer_data.state = STATE_START; 67 68 if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */ 69 { 70 s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << 1; 71 s3c2440_i2c_regs->iicstat = 0xb0; // 主机接收,启动 72 } 73 else /* 写 */ 74 { 75 s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << 1; 76 s3c2440_i2c_regs->iicstat = 0xf0; // 主机发送,启动 77 } 78 } 79 80 static void s3c2440_i2c_stop(int err) 81 { 82 s3c2440_i2c_xfer_data.state = STATE_STOP; 83 s3c2440_i2c_xfer_data.err = err; 84 85 PRINTK("STATE_STOP, err = %d ", err); 86 87 88 if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */ 89 { 90 // 下面两行恢复I2C操作,发出P信号 91 s3c2440_i2c_regs->iicstat = 0x90; 92 s3c2440_i2c_regs->iiccon = 0xaf; 93 ndelay(50); // 等待一段时间以便P信号已经发出 94 } 95 else /* 写 */ 96 { 97 // 下面两行用来恢复I2C操作,发出P信号 98 s3c2440_i2c_regs->iicstat = 0xd0; 99 s3c2440_i2c_regs->iiccon = 0xaf; 100 ndelay(50); // 等待一段时间以便P信号已经发出 101 } 102 103 /* 唤醒 */ 104 wake_up(&s3c2440_i2c_xfer_data.wait); 105 106 } 107 108 //i2c总线数据传输处理函数 109 static int s3c2440_i2c_xfer(struct i2c_adapter *adap, 110 struct i2c_msg *msgs, int num) 111 { 112 unsigned long timeout; 113 114 /* 把num个msg的I2C数据发送出去/读进来 */ 115 s3c2440_i2c_xfer_data.msgs = msgs; 116 s3c2440_i2c_xfer_data.msn_num = num; 117 s3c2440_i2c_xfer_data.cur_msg = 0; 118 s3c2440_i2c_xfer_data.cur_ptr = 0; 119 s3c2440_i2c_xfer_data.err = -ENODEV; //确认是否有ack应答 120 121 s3c2440_i2c_start(); //发出start信号,判断read or write 122 123 /* 休眠-等待i2c读写状态改变 */ 124 timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5); //等待状态成立或5s 125 if (0 == timeout) 126 { 127 printk("s3c2440_i2c_xfer time out "); 128 return -ETIMEDOUT; 129 } 130 else 131 { 132 return s3c2440_i2c_xfer_data.err; 133 } 134 } 135 136 static u32 s3c2440_i2c_func(struct i2c_adapter *adap) 137 { 138 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; 139 } 140 141 142 static const struct i2c_algorithm s3c2440_i2c_algo = { 143 // .smbus_xfer = , //smbus是i2c传输的一个子集,支持的话可以在这里指定处理函数 144 .master_xfer = s3c2440_i2c_xfer, //传输函数 145 .functionality = s3c2440_i2c_func, 146 }; 147 148 /* 1. 分配/设置i2c_adapter 149 */ 150 static struct i2c_adapter s3c2440_i2c_adapter = { 151 .name = "s3c2440_sheldon", 152 .algo = &s3c2440_i2c_algo, //算法函数 153 .owner = THIS_MODULE, 154 }; 155 156 static int isLastMsg(void) 157 { 158 return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - 1); 159 } 160 161 static int isEndData(void) 162 { 163 return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len); 164 } 165 166 static int isLastData(void) 167 { 168 return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - 1); 169 } 170 171 static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id) 172 { 173 unsigned int iicSt; 174 175 iicSt = s3c2440_i2c_regs->iicstat; //读取i2c控制器的状态寄存器,判断是否读写成功 176 177 if(iicSt & 0x8){ printk("Bus arbitration failed "); } 178 179 switch (s3c2440_i2c_xfer_data.state) 180 { 181 case STATE_START : /* 发出S和设备地址后,产生中断 */ 182 { 183 PRINTK("Start "); 184 /* 如果没有ACK, 返回错误 */ 185 if (iicSt & S3C2410_IICSTAT_LASTBIT) 186 { 187 s3c2440_i2c_stop(-ENODEV); 188 break; 189 } 190 191 if (isLastMsg() && isEndData()) 192 { 193 s3c2440_i2c_stop(0); 194 break; 195 } 196 197 /* 进入下一个状态 */ 198 if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */ 199 { 200 s3c2440_i2c_xfer_data.state = STATE_READ; 201 goto next_read; 202 } 203 else 204 { 205 s3c2440_i2c_xfer_data.state = STATE_WRITE; 206 } 207 } 208 209 case STATE_WRITE: 210 { 211 PRINTK("STATE_WRITE "); 212 /* 如果没有ACK, 返回错误 */ 213 if (iicSt & S3C2410_IICSTAT_LASTBIT) 214 { 215 s3c2440_i2c_stop(-ENODEV); 216 break; 217 } 218 219 if (!isEndData()) /* 如果当前msg还有数据要发送 */ 220 { 221 s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr]; 222 s3c2440_i2c_xfer_data.cur_ptr++; 223 224 // 将数据写入IICDS后,需要一段时间才能出现在SDA线上 225 ndelay(50); 226 227 s3c2440_i2c_regs->iiccon = 0xaf; // 恢复I2C传输 228 break; 229 } 230 else if (!isLastMsg()) 231 { 232 /* 开始处理下一个消息 */ 233 s3c2440_i2c_xfer_data.msgs++; 234 s3c2440_i2c_xfer_data.cur_msg++; 235 s3c2440_i2c_xfer_data.cur_ptr = 0; 236 s3c2440_i2c_xfer_data.state = STATE_START; 237 /* 发出START信号和发出设备地址 */ 238 s3c2440_i2c_start(); 239 break; 240 } 241 else 242 { 243 /* 是最后一个消息的最后一个数据 */ 244 s3c2440_i2c_stop(0); 245 break; 246 } 247 248 break; 249 } 250 251 case STATE_READ: 252 { 253 PRINTK("STATE_READ "); 254 /* 读出数据 */ 255 s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds; 256 s3c2440_i2c_xfer_data.cur_ptr++; 257 next_read: 258 if (!isEndData()) /* 如果数据没读写, 继续发起读操作 */ 259 { 260 if (isLastData()) /* 如果即将读的数据是最后一个, 不发ack */ 261 { 262 s3c2440_i2c_regs->iiccon = 0x2f; // 恢复I2C传输,接收到下一数据时无ACK 263 } 264 else 265 { 266 s3c2440_i2c_regs->iiccon = 0xaf; // 恢复I2C传输,接收到下一数据时发出ACK 267 } 268 break; 269 } 270 else if (!isLastMsg()) 271 { 272 /* 开始处理下一个消息 */ 273 s3c2440_i2c_xfer_data.msgs++; 274 s3c2440_i2c_xfer_data.cur_msg++; 275 s3c2440_i2c_xfer_data.cur_ptr = 0; 276 s3c2440_i2c_xfer_data.state = STATE_START; 277 /* 发出START信号和发出设备地址 */ 278 s3c2440_i2c_start(); 279 break; 280 } 281 else 282 { 283 /* 是最后一个消息的最后一个数据 */ 284 s3c2440_i2c_stop(0); 285 break; 286 } 287 break; 288 } 289 290 default: break; 291 } 292 293 /* 清中断 */ 294 s3c2440_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND); 295 296 return IRQ_HANDLED; 297 } 298 299 300 /* 301 * I2C初始化 302 */ 303 static void s3c2440_i2c_init(void) 304 { 305 struct clk *clk; 306 307 clk = clk_get(NULL, "i2c"); 308 clk_enable(clk); 309 310 // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL 311 s3c_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL); 312 s3c_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA); 313 314 /* bit[7] = 1, 使能ACK 315 * bit[6] = 0, IICCLK = PCLK/16 316 * bit[5] = 1, 使能中断 317 * bit[3:0] = 0xf, Tx clock = IICCLK/16 318 * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz 319 */ 320 s3c2440_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf 321 322 s3c2440_i2c_regs->iicadd = 0x10; // S3C24xx slave address = [7:1] 323 s3c2440_i2c_regs->iicstat = 0x10; // I2C串行输出使能(Rx/Tx) 324 } 325 326 static int i2c_bus_s3c2440_init(void) 327 { 328 /* 2. 硬件相关的设置 */ 329 s3c2440_i2c_regs = ioremap(0x54000000, sizeof(struct s3c2440_i2c_regs));//映射功能寄存器 330 331 s3c2440_i2c_init(); //初始化i2c控制器 332 333 request_irq(IRQ_IIC, s3c2440_i2c_xfer_irq, 0, "s3c2440-i2c", NULL); //申请中断源,加载中断处理函数-s3c2440_i2c_xfer_irq 334 335 init_waitqueue_head(&s3c2440_i2c_xfer_data.wait); //初始化一个等待队列头 336 337 /* 3. 注册i2c_adapter */ 338 i2c_add_adapter(&s3c2440_i2c_adapter); 339 340 return 0; 341 } 342 343 static void i2c_bus_s3c2440_exit(void) 344 { 345 i2c_del_adapter(&s3c2440_i2c_adapter); 346 free_irq(IRQ_IIC, NULL); 347 iounmap(s3c2440_i2c_regs); 348 } 349 350 module_init(i2c_bus_s3c2440_init); 351 module_exit(i2c_bus_s3c2440_exit); 352 MODULE_LICENSE("GPL");
附一份测试程序:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> 7 #include "i2c-dev.h" 8 9 10 /* i2c_usr_test </dev/i2c-0> <dev_addr> r addr 11 * i2c_usr_test </dev/i2c-0> <dev_addr> w addr val 12 */ 13 14 void print_usage(char *file) 15 { 16 printf("%s </dev/i2c-0> <dev_addr> r addr ", file); 17 printf("%s </dev/i2c-0> <dev_addr> w addr val ", file); 18 } 19 20 int main(int argc, char **argv) 21 { 22 int fd; 23 unsigned char addr, data; 24 int dev_addr; 25 26 if ((argc != 5) && (argc != 6)) 27 { 28 print_usage(argv[0]); 29 return -1; 30 } 31 32 fd = open(argv[1], O_RDWR); 33 if (fd < 0) 34 { 35 printf("can't open %s ", argv[1]); 36 return -1; 37 } 38 39 dev_addr = strtoul(argv[2], NULL, 0); 40 if (ioctl(fd, I2C_SLAVE, dev_addr) < 0) 41 { 42 /* ERROR HANDLING; you can check errno to see what went wrong */ 43 printf("set addr error! "); 44 return -1; 45 } 46 47 if (strcmp(argv[3], "r") == 0) 48 { 49 addr = strtoul(argv[4], NULL, 0); 50 51 data = i2c_smbus_read_word_data(fd, addr); 52 53 printf("data: %c, %d, 0x%2x ", data, data, data); 54 } 55 else if ((strcmp(argv[3], "w") == 0) && (argc == 6)) 56 { 57 addr = strtoul(argv[4], NULL, 0); 58 data = strtoul(argv[5], NULL, 0); 59 i2c_smbus_write_byte_data(fd, addr, data); 60 } 61 else 62 { 63 print_usage(argv[0]); 64 return -1; 65 } 66 67 return 0; 68 }
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