FROM: /* $Id: eeprom.h,v 1.21.2.6 2008/08/19 22:10:39 arcanum Exp $ */
ditch all control etc, just keep operative stuff for documentation DGG 3/8/12
move all #else       /* If ATmega256x device, do not call function. */ to the bottom for clarity!

/* writting to EEPROM takes 3.3ms, eeprom_write_block(src,dst,150) takes 1/2 second  BAD if disabled
uses a simple polled mode interface.  
 Applications that require interrupt-controlled EEPROM access to ensure that no time will be wasted in spinloops will 
 have to deploy their own implementation.

The read/write functions first make sure the EEPROM is ready to be accessed.  
    this may cause long delays if a write operation is still pending,  3+ ms
    time-critical applications should first poll the EEPROM e. g. using eeprom_is_ready() before attempting any actual I/O.  
    But this functions are not wait until SELFPRGEN in SPMCSR becomes zero.  
    Do this manually, if your softwate contains the Flash burning. (?)

Uses r0 for saving SREG when interrupts disable is required           /* revision: add push/pull r0 to stack
 non-reentrant. 
If used from normal and interrupt context, the applications must ensure
    proper protection (e.g. by disabling interrupts before accessing them).

uses atomic erase_and_write programming mode.

# define EEWE EEPE           # define EEMWE EEMPE
/* AT86RF401 */
# define EECR  DEECR        # define EEAR  DEEAR    # define EEARL DEEAR # define EEDR  DEEDR
# define EERE  EER          # define EEWE  EEL      # define EEMWE EEU 
#if	EECR | EEDR | EEARL

# if	 !defined(__EEPROM_REG_LOCATIONS__)  && !defined(EEPROM_REG_LOCATIONS_OVERRIDE)
/* 6-byte string denoting where to find the EEPROM registers in memory space.  
    Adresses denoted in hex syntax with uppercase letters. Used by the EEPROM subroutines.
	First two letters:  EECR address.
	Second two letters: EEDR address.
	Last two letters:   EEAR address.
    */

# define CONCAT1(a, b) CONCAT2(a, b)
# define CONCAT2(a, b) a ## b
# define HEXNR CONCAT1(0x, __EEPROM_REG_LOCATIONS__)

# define EECR _SFR_IO8((HEXNR >> 16) & 0xFF) 
# define EEDR _SFR_IO8((HEXNR >> 8 ) & 0xFF) 
# define EEAR _SFR_IO8( HEXNR        & 0xFF) 

# define eeprom_is_ready() bit_is_clear(DEECR, BSY) || bit_is_clear(EECR, EEWE) 
#define eeprom_busy_wait() do {STALL here} while (!eeprom_is_ready())
uint8_t eeprom_read_byte (const uint8_t *__p)
    { do { STALL here } while (!eeprom_is_ready ());                // STALL
                                        /* Use inline assembly below as some AVRs have problems with accessing
                                        EECR with STS instructions. For example, see errata for ATmega64. 
    (   sbi %1, %2                      
        in  %0, %3                      // This code uses EECR and EEDR are in the I/O space. 
         =r  (__result)
         i  (_SFR_IO_ADDR(EECR)),
         i  (EERE),
         i  (_SFR_IO_ADDR(EEDR))
    );
    return __result;
}

uint16_t eeprom_read_word (const uint16_t *__p)
{    union { uint16_t word; struct { uint8_t lo; uint8_t hi; } byte; } x; 
    x.byte.lo = eeprom_read_byte ((const uint8_t *)__p);
    x.byte.hi = eeprom_read_byte ((const uint8_t *)__p + 1);
    return x.word;
}

uint32_t eeprom_read_dword (const uint32_t *__p)
{    union { uint32_t dword; struct { uint8_t byte0; uint8_t byte1; uint8_t byte2; uint8_t byte3; } byte; } x; 
    x.byte.byte0 = eeprom_read_byte ((const uint8_t *)__p);     x.byte.byte1 = eeprom_read_byte ((const uint8_t *)__p + 1);
    x.byte.byte2 = eeprom_read_byte ((const uint8_t *)__p + 2); x.byte.byte3 = eeprom_read_byte ((const uint8_t *)__p + 3);
    return x.dword;
}

cpyfromEE   a block                arg1 <---- arg2
eeprom_read_block (void *__dst, const void *__src, size_t __n)
{ char *_myDstPtr,               *_mySrcPtr;         
	    _myDstPtr=(char *)__dst; _mySrcPtr=(char *)__src;
	while (__n--) {   *_myDstPtr	=	eeprom_read_byte((const uint8_t *)_mySrcPtr); _myDstPtr++; _mySrcPtr++; }
}

void eeprom_write_byte (uint8_t *__p, uint8_t __value)
{   do { STALL here} while (!eeprom_is_ready ());                   // STALL
    EECR = 0;		/* Set programming mode: erase and write.	*/

#if	E2END <= 0xFF
    EEARL = (unsigned)__p; #else
    EEAR  = (unsigned)__p;
#endif
    EEDR = __value;

         in	r0, %[__sreg]		       save IMASK (and otheres too)
         cli				           /* --DISABLE -- */
         sbi	%[__eecr], %[__eemwe]	
         sbi	%[__eecr], %[__eewe]
         out	%[__sreg], r0           /* restore IMASK ( and others too) maybe enable
        :
        : [__eecr]   i  (_SFR_IO_ADDR(EECR)),
          [__sreg]   i  (_SFR_IO_ADDR(SREG)),
          [__eemwe]  i  (EEMWE),
          [__eewe]   i  (EEWE)
        :  r0 
    );
}

eeprom_write_word (uint16_t *__p, uint16_t __value)
{   union { uint16_t word; struct { uint8_t lo; uint8_t hi; } byte; } x; 
    x.word = __value;
    eeprom_write_byte ((uint8_t *)__p,     x.byte.lo);
    eeprom_write_byte ((uint8_t *)__p + 1, x.byte.hi);
}

eeprom_write_dword (uint32_t *__p, uint32_t __value)
{   union { uint32_t dword; struct { uint8_t byte0; uint8_t byte1; uint8_t byte2; uint8_t byte3; } byte; } x; 
    x.dword = __value;
    eeprom_write_byte ((uint8_t *)__p,     x.byte.byte0);
    eeprom_write_byte ((uint8_t *)__p + 1, x.byte.byte1);
    eeprom_write_byte ((uint8_t *)__p + 2, x.byte.byte2);
    eeprom_write_byte ((uint8_t *)__p + 3, x.byte.byte3);
} 
                                                                        >>> argument order is mismatch with strcpy().  
cpy  toEE                       arg1 ---> arg2                          
eeprom_write_block (const void *__src, void *__dst, size_t __n)        // * Warning * do not enter this  disabled
	uint8_t	*_myDstPtr; uint8_t	*_mySrcPtr;                             // This takes a long time 
	_myDstPtr=	(uint8_t *)__dst; _mySrcPtr	=	(uint8_t *)__src;       // like 150 bytes @3.3ms per byte is .5 seconds !
	while (__n--) { eeprom_write_byte(_myDstPtr++, *_mySrcPtr++); }
}

#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
    return __eerd_word (__p, eeprom_read_byte);
    return __eerd_dword (__p, eeprom_read_byte);
    __eewr_dword (__p, __value, eeprom_write_byte);
    __eewr_word (__p, __value, eeprom_write_byte);
    __eerd_block (__dst, __src, __n, eeprom_read_byte);
    __eewr_block (__dst, __src, __n, eeprom_write_byte);
#endif
