//==========================================================================
//
//      sam9260ek_misc.c
//
//      HAL misc board support code for Atmel SAM9260-EK Evaluation Kit
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
// Copyright (C) 2003, 2004, 2005, 2006, 2007 eCosCentric Limited           
//
// eCos 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 or (at your option) any later      
// version.                                                                 
//
// eCos 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.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    eCosCentric
// Contributors: hmt, Travis C. Furrer <furrer@mit.edu>, jskov
// Date:         2005-07-29
// Purpose:      HAL board support
// Description:  Implementations of HAL board interfaces
//
//####DESCRIPTIONEND####
//
//========================================================================*/

#include <cyg/infra/cyg_type.h>             // base types

#include <pkgconf/system.h>
#include CYGBLD_HAL_PLATFORM_H

#include <cyg/hal/sam9260ek.h>  // Physical memory addresses
#include <cyg/hal/sam9.h>             // _sam9_hardware_init()
#include <string.h>                         // memset()
#include <cyg/hal/hal_diag.h>               // HAL_DELAY_US

//#include <cyg/infra/diag.h>               // diag_printf

static void sam9260ek_eth_init(void);


// -------------------------------------------------------------------------
// MMU initialization:
// 
// These structures are laid down in memory to define the translation
// table.
// 

/*
 * ARM Translation Table Base Bit Masks */
#define ARM_TRANSLATION_TABLE_MASK               0xFFFFC000

/*
 * ARM Domain Access Control Bit Masks
 */
#define ARM_ACCESS_TYPE_NO_ACCESS(domain_num)    (0x0 << (domain_num)*2)
#define ARM_ACCESS_TYPE_CLIENT(domain_num)       (0x1 << (domain_num)*2)
#define ARM_ACCESS_TYPE_MANAGER(domain_num)      (0x3 << (domain_num)*2)

struct ARM_MMU_FIRST_LEVEL_FAULT {
    int id : 2;
    int sbz : 30;
};
#define ARM_MMU_FIRST_LEVEL_FAULT_ID 0x0

struct ARM_MMU_FIRST_LEVEL_PAGE_TABLE {
    int id : 2;
    int imp : 2;
    int domain : 4;
    int sbz : 1;
    int base_address : 23;
};
#define ARM_MMU_FIRST_LEVEL_PAGE_TABLE_ID 0x1

struct ARM_MMU_FIRST_LEVEL_SECTION {
    int id : 2;
    int b : 1;
    int c : 1;
    int imp : 1;
    int domain : 4;
    int sbz0 : 1;
    int ap : 2;
    int sbz1 : 8;
    int base_address : 12;
};
#define ARM_MMU_FIRST_LEVEL_SECTION_ID 0x2

struct ARM_MMU_FIRST_LEVEL_RESERVED {
    int id : 2;
    int sbz : 30;
};
#define ARM_MMU_FIRST_LEVEL_RESERVED_ID 0x3

#define ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS(ttb_base, table_index) \
   (unsigned long *)((unsigned long)(ttb_base) + ((table_index) << 2))

#define ARM_FIRST_LEVEL_PAGE_TABLE_SIZE 0x4000

#define ARM_MMU_SECTION(ttb_base, actual_base, virtual_base,              \
                        cacheable, bufferable, perm)                      \
    CYG_MACRO_START                                                       \
        register union ARM_MMU_FIRST_LEVEL_DESCRIPTOR desc;               \
                                                                          \
        desc.word = 0;                                                    \
        desc.section.id = ARM_MMU_FIRST_LEVEL_SECTION_ID;                 \
        desc.section.imp = 1;                                             \
        desc.section.domain = 0;                                          \
        desc.section.c = (cacheable);                                     \
        desc.section.b = (bufferable);                                    \
        desc.section.ap = (perm);                                         \
        desc.section.base_address = (actual_base);                        \
        *ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS(ttb_base, (virtual_base)) \
                            = desc.word;                                  \
    CYG_MACRO_END

#define X_ARM_MMU_SECTION(abase,vbase,size,cache,buff,access)      \
    { int i; int j = abase; int k = vbase;                         \
      for (i = size; i > 0 ; i--,j++,k++)                          \
      {                                                            \
        ARM_MMU_SECTION(ttb_base, j, k, cache, buff, access);      \
      }                                                            \
    }

union ARM_MMU_FIRST_LEVEL_DESCRIPTOR {
    unsigned long word;
    struct ARM_MMU_FIRST_LEVEL_FAULT fault;
    struct ARM_MMU_FIRST_LEVEL_PAGE_TABLE page_table;
    struct ARM_MMU_FIRST_LEVEL_SECTION section;
    struct ARM_MMU_FIRST_LEVEL_RESERVED reserved;
};

#define _UNCACHEABLE                  0
#define _CACHEABLE                    1
#define _UNBUFFERABLE                 0
#define _BUFFERABLE                   1

#define _PERM_NONE_NONE               0
#define _PERM_RO_NONE                 0
#define _PERM_RO_RO                   0
#define _PERM_RW_NONE                 1
#define _PERM_RW_RO                   2
#define _PERM_RW_RW                   3

#define _MMU X_ARM_MMU_SECTION

__externC void cyg_hal_arm9_set_mmuregs( unsigned long tt_base,
                                         unsigned long dacr );
void
hal_mmu_init(void)
{
    unsigned long ttb_base = SAM9260EK_SDRAM_PHYS_BASE + 0x4000;
    unsigned long i;

    /*
     * Set the Domain Access Control Register
     */
    i = ARM_ACCESS_TYPE_MANAGER(0)    | 
        ARM_ACCESS_TYPE_NO_ACCESS(1)  |
        ARM_ACCESS_TYPE_NO_ACCESS(2)  |
        ARM_ACCESS_TYPE_NO_ACCESS(3)  |
        ARM_ACCESS_TYPE_NO_ACCESS(4)  |
        ARM_ACCESS_TYPE_NO_ACCESS(5)  |
        ARM_ACCESS_TYPE_NO_ACCESS(6)  |
        ARM_ACCESS_TYPE_NO_ACCESS(7)  |
        ARM_ACCESS_TYPE_NO_ACCESS(8)  |
        ARM_ACCESS_TYPE_NO_ACCESS(9)  |
        ARM_ACCESS_TYPE_NO_ACCESS(10) |
        ARM_ACCESS_TYPE_NO_ACCESS(11) |
        ARM_ACCESS_TYPE_NO_ACCESS(12) |
        ARM_ACCESS_TYPE_NO_ACCESS(13) |
        ARM_ACCESS_TYPE_NO_ACCESS(14) |
        ARM_ACCESS_TYPE_NO_ACCESS(15);

    /*
     * Set the TTB register and the DACR
     */
    cyg_hal_arm9_set_mmuregs( ttb_base, i );
    
    /*
     * First clear all TT entries - ie Set them to Faulting
     */
    memset((void *)ttb_base, 0, ARM_FIRST_LEVEL_PAGE_TABLE_SIZE);

    /*     Physical   Virtual  Size Attributes                                    Function  */
    /*     Base       Base     MB   cached?       buffered?      access perms               */
    /*     xxx00000   xxx00000                                                              */
    _MMU(0x200,     0x000,     64,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* SDRAM at 0 */
    _MMU(0x200,     0x200,     64,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* SDRAM copy */
    _MMU(0x200,     0x300,     64,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* Uncached access to SDRAM */
    _MMU(0x002,     0x700,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* On-chip SRAM0 - 4KB only */
    _MMU(0x003,     0x701,      1,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* On-chip SRAM0 - uncached access */
    _MMU(0x003,     0x702,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* On-chip SRAM1 - 4KB only */
    _MMU(0x002,     0x703,      1,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* On-chip SRAM1 - uncached access */
    _MMU(0x001,     0x710,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* On-chip ROM - 128KB only */
    _MMU(0x001,     0x718,      1,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* On-chip ROM - uncached access */
    //_MMU(0x005,     0x720,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* USB host port registers - cached access */
    _MMU(0x005,     0x728,      1,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* USB host port registers - uncached access */
    _MMU(0xFF0,     0xFF0,     16,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* On-chip devices */
}

//
// Platform specific initialization
//

extern void cyg_hal_plf_serial_putc0(char c);

void
plf_hardware_init(void)
{
    _sam9_hardware_init();
#if defined(CYGPKG_DEVS_ETH_ARM_AT91)
    sam9260ek_eth_init();
#endif
}

//=============================================================================

#ifdef CYGPKG_IO_I2C

#include <cyg/io/i2c.h>

__externC cyg_i2c_bus hal_sam9_i2c_bus;


CYG_I2C_DEVICE(cyg_i2c_sam9_at24c512,
               &hal_sam9_i2c_bus,
               0xA0>>1,
               0x00,
               CYG_I2C_DEFAULT_DELAY);

#endif

//=============================================================================
#if defined(CYGPKG_DEVS_ETH_ARM_AT91)
// By rights this should fix reliability problems with the eth and PHY driver.
// Although it can potentially confuse a JTAG device (e.g. PEEDI) if connected.
static void sam9260ek_eth_init(void)
{
    cyg_uint32 stat, oldrmr, i;

   /* Enable the PIOA Clock */
   HAL_WRITE_UINT32(_PMC_PCER, 1<<2);

   // PHY can latch bad config state on power-on, so we need manual reset
   // of PHY via NRST, once pins are configured to appropriate state.

   // Set all EMAC pins to PIO inputs with no pull-up (PA10-PA29)
   HAL_WRITE_UINT32( SAM9_PIOA+_PIO_ODR, 0x3ffffc00 );
   HAL_WRITE_UINT32( SAM9_PIOA+_PIO_PUDR, 0x3ffffc00 );
   HAL_WRITE_UINT32( SAM9_PIOA+_PIO_PER, 0x3ffffc00 );

   /* All the lines setup correctly. Now do a external reset and let the phy
      start up in the correct mode */
   HAL_READ_UINT32(_RST_RMR,oldrmr);
   HAL_WRITE_UINT32(_RST_RMR,_RST_RMR_KEY|(1<<0x8));
   HAL_WRITE_UINT32(_RST_RCR,_RST_RCR_KEY|_RST_RCR_EXTRST);

   do {
     HAL_READ_UINT32(_RST_RSR,stat);
   } while ((stat&(_RST_RSR_NRST_SET|_RST_RSR_SRCMP)) != _RST_RSR_NRST_SET);

   // Delay apparently to allow NRST to settle before we re-enable user resets.
   // This delay has to be surprisingly large. I don't know why.
   // If we don't wait this long, then the chip resets itself. If we don't re-enable user
   // resets, then neither the reset button nor JTAG reset are reliable resets.
   // I think this is a bug personally as the NRST_SET check above should be
   // sufficient.
   HAL_DELAY_US(700000); // FIXME: can this be reduced on SAM9?

   /* Restore old RMR settings */
   HAL_WRITE_UINT32(_RST_RMR,oldrmr|_RST_RMR_KEY);
   // Peripheral function will be restored if needed in eth driver.
}
#endif
//=============================================================================
// EOF sam9260ek_misc.c
