/*!**************************************************************************
*!
*!  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.1 2001/05/31 10:55:04 green 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.1  2001/05/31 10:55:04  green
*!  Update to 2.4.5-rmk2-np1
*!
*!  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 *****************************/
