/* ia_eeprom.c */

/* eeprom access routine */

/* Copyright (c) 2000  James M. Westall, Dept of Computer Science,
 *                     Clemson University, Clemson SC 29634 USA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * This software is derived from software developed by the Interphase
 * corporation and released by them in September 2000. The software
 * released by Interphase carried the following copyright notice:
 *
 *
 *                          Copyright (C) 1993
 *                Interphase Corporation, Dallas, TX 75234
 *                          All Rights Reserved
 *
 * This code contains confidential information and  trade  secrets  of
 * Interphase Corporation which shall not be reproduced or transferred
 * to other programs or disclosed to others or used for  manufacturing
 * or any other purpose without prior written permission of Interphase
 * Corporation.  Use of copyright notice is precautionary and does not
 * imply publication or intent thereof.
 */

/*H
 * FILE: ia_eeprom.c
 *
 * DESCRIPTION:
 *   This file contains all the routines to read and write serial
 *   eprom.
 *
 * FUNCTIONS:
 * EXPORTED:
 *   see below.
 *
 * STATIC:
 *   fsee below.
 *
 * GLOBAL DATA:
 *
 */

#include "ia_defs.h"

#define  NOVRAM_SIZE 64
#define  CMD_LEN     10

/***********
 *
 * Switches and defines for header files.
 *
 * The following defines are used to turn on and off
 * various options in the header files. Primarily useful
 * for debugging.
 *
 ***********/

/*
 * a list of the commands that can be sent to the NOVRAM
 */

#define  EXTEND   0x100
#define  EEWRITE    0x140
#define  EEREAD     0x180
#define  ERASE    0x1c0

#define  EWDS     0x00
#define  WRAL     0x10
#define  ERAL     0x20
#define  EWEN     0x30

/*
 * these bits duplicate the hw_flip.h register settings
 * note: how the data in / out bits are defined in the flipper specification
 */

#define  NVCE  0x02
#define  NVSK  0x01
#define  NVDO  0x08
#define  NVDI  0x04

/***********************
 *
 * This define ands the value and the current config register and puts
 * the result in the config register
 *
 ***********************/

#define  CFG_AND(val) { \
      uint32_t t; \
      t = ia_get32(&softc->brd_regs.flipper->fl_eeprom_access); \
      t &= (val); \
      ia_put32(&softc->brd_regs.flipper->fl_eeprom_access, t); \
   }

/***********************
 *
 * This define ors the value and the current config register and puts
 * the result in the config register
 *
 ***********************/

#define  CFG_OR(val) { \
      uint32_t t; \
      t = ia_get32(&softc->brd_regs.flipper->fl_eeprom_access); \
      t |= (val); \
      ia_put32(&softc->brd_regs.flipper->fl_eeprom_access, t); \
   }

/***********************
 *
 * Send a command to the NOVRAM, the command is in cmd.
 *
 * clear CE and SK. Then assert CE.
 * Clock each of the command bits out in the correct order with SK
 * exit with CE still asserted
 *
 ***********************/

#define  NVRAM_CMD(cmd) { \
      int   i; \
      u_short c = cmd; \
      CFG_AND(~(NVCE|NVSK)); \
      CFG_OR(NVCE); \
      for (i=0; i<CMD_LEN; i++) { \
         NVRAM_CLKOUT((c & (1 << (CMD_LEN - 1))) ? 1 : 0); \
         c <<= 1; \
      } \
   }

/***********************
 *
 * clear the CE, this must be used after each command is complete
 *
 ***********************/

#define  NVRAM_CLR_CE   {CFG_AND(~NVCE)}

/***********************
 *
 * clock the data bit in bitval out to the NOVRAM.  The bitval must be
 * a 1 or 0, or the clockout operation is undefined
 *
 ***********************/

#define  NVRAM_CLKOUT(bitval) { \
      CFG_AND(~NVDI); \
      CFG_OR((bitval) ? NVDI : 0); \
      CFG_OR(NVSK); \
      CFG_AND( ~NVSK); \
   }

/***********************
 *
 * clock the data bit in and return a 1 or 0, depending on the value
 * that was received from the NOVRAM
 *
 ***********************/

#define  NVRAM_CLKIN(value) { \
      uint32_t _t; \
      CFG_OR(NVSK); \
      CFG_AND(~NVSK); \
      _t = ia_get32(&softc->brd_regs.flipper->fl_eeprom_access); \
      value = (_t & NVDO) ? 1 : 0; \
   }


/*F
 * FUNCTION: ia_eeprom_put
 *
 * DESCRIPTION:
 *      Put a single word of NOVRAM.
 *
 * ARGUMENTS:
 *      softc     Device state structure
 *      addr      Novram word address
 *      val             16 bit value to write
 *
 * ENVIRONMENT:
 *
 * RETURN:
 *      nothing
 *
 * NOTES:
 *
 */

/**/
void
ia_eeprom_put (ia_softc_t *softc, u_int addr, u_short val)
{
   uint  t;
   int   i;

   /*
    * Issue a command to enable writes to the NOVRAM
    */
   NVRAM_CMD (EXTEND + EWEN);
   NVRAM_CLR_CE;

   /*
    * issue the write command
    */
   NVRAM_CMD(EEWRITE + addr);

   /*
    * Send the data, starting with D15, then D14, and so on for 16 bits
    */
   for (i=15; i>=0; i--) {
      NVRAM_CLKOUT (val & 0x8000);
      val <<= 1;
   }

   NVRAM_CLR_CE;

   CFG_OR(NVCE);

   t = ia_get32(&softc->brd_regs.flipper->fl_eeprom_access);
   while (!(t & NVDO))
      t = ia_get32(&softc->brd_regs.flipper->fl_eeprom_access);

   NVRAM_CLR_CE;

   /*
    * disable writes again
    */
   NVRAM_CMD(EXTEND + EWDS)
   NVRAM_CLR_CE;
   CFG_AND(~NVDI);
}


/*F
 * FUNCTION: ia_eeprom_get
 *
 * DESCRIPTION:
 *      Get a single word from NOVRAM.
 *
 * ARGUMENTS:
 *      softc     Device state structure
 *      addr      Novram word address
 *
 * ENVIRONMENT:
 *
 * RETURN:
 *      A 16 bit word read from the Novram
 *
 * NOTES:
 *
 */

/**/
u_short
ia_eeprom_get (ia_softc_t *softc, u_int addr)
{
   u_short  val;
   uint  t;
   int   i;

   /*
    * Read the first bit that was clocked with the falling edge of the
    * the last command data clock
    */
   NVRAM_CMD(EEREAD + addr);

   /*
    * Now read the rest of the bits, the next bit read is D14, then D13,
    * and so on.
    */

   val = 0;
   for (i=15; i>=0; i--) {
      NVRAM_CLKIN(t);
      val |= (t << i);
   }

   NVRAM_CLR_CE;

   CFG_AND(~NVDI);

   return val;
}
