patch-2.4.5 linux/arch/cris/drivers/eeprom.c
Next file: linux/arch/cris/drivers/ethernet.c
Previous file: linux/arch/cris/drivers/ds1302.c
Back to the patch index
Back to the overall index
- Lines: 1162
- Date:
Tue May 1 16:04:56 2001
- Orig file:
v2.4.4/linux/arch/cris/drivers/eeprom.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.4/linux/arch/cris/drivers/eeprom.c linux/arch/cris/drivers/eeprom.c
@@ -0,0 +1,1161 @@
+/*!**************************************************************************
+*!
+*! FILE NAME: e100eeprom.c
+*!
+*! DESCRIPTION: 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.
+*!
+*! FUNCTIONS:
+*!
+*! (Exported)
+*! eeprom_init()
+*!
+*! (Local)
+*!
+*! eeprom_open()
+*! eeprom_lseek()
+*! eeprom_read()
+*! eeprom_write()
+*! eeprom_close()
+*! eeprom_address()
+*! eeprom_disable_write_protect()
+*!
+*!
+*! $Id: eeprom.c,v 1.3 2001/03/19 16:04:46 markusl Exp $
+*!
+*!------------------------------------------------------------------------
+*! 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.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 FILES SECTION ******************************/
+
+#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 "i2c.h"
+
+/********************** CONSTANT AND MACRO SECTION *************************/
+#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
+
+#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)
+
+/********************** TYPE DEFINITION SECTION ****************************/
+
+/*
+ * 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 int 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;
+ int busy;
+ int retry_cnt_addr; /* Used to keep track of number of retries for
+ adaptive timing adjustments */
+ int retry_cnt_read;
+
+
+
+};
+
+/********************** LOCAL FUNCTION DECLARATION SECTION *****************/
+
+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(unsigned long int addr,
+ const char * buf, int count);
+static int eeprom_read_buf(unsigned long addr,
+ char * buf, int count);
+
+static void eeprom_disable_write_protect(void);
+
+
+/********************** GLOBAL VARIABLE DECLARATION SECTION ****************/
+
+/********************** LOCAL VARIABLE DECLARATION SECTION *****************/
+
+/* 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
+};
+
+/********************** FUNCTION DEFINITION SECTION ************************/
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_init
+*#
+*# PARAMETERS : none
+*#
+*# RETURNS : 0 if OK
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : eeprom init call. Probes for different eeprom models.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Sep 03 1999 Edgar Iglesias Updated probing. Added forced values.
+*#
+*#**************************************************************************/
+
+int __init eeprom_init(void)
+{
+ init_waitqueue_head(&eeprom.wait_q);
+ eeprom.busy = 0;
+
+#if CONFIG_ETRAX_I2C_EEPROM_PROBE
+#define EETEXT "Found"
+#else
+#define EETEXT "Assuming"
+#endif
+ if (register_chrdev(EEPROM_MAJOR_NR, "mem", &eeprom_fops))
+ {
+ printk("unable to get major %d for eeprom device\n", 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 = 4000;/*MAX_WRITEDELAY_US / EEPROM_RETRIES;*/
+ eeprom.usec_delay_step = 128;
+ eeprom.adapt_state = 0;
+
+#if 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 diferent 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("Failed to read in 2k mode!\n");
+ }
+
+ /* 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("16k detected in step 1\n");
+ 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("read and write differs! Not 16kB\n");
+ loc1[0] = ~loc1[0];
+
+ if (eeprom_write_buf(LOC1, loc1, 1) == 1)
+ {
+ success = 1;
+ }
+ else
+ {
+ printk("eeprom: Restore 2k failed during probe EEPROM might be corrupt!\n");
+
+ }
+ 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("Failed to write back 2k start!\n");
+ }
+
+ 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("2k detected in step 2\n");
+ loc1[0] = ~loc1[0];
+ if (eeprom_write_buf(LOC1, loc1, 1) == 1)
+ {
+ success = 1;
+ }
+ else
+ {
+ printk("eeprom: Restore 2k failed during probe EEPROM might be corrupt!\n");
+
+ }
+
+ eeprom.size = EEPROM_2KB;
+ }
+ else
+ {
+ printk("16k detected in step 2\n");
+ loc1[0] = ~loc1[0];
+ /* Data differs, assume 16k */
+ /* Restore data */
+ if (eeprom_write_buf(LOC1, loc1, 1) == 1)
+ {
+ success = 1;
+ }
+ else
+ {
+ printk("eeprom: Restore 16k failed during probe EEPROM might be corrupt!\n");
+ }
+
+ eeprom.size = EEPROM_16KB;
+ }
+ }
+ }
+ }
+ } /* read LOC1 */
+ } /* address LOC1 */
+ if (!success)
+ {
+ printk("eeprom: Probing failed!, using 2KB!\n");
+ 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("e100eeprom: " EETEXT " i2c compatible 2Kb eeprom.\n");
+ eeprom.sequential_write_pagesize = 16;
+ eeprom.select_cmd = 0xA0;
+ break;
+ case (EEPROM_8KB):
+ printk("e100eeprom: " EETEXT " i2c compatible 8Kb eeprom.\n");
+ eeprom.sequential_write_pagesize = 16;
+ eeprom.select_cmd = 0x80;
+ break;
+ case (EEPROM_16KB):
+ printk("e100eeprom: " EETEXT " i2c compatible 16Kb eeprom.\n");
+ eeprom.sequential_write_pagesize = 64;
+ eeprom.select_cmd = 0xA0;
+ break;
+ default:
+ eeprom.size = 0;
+ printk("e100eeprom: Did not find a supported eeprom\n");
+ break;
+ }
+
+
+
+ eeprom_disable_write_protect();
+
+ return 0;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_open
+*#
+*# PARAMETERS : inode : Pointer to the inode
+*# file : Pointer to the file
+*#
+*# RETURNS : 0 if OK
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Opens the device.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Sep 03 1999 Edgar Iglesias Removed users check.
+*#
+*#**************************************************************************/
+
+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;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_lseek
+*#
+*# PARAMETERS : file : Pointer to the file
+*# offset : The offset (in bytes)
+*# orig : look at the note
+*#
+*# RETURNS : 0 if OK
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Changes the current file position.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Sep 03 1999 Edgar Iglesias Return -EOVERFLOW when beyond eeprom size.
+*#
+*#**************************************************************************/
+
+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 );
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_read
+*#
+*# PARAMETERS : inode : Pointer to the inode
+*# file : Pointer to the file
+*# buf : Destination buffer
+*# count : max nr bytes to read
+*#
+*# RETURNS : number of read bytes.
+*#
+*# SIDE EFFECTS : updates file->f_pos
+*#
+*# DESCRIPTION : Reads data from eeprom.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Jan 17 2000 Johan Adolfsson Initial version
+*#
+*#**************************************************************************/
+static int eeprom_read_buf(unsigned long addr,
+ char * buf, int count)
+{
+ struct file f;
+
+ f.f_pos = addr;
+ return eeprom_read(&f, buf, count, &addr);
+}
+
+
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_read
+*#
+*# PARAMETERS : file : Pointer to the file
+*# buf : Destination buffer
+*# count : max nr bytes to read
+*#
+*# RETURNS : number of read bytes.
+*#
+*# SIDE EFFECTS : updates file->f_pos
+*#
+*# DESCRIPTION : Reads data from eeprom.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Aug 31 1999 Edgar Iglesias Made operation atomic with wait queues
+*# Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted
+*# in the spin-lock.
+*#
+*#**************************************************************************/
+
+static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off)
+{
+ int i, 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("eeprom: Read failed to address the eeprom: "
+ "0x%08X (%i) page: %i\n", p, 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;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_write_buf
+*#
+*# PARAMETERS : addr : Address to write to
+*# buf : Data buffer to write from
+*# count : number bytes to write
+*#
+*# RETURNS : number of bytes actualy written.
+*#
+*# SIDE EFFECTS : None
+*#
+*# DESCRIPTION : Writes data to eeprom.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Jan 17 2000 Johan Adolfsson Initial vesion
+*#
+*#**************************************************************************/
+static int eeprom_write_buf(unsigned long int addr,
+ const char * buf, int count)
+{
+ struct file f;
+
+ f.f_pos = addr;
+
+ return eeprom_write(&f, buf, count, &addr);
+}
+
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_write
+*#
+*# PARAMETERS : file : Pointer to the file
+*# buf : Data buffer to write from
+*# count : number bytes to write
+*#
+*# RETURNS : number of bytes actualy written.
+*#
+*# SIDE EFFECTS : updates file->f_pos
+*#
+*# DESCRIPTION : Writes data to eeprom.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Aug 31 1999 Edgar Iglesias Made operation atomic with wait queues
+*# Sep 03 1999 Edgar Iglesias Moved the actual reading to read_from_eeprom
+*# Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted
+*# in the spin-lock.
+*# May 18 2000 Edgar Iglesias Make sure to end write cycle after every write.
+*#
+*#**************************************************************************/
+
+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;
+
+ 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("eeprom: Write failed to address the eeprom: 0x%08X (%i) \n",
+ p, 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)
+ {
+ if (eeprom.usec_delay_step > 0)
+ {
+ 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("eeprom: write error, retrying. %d\n", 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;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_close
+*#
+*# PARAMETERS : inode : Pointer to the inode
+*# file : Pointer to the file
+*#
+*# RETURNS : nothing
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Closes the device.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Sep 03 1999 Edgar Iglesias Removed eeprom.users stuff.
+*#
+*#**************************************************************************/
+
+static int eeprom_close(struct inode * inode, struct file * file)
+{
+ /* do nothing for now */
+ return 0;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_address
+*#
+*# PARAMETERS : addr : Address to be given to eeprom
+*#
+*# RETURNS : 1 if OK, 0 if failure.
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Sets the current address of the eeprom.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Aug 28 1999 Edgar Iglesias Initial version
+*# Sep 03 1999 Edgar Iglesias Corrected typo.
+*#
+*#**************************************************************************/
+
+static int eeprom_address(unsigned long addr)
+{
+ int i, j;
+ 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;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: read_from_eeprom
+*#
+*# PARAMETERS : buf : Destination buffer.
+*# count : Number of bytes to read.
+*#
+*# RETURNS : number of read bytes or -EFAULT.
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Reads from current adress.
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Sep 03 1999 Edgar Iglesias Initial version
+*#
+*#**************************************************************************/
+
+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("eeprom: failed to read from eeprom\n");
+ i2c_stop();
+
+ return -EFAULT;
+ }
+
+ while( (read < count))
+ {
+ buf[read++] = i2c_inbyte();
+
+ /*
+ * 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;
+}
+
+/*#**************************************************************************
+*#
+*# FUNCTION NAME: eeprom_disable_write_protect
+*#
+*# PARAMETERS : None
+*#
+*# RETURNS : Nothing
+*#
+*# SIDE EFFECTS :
+*#
+*# DESCRIPTION : Disables write protection if applicable
+*#
+*#---------------------------------------------------------------------------
+*# HISTORY
+*#
+*# DATE NAME CHANGES
+*# ---- ---- -------
+*# Jan 14 2000 Johan Adolfsson Initial version (from PS pareerom.c)
+*#
+*#**************************************************************************/
+#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);
+
+/********************** END OF FILE e100eeprom.c *****************************/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)