您的位置:首页 > 运维架构 > Linux

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

2014-09-03 17:58 483 查看


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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: