📄 eeprom.c
字号:
/*!******************************************************************************!*! Implements an interface for i2c compatible eeproms to run under linux.*! Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustents by*! Johan.Adolfsson@axis.com*!*! Probing results:*! 8k or not is detected (the assumes 2k or 16k)*! 2k or 16k detected using test reads and writes.*!*!------------------------------------------------------------------------*! HISTORY*!*! DATE NAME CHANGES*! ---- ---- -------*! Aug 28 1999 Edgar Iglesias Initial Version*! Aug 31 1999 Edgar Iglesias Allow simultaneous users.*! Sep 03 1999 Edgar Iglesias Updated probe.*! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted*! in the spin-lock.*!*! $Log: eeprom.c,v $*! Revision 1.10 2003/09/11 07:29:48 starvik*! Merge of Linux 2.6.0-test5*!*! Revision 1.9 2003/07/04 08:27:37 starvik*! Merge of Linux 2.5.74*!*! Revision 1.8 2003/04/09 05:20:47 starvik*! Merge of Linux 2.5.67*!*! Revision 1.6 2003/02/10 07:19:28 starvik*! Removed misplaced ;*!*! Revision 1.5 2002/12/11 13:13:57 starvik*! Added arch/ to v10 specific includes*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)*!*! Revision 1.4 2002/11/20 11:56:10 starvik*! Merge of Linux 2.5.48*!*! Revision 1.3 2002/11/18 13:16:06 starvik*! Linux 2.5 port of latest 2.4 drivers*!*! Revision 1.8 2001/06/15 13:24:29 jonashg*! * Added verification of pointers from userspace in read and write.*! * Made busy counter volatile.*! * Added define for inital write delay.*! * Removed warnings by using loff_t instead of unsigned long.*!*! Revision 1.7 2001/06/14 15:26:54 jonashg*! Removed test because condition is always true.*!*! Revision 1.6 2001/06/14 15:18:20 jonashg*! Kb -> kB (makes quite a difference if you don't know if you have 2k or 16k).*!*! Revision 1.5 2001/06/14 14:39:51 jonashg*! Forgot to use name when registering the driver.*!*! Revision 1.4 2001/06/14 14:35:47 jonashg*! * Gave driver a name and used it in printk's.*! * Cleanup.*!*! Revision 1.3 2001/03/19 16:04:46 markusl*! Fixed init of fops struct*!*! Revision 1.2 2001/03/19 10:35:07 markusl*! 2.4 port of eeprom driver*!*! Revision 1.8 2000/05/18 10:42:25 edgar*! Make sure to end write cycle on _every_ write*!*! Revision 1.7 2000/01/17 17:41:01 johana*! Adjusted probing and return -ENOSPC when writing outside EEPROM*!*! Revision 1.6 2000/01/17 15:50:36 johana*! Added adaptive timing adjustments and fixed autoprobing for 2k and 16k(?)*! EEPROMs*!*! Revision 1.5 1999/09/03 15:07:37 edgar*! Added bail-out check to the spinlock*!*! Revision 1.4 1999/09/03 12:11:17 bjornw*! Proper atomicity (need to use spinlocks, not if's). users -> busy.*!*!*! (c) 1999 Axis Communications AB, Lund, Sweden*!*****************************************************************************/#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include "i2c.h"#define D(x) /* If we should use adaptive timing or not: *///#define EEPROM_ADAPTIVE_TIMING #define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */#define EEPROM_MINOR_NR 0/* Empirical sane initial value of the delay, the value will be adapted to * what the chip needs when using EEPROM_ADAPTIVE_TIMING. */#define INITIAL_WRITEDELAY_US 4000#define MAX_WRITEDELAY_US 10000 /* 10 ms according to spec for 2KB EEPROM *//* This one defines how many times to try when eeprom fails. */#define EEPROM_RETRIES 10#define EEPROM_2KB (2 * 1024)/*#define EEPROM_4KB (4 * 1024)*/ /* Exists but not used in Axis products */#define EEPROM_8KB (8 * 1024 - 1 ) /* Last byte has write protection bit */#define EEPROM_16KB (16 * 1024)#define i2c_delay(x) udelay(x)/* * This structure describes the attached eeprom chip. * The values are probed for. */struct eeprom_type{ unsigned long size; unsigned long sequential_write_pagesize; unsigned char select_cmd; unsigned long usec_delay_writecycles; /* Min time between write cycles (up to 10ms for some models) */ unsigned long usec_delay_step; /* For adaptive algorithm */ int adapt_state; /* 1 = To high , 0 = Even, -1 = To low */ /* this one is to keep the read/write operations atomic */ wait_queue_head_t wait_q; volatile int busy; int retry_cnt_addr; /* Used to keep track of number of retries for adaptive timing adjustments */ int retry_cnt_read;};static int eeprom_open(struct inode * inode, struct file * file);static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig);static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off);static ssize_t eeprom_write(struct file * file, const char * buf, size_t count, loff_t *off);static int eeprom_close(struct inode * inode, struct file * file);static int eeprom_address(unsigned long addr);static int read_from_eeprom(char * buf, int count);static int eeprom_write_buf(loff_t addr, const char * buf, int count);static int eeprom_read_buf(loff_t addr, char * buf, int count);static void eeprom_disable_write_protect(void);static const char eeprom_name[] = "eeprom";/* chip description */static struct eeprom_type eeprom;/* This is the exported file-operations structure for this device. */struct file_operations eeprom_fops ={ .llseek = eeprom_lseek, .read = eeprom_read, .write = eeprom_write, .open = eeprom_open, .release = eeprom_close};/* eeprom init call. Probes for different eeprom models. */int __init eeprom_init(void){ init_waitqueue_head(&eeprom.wait_q); eeprom.busy = 0;#ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE#define EETEXT "Found"#else#define EETEXT "Assuming"#endif if (register_chrdev(EEPROM_MAJOR_NR, eeprom_name, &eeprom_fops)) { printk(KERN_INFO "%s: unable to get major %d for eeprom device\n", eeprom_name, EEPROM_MAJOR_NR); return -1; } printk("EEPROM char device v0.3, (c) 2000 Axis Communications AB\n"); /* * Note: Most of this probing method was taken from the printserver (5470e) * codebase. It did not contain a way of finding the 16kB chips * (M24128 or variants). The method used here might not work * for all models. If you encounter problems the easiest way * is probably to define your model within #ifdef's, and hard- * code it. */ eeprom.size = 0; eeprom.usec_delay_writecycles = INITIAL_WRITEDELAY_US; eeprom.usec_delay_step = 128; eeprom.adapt_state = 0; #ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE i2c_start(); i2c_outbyte(0x80); if(!i2c_getack()) { /* It's not 8k.. */ int success = 0; unsigned char buf_2k_start[16]; /* Im not sure this will work... :) */ /* assume 2kB, if failure go for 16kB */ /* Test with 16kB settings.. */ /* If it's a 2kB EEPROM and we address it outside it's range * it will mirror the address space: * 1. We read two locations (that are mirrored), * if the content differs * it's a 16kB EEPROM. * 2. if it doesn't differ - write different value to one of the locations, * check the other - if content still is the same it's a 2k EEPROM, * restore original data. */#define LOC1 8#define LOC2 (0x1fb) /*1fb, 3ed, 5df, 7d1 */ /* 2k settings */ i2c_stop(); eeprom.size = EEPROM_2KB; eeprom.select_cmd = 0xA0; eeprom.sequential_write_pagesize = 16; if( eeprom_read_buf( 0, buf_2k_start, 16 ) == 16 ) { D(printk("2k start: '%16.16s'\n", buf_2k_start)); } else { printk(KERN_INFO "%s: Failed to read in 2k mode!\n", eeprom_name); } /* 16k settings */ eeprom.size = EEPROM_16KB; eeprom.select_cmd = 0xA0; eeprom.sequential_write_pagesize = 64; { unsigned char loc1[4], loc2[4], tmp[4]; if( eeprom_read_buf(LOC2, loc2, 4) == 4) { if( eeprom_read_buf(LOC1, loc1, 4) == 4) { D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n", LOC1, loc1, LOC2, loc2));#if 0 if (memcmp(loc1, loc2, 4) != 0 ) { /* It's 16k */ printk(KERN_INFO "%s: 16k detected in step 1\n", eeprom_name); eeprom.size = EEPROM_16KB; success = 1; } else#endif { /* Do step 2 check */ /* Invert value */ loc1[0] = ~loc1[0]; if (eeprom_write_buf(LOC1, loc1, 1) == 1) { /* If 2k EEPROM this write will actually write 10 bytes * from pos 0 */ D(printk("1 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n", LOC1, loc1, LOC2, loc2)); if( eeprom_read_buf(LOC1, tmp, 4) == 4) { D(printk("2 loc1: (%i) '%4.4s' tmp '%4.4s'\n", LOC1, loc1, tmp)); if (memcmp(loc1, tmp, 4) != 0 ) { printk(KERN_INFO "%s: read and write differs! Not 16kB\n", eeprom_name); loc1[0] = ~loc1[0]; if (eeprom_write_buf(LOC1, loc1, 1) == 1) { success = 1; } else { printk(KERN_INFO "%s: Restore 2k failed during probe," " EEPROM might be corrupt!\n", eeprom_name); } i2c_stop(); /* Go to 2k mode and write original data */ eeprom.size = EEPROM_2KB; eeprom.select_cmd = 0xA0; eeprom.sequential_write_pagesize = 16; if( eeprom_write_buf(0, buf_2k_start, 16) == 16) { } else { printk(KERN_INFO "%s: Failed to write back 2k start!\n", eeprom_name); } eeprom.size = EEPROM_2KB; } } if(!success) { if( eeprom_read_buf(LOC2, loc2, 1) == 1) { D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n", LOC1, loc1, LOC2, loc2)); if (memcmp(loc1, loc2, 4) == 0 ) { /* Data the same, must be mirrored -> 2k */ /* Restore data */ printk(KERN_INFO "%s: 2k detected in step 2\n", eeprom_name); loc1[0] = ~loc1[0]; if (eeprom_write_buf(LOC1, loc1, 1) == 1) { success = 1; } else { printk(KERN_INFO "%s: Restore 2k failed during probe," " EEPROM might be corrupt!\n", eeprom_name); } eeprom.size = EEPROM_2KB; } else { printk(KERN_INFO "%s: 16k detected in step 2\n", eeprom_name); loc1[0] = ~loc1[0]; /* Data differs, assume 16k */ /* Restore data */ if (eeprom_write_buf(LOC1, loc1, 1) == 1) { success = 1; } else { printk(KERN_INFO "%s: Restore 16k failed during probe," " EEPROM might be corrupt!\n", eeprom_name); } eeprom.size = EEPROM_16KB; } } } } } /* read LOC1 */ } /* address LOC1 */ if (!success) { printk(KERN_INFO "%s: Probing failed!, using 2KB!\n", eeprom_name); eeprom.size = EEPROM_2KB; } } /* read */ } } else { i2c_outbyte(0x00); if(!i2c_getack()) { /* No 8k */ eeprom.size = EEPROM_2KB; } else { i2c_start(); i2c_outbyte(0x81); if (!i2c_getack()) { eeprom.size = EEPROM_2KB; } else { /* It's a 8kB */ i2c_inbyte(); eeprom.size = EEPROM_8KB; } } } i2c_stop();#elif defined(CONFIG_ETRAX_I2C_EEPROM_16KB) eeprom.size = EEPROM_16KB;#elif defined(CONFIG_ETRAX_I2C_EEPROM_8KB) eeprom.size = EEPROM_8KB;#elif defined(CONFIG_ETRAX_I2C_EEPROM_2KB) eeprom.size = EEPROM_2KB;#endif switch(eeprom.size) { case (EEPROM_2KB): printk("%s: " EETEXT " i2c compatible 2kB eeprom.\n", eeprom_name); eeprom.sequential_write_pagesize = 16; eeprom.select_cmd = 0xA0; break; case (EEPROM_8KB): printk("%s: " EETEXT " i2c compatible 8kB eeprom.\n", eeprom_name); eeprom.sequential_write_pagesize = 16; eeprom.select_cmd = 0x80; break; case (EEPROM_16KB): printk("%s: " EETEXT " i2c compatible 16kB eeprom.\n", eeprom_name); eeprom.sequential_write_pagesize = 64; eeprom.select_cmd = 0xA0; break; default: eeprom.size = 0; printk("%s: Did not find a supported eeprom\n", eeprom_name); break; } eeprom_disable_write_protect(); return 0;}/* Opens the device. */static int eeprom_open(struct inode * inode, struct file * file){ if(MINOR(inode->i_rdev) != EEPROM_MINOR_NR) return -ENXIO; if(MAJOR(inode->i_rdev) != EEPROM_MAJOR_NR) return -ENXIO; if( eeprom.size > 0 ) { /* OK */ return 0; } /* No EEprom found */ return -EFAULT;}/* Changes the current file position. */static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig){/* * orig 0: position from begning of eeprom * orig 1: relative from current position * orig 2: position from last eeprom address */ switch (orig) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -