diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/cris/arch-v10/drivers/eeprom.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/cris/arch-v10/drivers/eeprom.c')
-rw-r--r-- | arch/cris/arch-v10/drivers/eeprom.c | 945 |
1 files changed, 945 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c new file mode 100644 index 00000000000..316ca15d680 --- /dev/null +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -0,0 +1,945 @@ +/*!***************************************************************************** +*! +*! 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) + { + case 0: + file->f_pos = offset; + break; + case 1: + file->f_pos += offset; + break; + case 2: + file->f_pos = eeprom.size - offset; + break; + default: + return -EINVAL; + } + + /* truncate position */ + if (file->f_pos < 0) + { + file->f_pos = 0; + return(-EOVERFLOW); + } + + if (file->f_pos >= eeprom.size) + { + file->f_pos = eeprom.size - 1; + return(-EOVERFLOW); + } + + return ( file->f_pos ); +} + +/* Reads data from eeprom. */ + +static int eeprom_read_buf(loff_t addr, char * buf, int count) +{ + struct file f; + + f.f_pos = addr; + return eeprom_read(&f, buf, count, &addr); +} + + + +/* Reads data from eeprom. */ + +static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off) +{ + int read=0; + unsigned long p = file->f_pos; + + unsigned char page; + + if(p >= eeprom.size) /* Address i 0 - (size-1) */ + { + return -EFAULT; + } + + while(eeprom.busy) + { + interruptible_sleep_on(&eeprom.wait_q); + + /* bail out if we get interrupted */ + if (signal_pending(current)) + return -EINTR; + + } + eeprom.busy++; + + page = (unsigned char) (p >> 8); + + if(!eeprom_address(p)) + { + printk(KERN_INFO "%s: Read failed to address the eeprom: " + "0x%08X (%i) page: %i\n", eeprom_name, (int)p, (int)p, page); + i2c_stop(); + + /* don't forget to wake them up */ + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + return -EFAULT; + } + + if( (p + count) > eeprom.size) + { + /* truncate count */ + count = eeprom.size - p; + } + + /* stop dummy write op and initiate the read op */ + i2c_start(); + + /* special case for small eeproms */ + if(eeprom.size < EEPROM_16KB) + { + i2c_outbyte( eeprom.select_cmd | 1 | (page << 1) ); + } + + /* go on with the actual read */ + read = read_from_eeprom( buf, count); + + if(read > 0) + { + file->f_pos += read; + } + + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + return read; +} + +/* Writes data to eeprom. */ + +static int eeprom_write_buf(loff_t addr, const char * buf, int count) +{ + struct file f; + + f.f_pos = addr; + + return eeprom_write(&f, buf, count, &addr); +} + + +/* Writes data to eeprom. */ + +static ssize_t eeprom_write(struct file * file, const char * buf, size_t count, + loff_t *off) +{ + int i, written, restart=1; + unsigned long p; + + if (!access_ok(VERIFY_READ, buf, count)) + { + return -EFAULT; + } + + while(eeprom.busy) + { + interruptible_sleep_on(&eeprom.wait_q); + /* bail out if we get interrupted */ + if (signal_pending(current)) + return -EINTR; + } + eeprom.busy++; + for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++) + { + restart = 0; + written = 0; + p = file->f_pos; + + + while( (written < count) && (p < eeprom.size)) + { + /* address the eeprom */ + if(!eeprom_address(p)) + { + printk(KERN_INFO "%s: Write failed to address the eeprom: " + "0x%08X (%i) \n", eeprom_name, (int)p, (int)p); + i2c_stop(); + + /* don't forget to wake them up */ + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + return -EFAULT; + } +#ifdef EEPROM_ADAPTIVE_TIMING + /* Adaptive algorithm to adjust timing */ + if (eeprom.retry_cnt_addr > 0) + { + /* To Low now */ + D(printk(">D=%i d=%i\n", + eeprom.usec_delay_writecycles, eeprom.usec_delay_step)); + + if (eeprom.usec_delay_step < 4) + { + eeprom.usec_delay_step++; + eeprom.usec_delay_writecycles += eeprom.usec_delay_step; + } + else + { + + if (eeprom.adapt_state > 0) + { + /* To Low before */ + eeprom.usec_delay_step *= 2; + if (eeprom.usec_delay_step > 2) + { + eeprom.usec_delay_step--; + } + eeprom.usec_delay_writecycles += eeprom.usec_delay_step; + } + else if (eeprom.adapt_state < 0) + { + /* To High before (toggle dir) */ + eeprom.usec_delay_writecycles += eeprom.usec_delay_step; + if (eeprom.usec_delay_step > 1) + { + eeprom.usec_delay_step /= 2; + eeprom.usec_delay_step--; + } + } + } + + eeprom.adapt_state = 1; + } + else + { + /* To High (or good) now */ + D(printk("<D=%i d=%i\n", + eeprom.usec_delay_writecycles, eeprom.usec_delay_step)); + + if (eeprom.adapt_state < 0) + { + /* To High before */ + if (eeprom.usec_delay_step > 1) + { + eeprom.usec_delay_step *= 2; + eeprom.usec_delay_step--; + + if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step) + { + eeprom.usec_delay_writecycles -= eeprom.usec_delay_step; + } + } + } + else if (eeprom.adapt_state > 0) + { + /* To Low before (toggle dir) */ + if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step) + { + eeprom.usec_delay_writecycles -= eeprom.usec_delay_step; + } + if (eeprom.usec_delay_step > 1) + { + eeprom.usec_delay_step /= 2; + eeprom.usec_delay_step--; + } + + eeprom.adapt_state = -1; + } + + if (eeprom.adapt_state > -100) + { + eeprom.adapt_state--; + } + else + { + /* Restart adaption */ + D(printk("#Restart\n")); + eeprom.usec_delay_step++; + } + } +#endif /* EEPROM_ADAPTIVE_TIMING */ + /* write until we hit a page boundary or count */ + do + { + i2c_outbyte(buf[written]); + if(!i2c_getack()) + { + restart=1; + printk(KERN_INFO "%s: write error, retrying. %d\n", eeprom_name, i); + i2c_stop(); + break; + } + written++; + p++; + } while( written < count && ( p % eeprom.sequential_write_pagesize )); + + /* end write cycle */ + i2c_stop(); + i2c_delay(eeprom.usec_delay_writecycles); + } /* while */ + } /* for */ + + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + if (written == 0 && file->f_pos >= eeprom.size){ + return -ENOSPC; + } + file->f_pos += written; + return written; +} + +/* Closes the device. */ + +static int eeprom_close(struct inode * inode, struct file * file) +{ + /* do nothing for now */ + return 0; +} + +/* Sets the current address of the eeprom. */ + +static int eeprom_address(unsigned long addr) +{ + int i; + unsigned char page, offset; + + page = (unsigned char) (addr >> 8); + offset = (unsigned char) addr; + + for(i = 0; i < EEPROM_RETRIES; i++) + { + /* start a dummy write for addressing */ + i2c_start(); + + if(eeprom.size == EEPROM_16KB) + { + i2c_outbyte( eeprom.select_cmd ); + i2c_getack(); + i2c_outbyte(page); + } + else + { + i2c_outbyte( eeprom.select_cmd | (page << 1) ); + } + if(!i2c_getack()) + { + /* retry */ + i2c_stop(); + /* Must have a delay here.. 500 works, >50, 100->works 5th time*/ + i2c_delay(MAX_WRITEDELAY_US / EEPROM_RETRIES * i); + /* The chip needs up to 10 ms from write stop to next start */ + + } + else + { + i2c_outbyte(offset); + + if(!i2c_getack()) + { + /* retry */ + i2c_stop(); + } + else + break; + } + } + + + eeprom.retry_cnt_addr = i; + D(printk("%i\n", eeprom.retry_cnt_addr)); + if(eeprom.retry_cnt_addr == EEPROM_RETRIES) + { + /* failed */ + return 0; + } + return 1; +} + +/* Reads from current address. */ + +static int read_from_eeprom(char * buf, int count) +{ + int i, read=0; + + for(i = 0; i < EEPROM_RETRIES; i++) + { + if(eeprom.size == EEPROM_16KB) + { + i2c_outbyte( eeprom.select_cmd | 1 ); + } + + if(i2c_getack()) + { + break; + } + } + + if(i == EEPROM_RETRIES) + { + printk(KERN_INFO "%s: failed to read from eeprom\n", eeprom_name); + i2c_stop(); + + return -EFAULT; + } + + while( (read < count)) + { + if (put_user(i2c_inbyte(), &buf[read++])) + { + i2c_stop(); + + return -EFAULT; + } + + /* + * make sure we don't ack last byte or you will get very strange + * results! + */ + if(read < count) + { + i2c_sendack(); + } + } + + /* stop the operation */ + i2c_stop(); + + return read; +} + +/* Disables write protection if applicable. */ + +#define DBP_SAVE(x) +#define ax_printf printk +static void eeprom_disable_write_protect(void) +{ + /* Disable write protect */ + if (eeprom.size == EEPROM_8KB) + { + /* Step 1 Set WEL = 1 (write 00000010 to address 1FFFh */ + i2c_start(); + i2c_outbyte(0xbe); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false\n")); + } + i2c_outbyte(0xFF); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 2\n")); + } + i2c_outbyte(0x02); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 3\n")); + } + i2c_stop(); + + i2c_delay(1000); + + /* Step 2 Set RWEL = 1 (write 00000110 to address 1FFFh */ + i2c_start(); + i2c_outbyte(0xbe); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 55\n")); + } + i2c_outbyte(0xFF); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 52\n")); + } + i2c_outbyte(0x06); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 53\n")); + } + i2c_stop(); + + /* Step 3 Set BP1, BP0, and/or WPEN bits (write 00000110 to address 1FFFh */ + i2c_start(); + i2c_outbyte(0xbe); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 56\n")); + } + i2c_outbyte(0xFF); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 57\n")); + } + i2c_outbyte(0x06); + if(!i2c_getack()) + { + DBP_SAVE(ax_printf("Get ack returns false 58\n")); + } + i2c_stop(); + + /* Write protect disabled */ + } +} + +module_init(eeprom_init); |