// ----------------------------------------------------------------------------------------
//
//  s3c2410a/device/gpio.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides spin-wait delay routines for the Samsung S3C2410A processor.
//
//  Copyright (C) 2009 Bob Cook
//
//      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.
//    
//      You should have received a copy of the GNU General Public License along
//      with this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "gpio.h"

#include <fcntl.h>
#include <sys/mman.h>


// ----------------------------------------------------------------------------------------

const uint32_t gpio_addr_offset = 0x56000000;
const uint32_t gpio_page_size   = 4096;

const uint32_t gpio_offset_portc_control = ( 0x0020 / sizeof( uint32_t ) );
const uint32_t gpio_offset_portc_data    = ( 0x0024 / sizeof( uint32_t ) );
const uint32_t gpio_offset_portc_pudis   = ( 0x0028 / sizeof( uint32_t ) );

const uint32_t gpio_offset_portf_control = ( 0x0050 / sizeof( uint32_t ) );
const uint32_t gpio_offset_portf_data    = ( 0x0054 / sizeof( uint32_t ) );
const uint32_t gpio_offset_portf_pudis   = ( 0x0058 / sizeof( uint32_t ) );

static int       gpio_mmap_fd  = -1;
static uint32_t* gpio_mmap_ptr = 0;


// ----------------------------------------------------------------------------------------

static bool open_memory_map()
{
    if ( gpio_mmap_fd != -1 ) return true;

    if ( ( gpio_mmap_fd = open( "/dev/mem", O_RDWR | O_SYNC ) ) == -1 )
    {
        // log error?
        return false;
    }

    gpio_mmap_ptr 
        = reinterpret_cast< uint32_t* >(
            mmap( 0, // base address
                  gpio_page_size,
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED,
                  gpio_mmap_fd,
                  gpio_addr_offset ) );

    if ( gpio_mmap_ptr == reinterpret_cast< uint32_t* >( -1 ) )
    {
        // log error?
        return false;
    }

    return true;
}


// ----------------------------------------------------------------------------------------

void gpio_configure_portc_pin( uint8_t pin, gpio_mode_t mode )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portc_control_ptr = gpio_mmap_ptr + gpio_offset_portc_control;
    *portc_control_ptr &= ~( 0x03 << ( pin * 2 ) );
    *portc_control_ptr |= ( mode << ( pin * 2 ) );
}


// ----------------------------------------------------------------------------------------

void gpio_configure_portf_pin( uint8_t pin, gpio_mode_t mode )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portf_control_ptr = gpio_mmap_ptr + gpio_offset_portf_control;
    *portf_control_ptr &= ~( 0x03 << ( pin * 2 ) );
    *portf_control_ptr |= ( mode << ( pin * 2 ) );
}


// ----------------------------------------------------------------------------------------

void gpio_set_portc_pin( uint8_t pin )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portc_data_ptr = gpio_mmap_ptr + gpio_offset_portc_data;
    *portc_data_ptr |= ( 0x01 << pin );
}


// ----------------------------------------------------------------------------------------

void gpio_set_portf_pin( uint8_t pin )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portf_data_ptr = gpio_mmap_ptr + gpio_offset_portf_data;
    *portf_data_ptr |= ( 0x01 << pin );
}


// ----------------------------------------------------------------------------------------

void gpio_clear_portc_pin( uint8_t pin )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portc_data_ptr = gpio_mmap_ptr + gpio_offset_portc_data;
    *portc_data_ptr &= ~( 0x01 << pin );
}


// ----------------------------------------------------------------------------------------

void gpio_clear_portf_pin( uint8_t pin )
{
    if ( ! open_memory_map() ) return;

    uint32_t* portf_data_ptr = gpio_mmap_ptr + gpio_offset_portf_data;
    *portf_data_ptr &= ~( 0x01 << pin );
}


// ----------------------------------------------------------------------------------------
//  end of spinwait.cpp
// ----------------------------------------------------------------------------------------

