I2C总线的EEPROM(24C08)Linux驱动

  基于Linux 2.6.30内核

符合Linux驱动架构模型

针对24C08的Page读写做了优化。

完全模拟文件读写方式,支持lseek操作。


这个代码中,包含了设备的地址,在i2c_add_driver时会去探测该地址上是否有设备。

但通常,做板级开发时,i2c_device被放在board文件的i2c_registry中,

i2c_device和i2c_driver根据name字段来匹配。


转载请注明出处

代码原创

注:更新的Linux版本内核中,i2c_driver结构体有少许的变化。 

/* 
 * eeprom-24c08.c - eeprom driver for some mostly-compatible I2C chips. 
 * 
 *  Copyright (C) 2011 William Smith 
 * 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation. 
 */  
  
#include <linux/module.h>   
#include <linux/init.h>   
#include <linux/slab.h>   
#include <linux/i2c.h>   
//#include <linux/bcd.h>   
/*New Add */  
#include <linux/cdev.h>   
#include <arm-asm/uaccess.h>   
#include <linux/fs.h>   
  
#include <linux/device.h>   
  
  
#define EEP_MAJOR 236   /* Major number can also be dynamically allocated. */   
#define BNK_SIZE 1024   /* BANK SIZE */   
#define BLK_SIZE 256    /* BLOCK SIZE */   
#define PG_SIZE 16  /* PAGE SIZE */   
#define EEPROM_SLAVE_ADDR0 0x50 /* 7bit. Binary: 1010000 */   
#define EEPROM_SLAVE_ADDR1 0x51 /* 7bit. Binary: 1010001 */   
#define EEPROM_SLAVE_ADDR2 0x52 /* 7bit. Binary: 1010010 */   
#define EEPROM_SLAVE_ADDR3 0x53 /* 7bit. Binary: 1010011 */   
  
#define MAX_RETRYS 30   
  
/* For DEBUG */  
//#define DEBUG_EEP   
#define DEBUG_EEP_R   
  
static int max_retrys_record = 0;  
  
static struct eep_24c08 {  
    struct i2c_client *client[4];   /* I2C client for this eeprom */  
    struct cdev eep_cdev;       /* cdev */  
    dev_t dev_num;              /* dev num */  
    unsigned int cur_ptr;       /* Current File pointer */  
} *eep;  
  
static int eep_open (struct inode *inode, struct file *file)  
{  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM open\n");  
    #endif   
  
    max_retrys_record = 0;  
  
    eep->cur_ptr = 0;  
    return 0;  
}  
  
static int eep_release (struct inode *inode, struct file *file)  
{  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM close\n");  
    #endif   
    return 0;  
}  
  
static loff_t eep_llseek(struct file *filp, loff_t off, int whence)  
{  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM lseek\n");  
    #endif   
      
    switch(whence)  
    {  
        case 0: /* SEEK_SET */  
            eep->cur_ptr = off;  
            break;  
        case 1: /* SEEK_CUR */  
            eep->cur_ptr += off;  
            break;  
        case 2: /* SEEK_END */  
            eep->cur_ptr = BNK_SIZE + off;  
            break;  
        default: /* can't happen */  
            return -EINVAL;  
    }  
    if ((eep->cur_ptr < 0) || (eep->cur_ptr > BNK_SIZE)) {  
        printk(KERN_ALERT "EEPROM 'lseek()' call, param mistake\n");  
        eep->cur_ptr = 0;  
        return -1;  
    }  
      
    return eep->cur_ptr;  
}  
  
static int __rw_page(char *buf, int length, int rw)  
{  
    int ret,err=0;  
    int block;  
    unsigned char word_addr;  
    struct i2c_adapter *adap;  
    struct i2c_msg msg[2];  
    int msg_num;  
    unsigned char *buf_tmp=NULL;  
  
    block = eep->cur_ptr / BLK_SIZE;  
    adap = eep->client[block]->adapter;  
    word_addr = eep->cur_ptr % BLK_SIZE;  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "__rw_page(): %s: client[%d]->addr = 0x%02X, cur_ptr = %d, block = %d, word_addr = %d\n",   
        (rw?"write":"read"), block, eep->client[block]->addr, eep->cur_ptr, block, word_addr);  
    #endif   
  
    if(rw) {  
        /* write data */  
  
        /* alloc memory for buf_tmp */  
        buf_tmp = kmalloc(length+1, GFP_KERNEL);  
        if (!buf_tmp) {  
            printk(KERN_ALERT "EEPROM kmalloc %d failed\n", length+1);  
            return -ENOMEM;  
        }  
  
        memcpy(buf_tmp+1, buf, length);  
        buf_tmp[0] = word_addr;  
        msg[0].addr = eep->client[block]->addr;  
        msg[0].flags    = 0;      
        msg[0].len  = length+1;  
        msg[0].buf  = buf_tmp;  
    } else {  
        /* read data */  
        msg[0].addr = eep->client[block]->addr;  
        msg[0].flags    = 0;      
        msg[0].len  = 1;  
        msg[0].buf  = &word_addr;  
  
        msg[1].addr = eep->client[block]->addr;  
        msg[1].flags    = I2C_M_RD;   
        msg[1].len  = length;  
        msg[1].buf  = buf;  
    }  
    msg_num = rw?1:2;  
  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "msg_num = %d, msg[0]: addr = 0x%02X, flags = %d, len = %d, buf[0~3] = %02X,%02X,%02X,%02X\n",  
            msg_num, msg[0].addr, msg[0].flags, msg[0].len,  
            msg[0].buf[0], msg[0].buf[1], msg[0].buf[2], msg[0].buf[3]);  
    #endif   
  
    ret =  i2c_transfer(adap, msg, msg_num);  
    if (ret<0) {  
        #ifdef DEBUG_EEP   
        printk(KERN_ALERT "i2c_transfer() err %d\n", ret);  
        #endif   
        err = ret;  
        goto exit;  
    } else if (ret<(rw?1:2)) {  
        printk(KERN_ERR "__rw_page(): %s: error, actual write i2c_msg number %d, desire i2c_msg number %d\n",  
                (rw?"write":"read"), ret, (rw?1:2));  
        err = -EIO;  
        goto exit;  
    }  
  
exit:  
    if (rw)  
        kfree(buf_tmp);  
    return (err?err:length);  
}  
  
static int rw_page(char *buf, int length, int rw)  
{  
    int i;  
    int ret,err=0;  
    char *buf_tmp;  
  
    /* alloc memory for buf_tmp */  
    buf_tmp = kmalloc(length, GFP_KERNEL);  
    if (!buf_tmp) {  
        printk(KERN_ALERT "EEPROM kmalloc %d failed\n", length);  
        return -ENOMEM;  
    }  
  
    for (i=0;i<MAX_RETRYS;i++) {  
        ret = __rw_page(buf, length, rw);  
        if (ret<0)  
            continue;  
        ret = __rw_page(buf_tmp, length, 0);  
        if (ret<0)  
            continue;  
        ret = memcmp(buf, buf_tmp, length);  
        if (ret == 0)     
            break;  
        else {  
            #ifdef DEBUG_EEP   
            printk(KERN_ALERT "rw_page(): %s : memcmp return %d\n", (rw?"write":"read"),  ret);  
            printk(KERN_ALERT "Original buf: ");  
            for (i=0;i<length;i++)  
                printk(KERN_ALERT "%02X ", buf[i]);  
            printk(KERN_ALERT "\nRecheck buf: ");  
            for (i=0;i<length;i++)  
                printk(KERN_ALERT "%02X ", buf_tmp[i]);  
            printk(KERN_ALERT "\n");  
            #endif   
        }  
    }  
    #ifdef DEBUG_EEP_R   
    if (i) {  
        printk(KERN_ALERT "Retry %d times.\n", i);  
        if (i > max_retrys_record)  
            max_retrys_record = i;  
    }         
    #endif     
    if (i == MAX_RETRYS) {  
        printk(KERN_ALERT "Reach MAX_RETRYS(%d times) limit.\n", MAX_RETRYS);  
        err = -EAGAIN;  
        goto exit;  
    }  
      
exit:  
    kfree(buf_tmp);  
    return (err?err:length);  
}  
  
static ssize_t eep_rw (char *buf, size_t count, int rw)  
{  
    int ret,err=0;  
    int length = count;  
    int seg;  
    char *buf_tmp=NULL, *buf_cur;  
      
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "###### eep_rw(), buf=%p, count=%d, rw=%d ######\n",  
            buf, count, rw);  
    #endif   
  
    /* Check param */  
    if (eep->cur_ptr + count > BNK_SIZE) {  
        printk(KERN_ALERT "EEPROM Try to %s over BNK_SIZE: \n"  
                "\tcur_ptr = %d, count = %d, cur_ptr+count = %d\n",  
                (rw?"write":"read"), eep->cur_ptr, count, eep->cur_ptr + count);  
        return -EINVAL;  
    }  
    if (rw != 0 && rw != 1) {  
        printk(KERN_ALERT "eep_rw() error, rw param value %d invalid.\n", rw);  
        return -EINVAL;  
    }  
  
    /* alloc memory for buf_tmp */  
    buf_tmp = kmalloc(count, GFP_KERNEL);  
    if (!buf_tmp) {  
        printk(KERN_ALERT "EEPROM kmalloc %d failed\n", count);  
        return -ENOMEM;  
    }  
    buf_cur = buf_tmp;  
  
    if (rw == 1)  
        copy_from_user(buf_tmp, (const char *)buf, count);  
  
    while(length > 0) {  
        seg = length>PG_SIZE? PG_SIZE: length;  
        #ifdef DEBUG_EEP   
        printk(KERN_ALERT "eep_rw(): %s: count = %d, cur_ptr = %d, seg = %d, length = %d\n",  
            (rw?"write":"read"), count, eep->cur_ptr, seg, length);  
        #endif   
        ret = rw_page(buf_cur, seg, rw);  
        if (ret<0) {  
            printk(KERN_ALERT "EEPROM %s error %d\n", (rw?"write":"read"), ret);  
            err = ret;  
            goto exit;  
        }  
        buf_cur += seg;  
        length -= seg;  
        eep->cur_ptr += seg;  
    }  
  
    if (rw == 0)  
        copy_to_user(buf, (const char *)buf_tmp, count);  
  
    #ifdef DEBUG_EEP_R   
    printk(KERN_ALERT "Max retrys record is %d\n", max_retrys_record);  
    #endif   
  
exit:  
    kfree(buf_tmp);  
    return (err?err:count);  
}  
  
static ssize_t eep_read (struct file *file, char __user *buf, size_t count, loff_t *ppos)  
{  
    return eep_rw(buf, count, 0);  
}  
  
static ssize_t eep_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)  
{  
    return eep_rw(buf, count, 1);  
}  
  
  
static struct file_operations eep_fops = {  
    .owner      = THIS_MODULE,  
    .open       = eep_open,  
    .release    = eep_release,  
    .llseek     = eep_llseek,  
    .read       = eep_read,  
    .write      = eep_write,  
};  
  
static const unsigned short normal[] =   
    { EEPROM_SLAVE_ADDR0, EEPROM_SLAVE_ADDR1,   
      EEPROM_SLAVE_ADDR2, EEPROM_SLAVE_ADDR3,   
      I2C_CLIENT_END };  
static const unsigned short ignore[] = { I2C_CLIENT_END };  
static const struct i2c_client_address_data eep_addr_data = {  
        .normal_i2c = normal,  
        .forces     = NULL,  
        .probe      = ignore,  
        .ignore     = ignore,  
};  
/* The higher version of Linux kernel is much more simple. */  
  
/* The device name this driver supported. 
 */  
static const struct i2c_device_id eep_24c08_id[] = {  
    { "eeprom-blk0", EEPROM_SLAVE_ADDR0 },  
    { "eeprom-blk1", EEPROM_SLAVE_ADDR1 },  
    { "eeprom-blk2", EEPROM_SLAVE_ADDR2 },  
    { "eeprom-blk3", EEPROM_SLAVE_ADDR3 },  
    { }  
};  
/* Well, there is some necessary to take a explaination. 
 * Because of the device 24C08 has 8K bits in it, it need 4 addresses. 
 * In the I2C protocal, the SLAVE_ADDR data has only 8 bit, which could only address 256 Bytes(2048 bits). 
 * So 24C08 has 4 different SLAVE_ADDR addresses: 0x50, 0x51, 0x52, 0x53.  
 * Each address is a part that has 2048 bits. Totally, 4 parts have 8192 bits(8K bits). 
*/  
  
MODULE_DEVICE_TABLE(i2c, eep_24c08_id);  
  
/* eep_probe - probe method for new-style driver model. 
 * @client: the i2c_client which already been register.  
 * @id:     the i2c_device_id which matchs. 
 
 * When dev and driver are both create and matched. This func will be called to init some specfic data. 
 * Usually, the device's initial step will be done here. 
 * Note: When a driver has N devices, the probe will be called N times. 
 *       For different device, different i2c_device_id will be provided for use in the probe function. 
*/  
static int __devinit eep_probe(struct i2c_client *client,  
                  const struct i2c_device_id *id)  
{  
    int i=0;      
      
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM probe\n");  
    #endif   
      
    while (eep_24c08_id[i].driver_data) {  
        if (client->addr == (unsigned short)eep_24c08_id[i].driver_data) {  
            #ifdef DEBUG_EEP   
            printk(KERN_ALERT "probe(): client->addr = 0x%02X, i = %d, eep_24c08_id[%d].driver_data = 0x%02X, eep_24c08_id[%d].name = %s\n",  
                client->addr, i, i, (unsigned int)eep_24c08_id[i].driver_data, i, eep_24c08_id[i].name);  
            #endif   
            eep->client[i] = client; /* Store the client into eep for further use. */  
                /* client can also be stored in file->p when open(). */  
        }  
        i++;  
    }  
  
    return 0;  
}  
  
static int __devexit eep_remove(struct i2c_client *client)  
{  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM remove\n");  
    #endif   
      
    return 0;  
}  
  
/* eep_detect - detect method is called when there is really some device on '@kind' address.  
 @i2c_client:   the temp client device which is given to detect func's use.  
            It is only temp which has not been register. 
 @kind:  indicate the number in i2c_client_address_data for each possible member.  
        only for forces device. Others condition is -1. 
 @i2c_board_info:   the specific infomation's structure to store device's info. 
 
 * This function was called before the real i2c_client is create.  
 * Some special device need a initializion before read/write it, do the init here. 
 * Init the client's infomation, and it need to fill at least the name  
 * of i2c_board_info param for registering which is also the name of client device. 
 * Notice: Its addr member has already beed set. 
 */  
int eep_detect(struct i2c_client *client, int kind, struct i2c_board_info *bd_info)  
{  
    int i=0;  
  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM detect\n");  
    #endif   
      
    /* There is no need to take action of detect on I2C bus. */  
  
    while (eep_24c08_id[i].driver_data) {  
        if (client->addr == (unsigned short)eep_24c08_id[i].driver_data) {  
            strlcpy(bd_info->type, eep_24c08_id[i].name, I2C_NAME_SIZE);   
                /* In fact, giving the name is the only thing need to do. */  
            bd_info->flags = 0;  
            #ifdef DEBUG_EEP   
            printk(KERN_ALERT "detect(): i = %d, client->addr = 0x%02X, bd_info->type = %s\n",  
                i, client->addr, bd_info->type);  
            #endif   
        }  
        i++;  
    }  
  
    /* platform_data should be study later */  
  
    return 0;  
}  
  
static struct i2c_driver eep_driver = {  
    .driver = {  
        .name   = "eeprom-driver",  /* Name */  
        .owner  = THIS_MODULE,  
    },  
    .class          = 1,  
    .probe          = eep_probe,  
    .remove         = __devexit_p(eep_remove),  
    .id_table       = eep_24c08_id,  
    .detect         = eep_detect,  
    .address_data       = &eep_addr_data,  
};  
  
static int __init eeprom_init(void)  
{  
    int err=0;  
      
    eep = kmalloc(sizeof(struct eep_24c08), GFP_KERNEL);  
    if (!eep) {  
        err = -ENOMEM;  
        goto exit;  
    }  
  
    /* Get and register the cdev for eeprom */  
    eep->dev_num = MKDEV(EEP_MAJOR,0);  
    if (register_chrdev_region(eep->dev_num, 1, "eeprom") <0 ) {  
        printk(KERN_ALERT "Can't register device\n");  
        goto exit_free;  
    }  
      
    cdev_init(&eep->eep_cdev, &eep_fops);  
    eep->eep_cdev.owner = THIS_MODULE;  
    eep->eep_cdev.ops = &eep_fops;  
    if (cdev_add(&eep->eep_cdev, eep->dev_num, 1)) {  
        printk(KERN_ALERT "Failed to add cdev of EEPROM\n");  
        goto exit_unregister;  
    }  
  
    /* Register i2c_driver to i2c core */  
    err = i2c_add_driver(&eep_driver);  
    if (err) {  
        printk(KERN_ALERT "Registering I2C driver of EEPROM failed, errno is %d\n", err);  
        goto exit_del_cdev;  
    }  
  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM Driver Initialized.\n");  
    #endif   
      
    return 0;  
  
exit_del_cdev:  
    cdev_del(&eep->eep_cdev);  
exit_unregister:  
    unregister_chrdev_region(eep->dev_num, 1);  
exit_free:  
    kfree(eep);  
exit:  
    return err;   
}  
  
static void __exit eeprom_exit(void)  
{  
    i2c_del_driver(&eep_driver);  
    cdev_del(&eep->eep_cdev);  
    unregister_chrdev_region(eep->dev_num, 1);  
    kfree(eep);  
  
    #ifdef DEBUG_EEP   
    printk(KERN_ALERT "EEPROM Driver Removed.\n");  
    #endif   
}  
  
module_init(eeprom_init);  
module_exit(eeprom_exit);  
  
MODULE_DESCRIPTION("EEPROM driver for 24C08 and similar chips");  
MODULE_LICENSE("GPL");  

来源: http://blog.csdn.net/terminalnt/article/details/6582577
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值