一、M24256简介
1、256 Kbit Serial I²C Bus EEPROM
2、Compatible with I2C Extended Addressing
3、Two Wire I2C Serial Interface
4、Supports 400 kHz Protocol
5、 Hardware Write Control
6、 BYTE and PAGE WRITE (up to 64 Bytes)
7、 RANDOM and SEQUENTIAL READ Modes
8、 Automatic Address Incrementing
9、管脚定义
二、M24256读写时序
1、写page时序
2、读page时序
三、i2c驱动
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/smp_lock.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/proc_fs.h>
- #include <linux/i2c.h>
- #include <linux/i2c-dev.h>
- #include <linux/platform_device.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
- #define EEPROM_MAJOR 247
- #define EEPROM_I2C_MINORS 1
- #define EEPROM_NAME "eeprom"
- #define I2C_EEPROM_ADDR (0x54 << 1)
- #define I2C_EEPROM_ID 777
- #define EEPROM_BANK_SIZE 64
- #define I2C_NAME(x) (x)->name
- #define SET_EEPROM_PAGE_ADDR 1
- #define EEPROM_MAX_ADDR 0x7fff /* 256Kbit = 32Kbyte */
- static DEFINE_SPINLOCK(i2c_dev_array_lock);
- static struct i2c_driver eeprom_driver;
- static struct i2c_dev *i2c_dev_array[EEPROM_I2C_MINORS]= {NULL};
- struct i2c_dev
- {
- int minor;
- struct i2c_adapter *adap;
- struct i2c_client *client;
- atomic_t busy;
- unsigned short page_address;
- };
- static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
- {
- struct i2c_dev *i2c_dev = NULL;
- if(adap == NULL)
- {
- printk("invalid parameter,adap == NULL\n");
- return NULL;
- }
- if( adap->nr >= EEPROM_I2C_MINORS)
- {
- printk("invalid parameter,adap->nr = %d\n",adap->nr);
- return NULL;
- }
- i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
- if(i2c_dev == NULL)
- {
- printk("kzalloc i2c_dev error \n");
- return NULL;
- }
- spin_lock(&i2c_dev_array_lock);
- if (i2c_dev_array[adap->nr] != NULL)
- {
- spin_unlock(&i2c_dev_array_lock);
- printk("eeprom already has a device assigned to this adapter\n");
- goto exit;
- }
- i2c_dev->minor = adap->nr;
- i2c_dev_array[adap->nr] = i2c_dev;
- spin_unlock(&i2c_dev_array_lock);
- return i2c_dev;
- exit:
- kfree(i2c_dev);
- return NULL;
- }
- static int eeprom_detect_client(struct i2c_adapter *adapter, int address,int kind)
- {
- struct i2c_client *client = NULL;
- struct i2c_dev *i2c_dev = NULL;
- char *dname = NULL;
- int ret = -1;
- printk("detecte eeprom client on address 0x%x\n",address << 1);
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
- {
- printk("i2c check functionality failed\n");
- return -1;
- }
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- {
- return -ENOMEM;
- }
- client->addr = address;
- client->adapter = adapter;
- client->driver = &eeprom_driver;
- client->flags = 0;
- if (client->addr == (I2C_EEPROM_ADDR >> 1))
- {
- dname = EEPROM_NAME;
- }
- else
- {
- printk(" detect client: i2c addr error addr = 0x%X \n",client->addr);
- kfree(client);
- return -1;
- }
- strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client)));
- ret = i2c_attach_client(client);
- if (ret != 0)
- {
- printk(" detect client: i2c_attach_client(),ret = %d \n",ret);
- kfree(client);
- return ret;
- }
- i2c_dev = get_free_i2c_dev(adapter);
- if (i2c_dev == NULL)
- {
- kfree(client);
- return -1;
- }
- printk("eeprom adapter [%s] registered as minor %d\n",adapter->name, i2c_dev->minor);
- i2c_dev->adap = adapter;
- i2c_dev->client = client;
- atomic_set(&(i2c_dev->busy),1);
- return 0;
- }
- static unsigned short normal_i2c[] = {I2C_EEPROM_ADDR >> 1, I2C_CLIENT_END};
- static unsigned short ignore = I2C_CLIENT_END;
- static struct i2c_client_address_data addr_data =
- {
- .normal_i2c = normal_i2c,
- .probe = &ignore,
- .ignore = &ignore,
- };
- static int return_i2c_dev(struct i2c_dev *i2c_dev)
- {
- if(i2c_dev == NULL)
- {
- printk("invalid parameter,i2c_dev == NULL\n");
- return -1;
- }
- if(i2c_dev->minor >= EEPROM_I2C_MINORS)
- {
- printk("invalid parameter,i2c_dev->minor = %d\n",i2c_dev->minor);
- return -1;
- }
- spin_lock(&i2c_dev_array_lock);
- i2c_dev_array[i2c_dev->minor] = NULL;
- spin_unlock(&i2c_dev_array_lock);
- kfree(i2c_dev);
- return 0;
- }
- static struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap)
- {
- struct i2c_dev *i2c_dev = NULL;
- if(adap == NULL)
- {
- printk("invalid parameter,adap == NULL\n");
- return NULL;
- }
- if(adap->nr >= EEPROM_I2C_MINORS)
- {
- printk("invalid parameter,adap->nr = %d\n",adap->nr);
- return NULL;
- }
- spin_lock(&i2c_dev_array_lock);
- if ((i2c_dev_array[adap->nr] != NULL) && (i2c_dev_array[adap->nr]->adap == adap))
- {
- i2c_dev = i2c_dev_array[adap->nr];
- }
- spin_unlock(&i2c_dev_array_lock);
- return i2c_dev;
- }
- static int eeprom_attach_adapter(struct i2c_adapter *adap)
- {
- printk("start probe for adapter %s (0x%x)\n",I2C_NAME(adap), adap->nr);
- return i2c_probe(adap, &addr_data, &eeprom_detect_client);
- }
- static int eeprom_detach_client(struct i2c_client *client)
- {
- struct i2c_dev *i2c_dev = NULL;
- int ret = -1;
- if(client == NULL)
- {
- printk("invalid parameter,client == NULL\n");
- return -1;
- }
- i2c_dev = i2c_dev_get_by_adapter(client->adapter);
- if(i2c_dev == NULL)
- {
- printk("invalid parameter,i2c_dev == NULL\n");
- return -1;
- }
- return_i2c_dev(i2c_dev);
- ret = i2c_detach_client(client);
- if (ret != 0)
- {
- printk("i2c_detach_client() error,ret == %d\n",ret);
- return ret;
- }
- kfree(client);
- printk("eeprom detach client success\n");
- return 0;
- }
- static struct i2c_driver eeprom_driver =
- {
- .driver =
- {
- .name = "eeprom_driver",
- },
- .id = I2C_EEPROM_ID,
- .attach_adapter = eeprom_attach_adapter,
- .detach_client = eeprom_detach_client,
- };
- static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
- {
- struct i2c_dev *i2c_dev = NULL;
- if(index >= EEPROM_I2C_MINORS)
- {
- printk("invalid parameter,index = %d\n",index);
- return NULL;
- }
- spin_lock(&i2c_dev_array_lock);
- i2c_dev = i2c_dev_array[index];
- spin_unlock(&i2c_dev_array_lock);
- return i2c_dev;
- }
- static inline int eeprom_read_byte(struct i2c_client *client, u8 reg)
- {
- if (client == NULL)
- {
- printk("invalid parameter,client == NULL\n");
- return -1;
- }
- return i2c_smbus_read_byte_data(client, reg);
- }
- static inline int eeprom_write_byte(struct i2c_client *client, u8 reg, u8 value)
- {
- int ret = -1;
- if (client == NULL)
- {
- printk("invalid parameter,client == NULL\n");
- return -1;
- }
- ret = i2c_smbus_write_byte_data(client, reg, value);
- if(ret != 0)
- {
- printk("eeprom write addr:0x%x,name:%s,reg:0x%x,value0x%x\n",client->addr,client->name,reg,value);
- }
- return ret;
- }
- static int eeprom_open(struct inode *node,struct file *file)
- {
- unsigned int minor = iminor(node);
- struct i2c_dev *i2c_dev = NULL;
- printk("eeprom open\n");
- i2c_dev = i2c_dev_get_by_minor(minor);
- i2c_dev->page_address = 0;
- set_wc_bit_high(); /* 写保护信号拉高 */
- file->private_data = i2c_dev;
- if (atomic_dec_and_test(&i2c_dev->busy) == 0)
- {
- atomic_inc(&i2c_dev->busy);
- return -EBUSY; /* already open */
- }
- return 0;
- }
- static int eeprom_release(struct inode *node,struct file *file)
- {
- struct i2c_dev * i2c_dev = (struct i2c_dev *)file->private_data;
- if(i2c_dev == NULL)
- {
- printk("invalid parameter,i2c_dev == NULL\n");
- return -1;
- }
- atomic_inc(&i2c_dev->busy);
- file->private_data = NULL;
- printk("eeprom release\n");
- return 0;
- }
- static ssize_t eeprom_read(struct file *filp,char *buf, size_t count, loff_t *offset)
- {
- struct i2c_dev *i2c_dev = (struct i2c_dev *)filp->private_data;
- struct i2c_client *client = i2c_dev->client;
- int ret = -1;
- char my_buf[EEPROM_BANK_SIZE] = {0,};
- char message[2] = {0,};
- if(i2c_dev->page_address + count > EEPROM_MAX_ADDR)
- {
- printk("eeprom read address range beyond\n");
- i2c_dev->page_address = 0;
- return -1;
- }
- set_wc_bit_low(); /* 写保护信号拉低 */
- mdelay(1);
- message[0] = (u8)(i2c_dev->page_address >> 8);
- message[1] = (u8)(i2c_dev->page_address);
- i2c_master_send(client, message, 2);
- i2c_master_recv(client, my_buf, count);
- set_wc_bit_high(); /* 写保护信号拉高 */
- copy_to_user(buf, (void *)my_buf, count);
- return count;
- }
- static ssize_t eeprom_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
- {
- struct i2c_dev *i2c_dev = (struct i2c_dev *)file->private_data;
- struct i2c_client *client = i2c_dev->client;
- int ret = -1;
- char message[EEPROM_BANK_SIZE + 2] = {0,};
- if(i2c_dev->page_address + count > EEPROM_MAX_ADDR)
- {
- printk("eeprom write address range beyond\n");
- i2c_dev->page_address = 0;
- return -1;
- }
- if(count > EEPROM_BANK_SIZE)
- {
- count = EEPROM_BANK_SIZE;
- }
- message[0] = (u8)(i2c_dev->page_address >> 8);
- message[1] = (u8)(i2c_dev->page_address);
- ret = copy_from_user(&message[2], (void *)buf, count);
- if(ret != 0)
- {
- printk("eeprom write copy_from_user failed\n");
- return -1;
- }
- set_wc_bit_high(); /* 写保护信号拉低 */
- mdelay(1);
- i2c_master_send(client, message, count + 2);
- mdelay(1);
- set_wc_bit_high(); /* 写保护信号拉高 */
- return count;
- }
- static int eeprom_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
- {
- struct i2c_dev * i2c_dev = (struct i2c_dev *)file->private_data;
- if(i2c_dev == NULL)
- {
- printk("invalid parameter,i2c_dev == NULL\n");
- return -1;
- }
- switch(cmd)
- {
- case SET_EEPROM_PAGE_ADDR:
- if(arg > EEPROM_MAX_ADDR)
- {
- printk("eeprom address range beyond\n");
- i2c_dev->page_address = 0;
- return -1;
- }
- else
- {
- i2c_dev->page_address = (unsigned short)arg;
- }
- break;
- default:
- printk("eeprom cmd error!\n");
- return -1;
- }
- return 0;
- }
- static struct file_operations eeprom_fops =
- {
- .owner = THIS_MODULE,
- .open = eeprom_open,
- .release = eeprom_release,
- .read = eeprom_read,
- .write = eeprom_write,
- .ioctl = eeprom_ioctl,
- };
- static int __init eeprom_init(void)
- {
- int ret = -1;
- ret = register_chrdev(EEPROM_MAJOR, EEPROM_NAME, &eeprom_fops);
- if (ret != 0)
- {
- printk("%s: can't get major %d", EEPROM_NAME, EEPROM_MAJOR);
- goto exit;
- }
- memset(i2c_dev_array, 0 ,sizeof(i2c_dev_array));
- ret = i2c_add_driver(&eeprom_driver);
- if (ret != 0)
- {
- printk("i2c add eeprom_driver error!\n");
- goto unregister_chrdev;
- }
- return 0;
- unregister_chrdev:
- unregister_chrdev(EEPROM_MAJOR, EEPROM_NAME);
- exit:
- return ret;
- }
- static void __exit eeprom_exit(void)
- {
- i2c_del_driver(&eeprom_driver);
- unregister_chrdev(EEPROM_MAJOR, EEPROM_NAME);
- }
- MODULE_DESCRIPTION("eeprom driver");
- MODULE_LICENSE("GPL");
- module_init(eeprom_init);
- module_exit(eeprom_exit);