/*
****************************************************************************
PROJECT : device driver for V-Lib
FILE    : $Id: r_dev_pin.c 5139 2015-05-27 13:21:40Z tobyas.hennig $
============================================================================
DESCRIPTION
r7f701412 interrupt service routines
============================================================================
                            C O P Y R I G H T
============================================================================
                       Copyright (c) 2013 - 2014
                                  by
                       Renesas Electronics (Europe) GmbH.
                           Arcadiastrasse 10
                          D-40472 Duesseldorf
                               Germany
                          All rights reserved.
============================================================================
Purpose: only for testing, not for mass production

DISCLAIMER

LICENSEE has read, understood and accepted the terms and conditions defined in
the license agreement, especially the usage rights. In any case, it is
LICENSEE's responsibility to make sure that any user of the software complies
with the terms and conditions of the signed license agreement.

SAMPLE CODE is not part of the licensed software, as such it must not be used in
mass-production applications. It can only be used for evaluation and
demonstration purposes at customer's premises listed in the signed license
agreement.

****************************************************************************
*/

/*******************************************************************************
  Title: upd703670 port-pin init  functions


*/

/*******************************************************************************
  Section: Includes
*/

#include "r_typedefs.h"
#include "r_dev_api.h"
#include "r_config_pin.h"

/*******************************************************************************
  Section: Local Constants
*/

/*******************************************************************************
  PORTC Register Offsets
*/

/************************************************************************************************

Port registers
  <see r_dev_PinRegs_t> in the file "r_dev_pin_types.h".

*/

/*******************************************************************************
Array : R_GPIO_Offset_Table

The R_GPIO_Offset_Table array specifies  Register offset in a Macro
*/

static const uint32_t loc_RegOffset [ R_DEV_PIN_REG_LAST ] = {
    /*R_DEV_PIN_REG_PCR  */ 0x2000u,

    /*R_DEV_PIN_REG_P    */ 0x0000u,
    /*R_DEV_PIN_REG_PSR  */ 0x0004u,
    /*R_DEV_PIN_REG_PPR  */ 0x000Cu,
    /*R_DEV_PIN_REG_PM   */ 0x0010u,
    /*R_DEV_PIN_REG_PMC  */ 0x0014u,
    /*R_DEV_PIN_REG_PFC  */ 0x0018u,
    /*R_DEV_PIN_REG_PFCE */ 0x001Cu,
    /*R_DEV_PIN_REG_PNOT */ 0x0008u,
    /*R_DEV_PIN_REG_PMSR */ 0x0020u,
    /*R_DEV_PIN_REG_PMCSR*/ 0x0024u,

    /*  OS  */
    /*R_DEV_PIN_REG_PIBC */ (R_DEV_PIN_BASEO_OFFSET + 0x4000u),
    /*R_DEV_PIN_REG_PBDC */ (R_DEV_PIN_BASEO_OFFSET + 0x4004u),
    /*R_DEV_PIN_REG_PIPC */ (R_DEV_PIN_BASEO_OFFSET + 0x4008u),
    /*R_DEV_PIN_REG_PU   */ (R_DEV_PIN_BASEO_OFFSET + 0x400Cu),
    /*R_DEV_PIN_REG_PD   */ (R_DEV_PIN_BASEO_OFFSET + 0x4010u),
    /*R_DEV_PIN_REG_PODC */ (R_DEV_PIN_BASEO_OFFSET + 0x4014u),
    /*R_DEV_PIN_REG_PDSC */ (R_DEV_PIN_BASEO_OFFSET + 0x4018u),
    /*R_DEV_PIN_REG_PIS  */ (R_DEV_PIN_BASEO_OFFSET + 0x401Cu),
    /*R_DEV_PIN_REG_PISA */ (R_DEV_PIN_BASEO_OFFSET + 0x4024u),
    /*R_DEV_PIN_REG_PPCMD*/ (R_DEV_PIN_BASEO_OFFSET + 0x402Cu),
    /*R_DEV_PIN_REG_PPROTS*/ (R_DEV_PIN_BASEO_OFFSET + 0x4034u),
};

static const r_dev_PortInputCapability_t loc_PortCapability [] =
    /*  Port    InputType11,        InputType10,        InputType01,        InputType00 */
    {
        {0u, R_DEV_PIN_TTL, R_DEV_PIN_TTL, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {1u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {2u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {3u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {16u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {17u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {21u, R_DEV_PIN_TTL_MLB, R_DEV_PIN_TTL_MLB, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1},
        {42u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {43u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {46u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},
        {47u, R_DEV_PIN_CMOS1, R_DEV_PIN_CMOS1, R_DEV_PIN_SCHMITT4, R_DEV_PIN_SCHMITT1},

        /* delimiter - do not remove */
        {0xFFu, R_DEV_PIN_NONE, R_DEV_PIN_NONE, R_DEV_PIN_NONE, R_DEV_PIN_NONE},
};

/*******************************************************************************
  Section: Local types
*/

/*******************************************************************************
  Section: Local variables
*/
static uint8_t loc_PinEnable = 1;

/*******************************************************************************
  Section: Global functions
*/
/*******************************************************************************
  Function: R_DEV_PinOutStateGet

  See: r_dev_api.h for details

 */
void R_DEV_PinOutStateSet(uint8_t State)
{
    loc_PinEnable = State;
}
/*******************************************************************************
  Function: R_DEV_PinOutStateGet

  See: r_dev_api.h for details

*/
uint8_t R_DEV_PinOutStateGet(void)
{
    return loc_PinEnable;
}

/*******************************************************************************
  Function: R_DEV_PinConfig

  See: r_dev_api.h for details

*/

uint8_t R_DEV_PinConfig(const r_dev_PinConfig_t *PinCfg)
{
    uint32_t addr                = 0u;
    uint32_t prot_addr           = 0u;
    uint32_t reg_val             = 0u;
    uint8_t  ret_val             = 1u;
    uint16_t pin_position        = 1u;
    uint8_t  port_position       = 0u;
    uint8_t  portHasPisaRegister = 0u;
    uint8_t  portHasPisRegister  = 0u;
    uint8_t  PisaRegBitValue     = 0u;
    uint8_t  PisRegBitValue      = 0u;

    if ( 0 == loc_PinEnable )
    {
        return ret_val;
    }
    pin_position = ( uint16_t )(pin_position << PinCfg->Pin);
    /* optional check if already configured */
    /* read PMC, if 1 -> already set to other alternative mode, abort */
    /* read PM, if not 1 -> already set to output, abort (this is in case of pure
     * GPIO) */

    /* Input or output  */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PM, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( R_DEV_PIN_OUT == PinCfg->Dir )
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    else /* Set direction to input also in case of direct control */
    {
        reg_val |= pin_position;
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    /* direct control */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PIPC, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( R_DEV_PIN_DIRECT == PinCfg->Dir )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    /* activate pull-up / pull-down resistors */
    /*  set pull-up */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PU, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( R_DEV_PIN_PULLUP == PinCfg->Pull )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);
    /*  set pull-down */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PD, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( R_DEV_PIN_PULLDOWN == PinCfg->Pull )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    /* feedback input (bidirectional mode) */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PBDC, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( 1 == PinCfg->Feedback )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    /*Set mode to GPIO or alternative function */
    switch ( PinCfg->Func )
    {
        case 0: /* GPIO */
            /*Set PFC and PFCE bits to 0 */
            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFC, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);

            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFCE, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);
            break;
        case 1: /* Alternative function 1 */
            /*Set PFC and PFCE bits to 0 */
            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFC, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);

            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFCE, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);
            break;
        case 2: /* Alternative function 2 */
            /*Set PFC bit to 1  and PFCE bit to 0 */
            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFC, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val |= pin_position;
            R_DEV_WRITE_REG(16, addr, reg_val);

            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFCE, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);
            break;
        case 3: /* Alternative function 3 */
            /*Set PFC bit to 0  and PFCE bit to 1 */
            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFC, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val &= ( uint16_t )(~pin_position);
            R_DEV_WRITE_REG(16, addr, reg_val);

            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFCE, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val |= pin_position;
            R_DEV_WRITE_REG(16, addr, reg_val);
            break;
        case 4: /* Alternative function 4 */
            /*Set PFC and PFCE bits to 1 */
            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFC, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val |= pin_position;
            R_DEV_WRITE_REG(16, addr, reg_val);

            addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PFCE, PinCfg->Port);
            reg_val = R_DEV_READ_REG(16, addr);
            reg_val |= pin_position;
            R_DEV_WRITE_REG(16, addr, reg_val);
            break;
        default:
            R_DEV_ERROR(0u, R_DEV_ERR_NG, "We do not have what you want!");
            break;
    }

    /* Some electrical charactersitics */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PDSC, PinCfg->Port);
    reg_val = R_DEV_READ_REG(32, addr);
    if ( 0 != PinCfg->HiSpeed )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    prot_addr = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PPCMD, PinCfg->Port);
    R_DEV_WRITE_PROT_REG(prot_addr, addr, reg_val);

    /* Activate pin */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PMC, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( 0 != PinCfg->Func )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    /* Set input characteristic */
    port_position       = 0u;
    portHasPisaRegister = 1u;
    portHasPisRegister  = 1u;
    PisaRegBitValue     = 0u;
    PisRegBitValue      = 0u;
    do
    {
        if ( loc_PortCapability [ port_position ].Port == PinCfg->Port )
        {
            if ( loc_PortCapability [ port_position ].InputType00 == PinCfg->InputType )
            {
                PisaRegBitValue = 0;
                PisRegBitValue  = 0;
            }
            if ( loc_PortCapability [ port_position ].InputType01 == PinCfg->InputType )
            {
                PisaRegBitValue = 0;
                PisRegBitValue  = 1;
                if ( loc_PortCapability [ port_position ].InputType01 == loc_PortCapability [ port_position ].InputType00 )
                {
                    portHasPisRegister = 0;
                }
            }
            if ( loc_PortCapability [ port_position ].InputType10 == PinCfg->InputType )
            {
                PisaRegBitValue = 1;
                PisRegBitValue  = 0;
            }
            if ( loc_PortCapability [ port_position ].InputType11 == PinCfg->InputType )
            {
                PisaRegBitValue = 1;
                PisRegBitValue  = 1;
                if ( loc_PortCapability [ port_position ].InputType11 == loc_PortCapability [ port_position ].InputType10 )
                {
                    portHasPisRegister = 0;
                }
            }

            break;
        }
        port_position += 1;
    } while ( 0xFFu != loc_PortCapability [ port_position ].Port );

    if ( 0 != portHasPisaRegister )
    {
        addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PIS, PinCfg->Port);
        reg_val = R_DEV_READ_REG(16, addr);
        if ( 0 != PisaRegBitValue )
        {
            reg_val |= pin_position;
        }
        else
        {
            reg_val &= ( uint16_t )(~pin_position);
        }
        R_DEV_WRITE_REG(16, addr, reg_val);
    }

    if ( 0 != portHasPisRegister )
    {
        addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PIS, PinCfg->Port);
        reg_val = R_DEV_READ_REG(16, addr);
        if ( 0 != PisRegBitValue )
        {
            reg_val |= pin_position;
        }
        else
        {
            reg_val &= ( uint16_t )(~pin_position);
        }
        R_DEV_WRITE_REG(16, addr, reg_val);
    }

    /* Activate input buffer in case of GP input  */
    addr    = R_DEV_PinGetRegAddr(R_DEV_PIN_REG_PIBC, PinCfg->Port);
    reg_val = R_DEV_READ_REG(16, addr);
    if ( (0 == PinCfg->Func) && (R_DEV_PIN_IN == PinCfg->Dir) )
    {
        reg_val |= pin_position;
    }
    else
    {
        reg_val &= ( uint16_t )(~pin_position);
    }
    R_DEV_WRITE_REG(16, addr, reg_val);

    return ret_val;
}

/*******************************************************************************
  Function: R_DEV_PinInit

  See: r_dev_api.h for details
*/
void R_DEV_PinInit(const r_dev_PinConfig_t *PinCfg)
{
    uint16_t i = 0u;
    /* Here we can glbally disable any pion configuration */
    if ( 0 == loc_PinEnable )
    {
        return;
    }
    /* assuming we got a config table, we go through it until
    we see the delimiter */
    while ( R_DEV_PIN_LAST != PinCfg [ i ].Pin )
    {
        R_DEV_PinConfig(&(PinCfg [ i ]));
        i++;
    }
}

/*******************************************************************************
  Function: R_DEV_PinGetRegAddr

  See: r_dev_api.h for details
*/
uint32_t R_DEV_PinGetRegAddr(r_dev_PinRegs_t Reg, uint32_t Port)
{
    return R_DEV_PinGetRegAddrExt(Reg, Port, 0u);
}

/*******************************************************************************
  Function: R_DEV_PinGetRegAddrExt

  See: r_dev_api.h for details
*/
uint32_t R_DEV_PinGetRegAddrExt(r_dev_PinRegs_t Reg, uint32_t Port, uint32_t Pin)
{
    uint32_t addr = R_DEV_PIN_BASE + (R_DEV_PORT_OFFSET * Port);

    if ( Reg < R_DEV_PIN_REG_LAST )
    {
        if ( Port == 0 )
        {
            /* add offset for port n = 0 */
            addr += R_DEV_PIN_BASEOU_0_OFFSET;
        }
        if ( R_DEV_PIN_REG_PCR == Reg )
        {
            /* only for PCRn register, that has pin based control */
            addr += (R_DEV_PIN_OFFSET * Pin);
        }
        addr += loc_RegOffset [ Reg ];
    }
    else
    {
    }
    return addr;
}