//==========================================================================
//
//      at91rm9200_kits_misc.c
//
//      HAL misc board support code for Atmel AT91RM9200 Evaluation/Development Kits
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008 Free Software Foundation, Inc.
// Copyright (C) 2003, 2004, 2005, 2006, 2008 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 <pkgconf/hal_arm_arm9_at91rm9200.h>
#include CYGBLD_HAL_PLATFORM_H

#include <cyg/hal/atmel-at91rm9200-kits.h>  // Physical memory addresses
#include <cyg/hal/at91rm9200.h>             // _at91rm9200_hardware_init()
#include <string.h>                         // memset()

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



// -------------------------------------------------------------------------
// 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 = ATMEL_AT91RM9200_KITS_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                                                              */
#ifdef CYGHWR_HAL_ARM_ARM9_ATMEL_AT91RM9200_KITS_BOARD_VARIANT_AT91RM9200_DK
    _MMU(0x100,     0x100,      2,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* Uncached Boot flash ROMspace CS0. */
    _MMU(0x100,     0x600,      2,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* Boot flash ROMspace CS0. */
#else
    _MMU(0x100,     0x100,      8,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* Uncached Boot flash ROMspace CS0. */
    _MMU(0x100,     0x600,      8,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* Boot flash ROMspace CS0. */
#endif
    _MMU(0x200,     0x000,      8,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* SDRAM at 0 */
    _MMU(0x200,     0x200,      8,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* SDRAM copy */
    _MMU(0x200,     0x300,      8,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* Uncached access to SDRAM */
    _MMU(0x002,     0x700,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* On-chip SRAM - 16KB only */
    _MMU(0x002,     0x701,      1,  _UNCACHEABLE, _UNBUFFERABLE, _PERM_RW_RW); /* On-chip SRAM - 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(0x003,     0x720,      1,  _CACHEABLE,   _BUFFERABLE,   _PERM_RW_RW); /* USB host port registers - cached access */
    _MMU(0x003,     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
//
void
plf_hardware_init(void)
{
    _at91rm9200_hardware_init();
}

// Platform-specific MCI interface support

#ifdef CYGIMP_HAL_ARM_ARM9_AT91RM9200_MCI_INTMODE
#include <pkgconf/devs_disk_mmc.h>
// Provides next define, if it's defined.
#endif

#ifdef CYGFUN_DEVS_DISK_MMCSD_BUS_REMOVABLE_MEDIA_SUPPORT

#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_io.h>
#include <cyg/io/mmcsd_bus.h>

// Convenience define. Pin is low when card present.
#define CARD_DETECT_PIN (1<<27)

static cyg_uint32
mci_card_detect_isr( cyg_vector_t vector, cyg_addrword_t data )
{
    cyg_uint32 int_status;

    cyg_drv_interrupt_acknowledge( vector );

    // Check PB27 (card detect pin) interrupt status
    HAL_READ_UINT32( AT91RM9200_PIOB + _PIO_ISR, int_status );
    if (int_status && CARD_DETECT_PIN)
        return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);
    return 0; // not us
} // mci_card_detect_isr()

static void
mci_card_detect_dsr( cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data )
{
    cyg_mmcsd_card *socket = (cyg_mmcsd_card*)data;
    cyg_uint32 pdsr;
    cyg_bool card_present;

    // read current pin status
    HAL_READ_UINT32( AT91RM9200_PIOB + _PIO_PDSR, pdsr );
    card_present = (0 == (pdsr & CARD_DETECT_PIN));

    MMCSD_CARD_DETECT_EVENT(socket, card_present);
} // mci_card_detect_dsr()


__externC void
cyg_hal_at91rm9200kit_init_card_detect_intr(cyg_mmcsd_card *socket)
{
    static cyg_handle_t mci_card_detect_int_handle;
    static cyg_interrupt mci_card_detect_intr;
    cyg_uint32 pdsr;
    cyg_bool card_present;

    // PB27 is card detect interrupt pin

    // Create an interrupt handler for PIOB
    cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_PIOB, 1, (cyg_addrword_t)socket,
                              &mci_card_detect_isr, &mci_card_detect_dsr,
                              &mci_card_detect_int_handle, &mci_card_detect_intr );
    cyg_drv_interrupt_attach( mci_card_detect_int_handle );

    /* Although we unmask, this won't trigger ints, since all the MCI device's own
       interrupts are themselves still masked */
    cyg_drv_interrupt_unmask( CYGNUM_HAL_INTERRUPT_PIOB );

    HAL_WRITE_UINT32( AT91RM9200_PIOB + _PIO_IER, CARD_DETECT_PIN ); // Enable GPIO interrupt mode

    /* Default is to assume that no card is present, but we check whether there is,
     * and if so, indicate it */
    HAL_READ_UINT32( AT91RM9200_PIOB + _PIO_PDSR, pdsr );
    card_present = (0 == (pdsr & CARD_DETECT_PIN));

    if (card_present) {
        MMCSD_CARD_DETECT_EVENT(socket, card_present);
    }
} // cyg_hal_at91rm9200kit_init_card_detect_intr()
#endif // ifdef CYGFUN_DEVS_DISK_MMCSD_BUS_REMOVABLE_MEDIA_SUPPORT

// EOF at91rm9200_kits_misc.c
