linux 2.6下eeprom at24c08 i2c设备驱动(new style probe方式)

1 修改bsp_以便支持probe

1.1 AT24C08地址的确定

 
原理图上将A2、A1、A0都接地了,所以地址是0x50。

注意到是7位(bit).

1.2 修改bsp

采用友善之臂的, 2.6.32.2内核

[root@localhost mach-s3c2440]# vim /opt/FriendlyARM/mini2440/linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c

#include <linux/i2c.h>

static struct platform_device *mini2440_devices[] __initdata = {

&s3c_device_usb,

&s3c_device_rtc,

&s3c_device_lcd,

&s3c_device_wdt,

&s3c_device_i2c0, //没有修改

&s3c_device_iis,

&mini2440_device_eth,

&s3c24xx_uda134x,

&s3c_device_nand,

&s3c_device_sdi,

&s3c_device_usbgadget,

}; //这里没有修改

static struct at24_platform_data at24c08 = {

.byte_len = SZ_8K / 8,

.page_size = 16,

}; //add

static struct i2c_board_info i2c_devices[] __initdata = {

{ I2C_BOARD_INFO("at24c08b", 0x50),

.platform_data = &at24c08, //不可少的

},

}; //add

#if 0

static struct i2c_board_info i2c_devices[] __initdata = {

{ I2C_BOARD_INFO("at24c08b", 0x50),

.irq=43, //不用.platform_data = &at24c08, 用这个也行

//从cat /proc/interrupt中可知

},

}; //TESTED BY awaken_ing#163

#endif

static void __init mini2440_machine_init(void)

{

i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices)); //add

#if defined (LCD_WIDTH)

s3c24xx_fb_set_platdata(&mini2440_fb_info);

#endif

s3c_i2c0_set_platdata(NULL);

s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);

s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;

s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;

platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

s3c_pm_init();

}

然后make -j2 (传说的多任务, 这里是2个任务, 速度快点)进行编译内核

1.3 编译内核, 然后u-boot部分

#/opt/study_arm/u-boot-2009.11_ok_no_nand/tools/mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n linux_awaken -d /opt/FriendlyARM/mini2440/linux-2.6.32.2/arch/arm/boot/zImage /opt/study_arm/uImage_mini_2.img

#nfs 30008000 192.168.0.9:/opt/study_arm/uImage_mini_2.img

#setenv bootargs noinitrd root=/dev/nfs rw nfsroot=192.168.0.9:/opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/ ip=192.168.0.2:192.168.0.1::255.255.255.0 console=ttySAC0,115200 init=/linuxrc mem=64M

#bootm 0x30008000

1.4 启动linux后

[root@FriendlyARM /home]# ls /dev|grep i2

i2c

这是原先的


[root@FriendlyARM /home]# mknod /dev/at24c08b c 250 0

要mknod的, 不是insmod后产生的, (我傻傻地在这折腾了好久)

[root@FriendlyARM /home]# ls /dev|grep 24

at24c08b

tty24

2 驱动程序

 

/*

dev_i2c.c

*/

#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/kernel.h> 
#include <linux/fs.h> 
#include <asm/uaccess.h> 
#include <linux/i2c.h> 
#include <linux/cdev.h> 
#include <linux/slab.h> 
#include <linux/list.h> 
#include <linux/delay.h>

#define AT24C08B_MAJOR 250

static int at24c08b_major = AT24C08B_MAJOR; 
struct at24c08b_dev 

    struct i2c_client *client; 
    char name[30]; 
    unsigned short current_pointer; 
    struct cdev cdev; 
};

struct at24c08b_dev *at24c08b_devp;

static int 
at24c08b_open (struct inode *inode, struct file *file) 

    file->private_data = at24c08b_devp; 
    return 0; 
}

static ssize_t 
at24c08b_read (struct file *file, char *buf, size_t count, loff_t * ppos) 

    int i = 0; 
    int transferred = 0; 
    int ret, my_buf[512]; 
    struct at24c08b_dev *dev = (struct at24c08b_dev *) file->private_data; 
    dev->current_pointer = *ppos; 
    if (i2c_check_functionality (dev->client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) 
    { 
    while (transferred < count) 
    { 
        ret =i2c_smbus_read_byte_data (dev->client, 
                      dev->current_pointer + i); 
        my_buf[i++] = (unsigned short) ret;

        transferred += 1; 
    } 
    copy_to_user (buf, (void *) my_buf, transferred); 
    dev->current_pointer += transferred; 
    } 
    return transferred; 
}

static ssize_t 
at24c08b_write (struct file *file, char *buf, size_t count, loff_t * ppos) 

    int i = 0; 
    int transferred = 0; 
    int ret, my_buf[512];

    struct at24c08b_dev *dev = (struct at24c08b_dev *) file->private_data; 
    dev->current_pointer = *ppos; 
    if (i2c_check_functionality    (dev->client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 
    { 
    copy_from_user (my_buf, buf, count); 
    while (transferred < count) 
    { 
        ret =i2c_smbus_write_byte_data (dev->client, 
                       dev->current_pointer + i, 
                       my_buf[i]); 
        i += 1; 
        transferred += 1; 
    } 
    dev->current_pointer += transferred; 
    } 
    return transferred; 
}

static int 
at24c08b_ioctl (struct inode *inodep, struct file *file, unsigned int cmd,    unsigned long arg) 

    return 0; 
}

static int 
at24c08b_release (struct inode *inodep, struct file *file) 

    file->private_data = NULL; 
    return 0; 
}

static const struct file_operations at24c08b_fops = { 
    .owner = THIS_MODULE, 
    .open = at24c08b_open, 
    .read = at24c08b_read, 
    .write = at24c08b_write, 
    .ioctl = at24c08b_ioctl, 
    .release = at24c08b_release, 
};

static void 
at24c08b_setup_cdev (struct at24c08b_dev *dev, int index) 

    int err, devnum = MKDEV (at24c08b_major, index); 
    cdev_init (&dev->cdev, &at24c08b_fops); 
    dev->cdev.owner = THIS_MODULE; 
    err = cdev_add (&dev->cdev, devnum, 1); 
    if (err) 
    printk (KERN_NOTICE "Error %d adding at24c08b %d", err, index); 
}

static int __devinit 
at24c08b_probe (struct i2c_client *client, const struct i2c_device_id *id) 

    int ret; 
    printk (KERN_NOTICE "at24c08b probe is start\n");    //调试用,看是否执行了probe 函数 
    dev_t devnum = MKDEV (at24c08b_major, 0);

    if (at24c08b_major) 
    ret = register_chrdev_region (devnum, 1, "at24c08b"); 
    else 
    { 
    ret = alloc_chrdev_region (&devnum, 0, 1, "at24c08b"); 
    at24c08b_major = MAJOR (devnum); 
    } 
    if (ret < 0) 
    return ret; 
    at24c08b_devp = kmalloc (sizeof (struct at24c08b_dev), GFP_KERNEL); 
    if (!at24c08b_devp) 
    { 
    ret = -ENOMEM; 
    goto fail_malloc; 
    } 
    memset (at24c08b_devp, 0, sizeof (struct at24c08b_dev));

    at24c08b_devp->client = client;

    at24c08b_setup_cdev (at24c08b_devp, 0);

    return 0;

  fail_malloc: 
    unregister_chrdev_region (devnum, 1);

    return ret; 
}

static int __devexit 
at24c08b_remove (struct i2c_client *client) 

    cdev_del (&at24c08b_devp->cdev); 
    kfree (at24c08b_devp); 
    unregister_chrdev_region (MKDEV (at24c08b_major, 0), 1);

    return 0; 
}

static const struct i2c_device_id at24c08b_id[] = { 
    {"at24c08b", 0}, //这个0是不是有点奇怪啊, 呵呵 
    {} 
};

MODULE_DEVICE_TABLE (i2c, at24c08b_id); 
static struct i2c_driver at24c08b_driver = { 
    .driver = { 
           .name = "at24c08b", 
           .owner = THIS_MODULE, 
           }, 
    .probe = at24c08b_probe, 
    .remove = __devexit_p (at24c08b_remove), 
    .id_table = at24c08b_id, 
};

static int __init 
at24c08b_init (void) 

    printk (KERN_NOTICE "at24c08b is insmod\n"); 
    return i2c_add_driver (&at24c08b_driver); 
}

void 
at24c08b_exit (void) 

    printk (KERN_NOTICE "at24c08b is rmmod\n"); 
    i2c_del_driver (&at24c08b_driver); 
}

MODULE_DESCRIPTION ("at24c08b eeprom driver"); 
MODULE_LICENSE ("Dual BSD/GPL"); 
MODULE_AUTHOR ("Weimeng Li <pursuitxh@163.com>"); //不是我, 我是awaken_inghttp://blog.163.com/awaken_ing/ 
MODULE_VERSION ("V1.0");

module_param (at24c08b_major, int, S_IRUGO);

module_init (at24c08b_init); 
module_exit (at24c08b_exit);

 

 

3 用户程序

/*app_i2c.c*/

#include <stdio.h> 
#include <linux/types.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/types.h>

int main(int argc, char **argv) 

      int i; 
      unsigned int value[512]; 
      value[0] = 0x12; 
      value[1] = 0x23; 
      value[2] = 0x34; 
      value[3] = 0x45; 
      value[4] = 0x56; 
      value[5] = 0x67;

      int fd; 
      fd = open("/dev/at24c08b", O_RDWR); 
      if(fd < 0) { 
           printf("Open at24c08b Device Faild!\n"); 
                 exit(1); 
            }

      write(fd, value, 6); 
      for(i = 0; i < 6; i++) 
           printf("write reg[%d] data: %x to at24c08\n", i, value[i]); 
      printf("#########################################\n"); 
      sleep(1);

      read(fd, value, 6); 
      for(i = 0; i < 6; i++) 
           printf("read reg[%d] data: %x to at24c08\n", i, value[i]);

      close(fd); 
      return 0;                                   
}

 

 

4 makefile

#Makefile 
#变量APP、DEV分别用于配置用户程序/驱动程序 *文件名* 
APP=app_i2c 
DEV=i2c_no_fops 
ifneq ($(KERNELRELEASE),) 
#call from kernel build system 
obj-m:=$(DEV).o 
else 
KERNELDIR ?=/opt/FriendlyARM/mini2440/linux-2.6.32.2 
PWD :=$(shell pwd) 
default: 
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
    cp $(DEV).ko /opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/home 
    #for app,根据变量APP是否为空来处理 
ifneq ($(APP),) 
    arm-linux-gcc -Wall $(APP).c -o $(APP) 
    cp $(APP) /opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/home 
endif 
endif

clean: 
    rm -rf *.ko 
    rm -rf *.o 
    rm -rf *.mod.*

#注意到$(APP).c会正确得到解析.   

 

5 测试

[root@FriendlyARM /home]# mknod /dev/at24c08b c 250 0

[root@FriendlyARM /home]# insmod dev_i2c.ko

at24c08b is insmod

at24c08b probe is start

[root@FriendlyARM /home]# ./app_i2c

write reg[0] data: 12 to at24c08

write reg[1] data: 23 to at24c08

write reg[2] data: 34 to at24c08

write reg[3] data: 45 to at24c08

write reg[4] data: 56 to at24c08

write reg[5] data: 67 to at24c08

#########################################

read reg[0] data: 12 to at24c08

read reg[1] data: 23 to at24c08

read reg[2] data: 34 to at24c08

read reg[3] data: 45 to at24c08

read reg[4] data: 56 to at24c08

read reg[5] data: 67 to at24c08

 

转载请注明出处 http://blog.163.com/awaken_ing/

 

参考文章

linux文档 Documentation/i2c/upgrading-clients

I2C in the 2.6.32 Linux Kernel http://www.embedded-bits.co.uk/2009/i2c-in-the-2632-linux-kernel/

Mini2440之i2c驱动(2) http://wenku.baidu.com/view/e479442acfc789eb172dc87f.html

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值