
#include "gpio.h"
#include "common.h"

// CSL - Chip support library. A suffix "R" means register (C macros based). The alternative is "C functions".
//       CSL can also work without SYS/BIOS, but the hardware interrupts are not compatible. check
//       http://processors.wiki.ti.com/index.php/Chip_support_library
#include <ti/sysbios/hal/Hwi.h>                         // Interrupts.
#include <ti/sysbios/family/c64p/EventCombiner.h>       // Interrupt combiner.
#include <ti/sysbios/family/c66/tci66xx/CpIntc.h>       // Mapping interrupts.

static CSL_GpioHandle gpio_handle[2]; // The are two banks: 0 for pins 0-15
                                      //                    1 for pins 16-31

#define KICK0 (*(unsigned int*)(0x02620038))            // Need to de-multiplex the GPIOs above 15.
#define KICK1 (*(unsigned int*)(0x0262003C))            // See also init_hardware().
#define KICK0_KEYVAL 0x83e70b13                         // Inspired https://e2e.ti.com/support/dsp/c6000_multi-core_dsps/f/639/t/272135
#define KICK1_KEYVAL 0x95a4f1e0
#define PIN_CONTROL_0 (*(unsigned int*)(0x02620580))

CSL_GpioHandle gpio_init( uint32_t pin, uint8_t* bank_number )
{
  static uint32_t pin_control_0 = 0; // None of the pins 16-31 is enabled by default.
  uint8_t bn = pin > 15 ? 1 : 0;
  if( gpio_handle[bn] == NULL )
    gpio_handle[bn] = CSL_GPIO_open( bn );
  if( bank_number != NULL )
    *bank_number = bn;
  if( pin > 15 )
  {
    uint32_t uno = 1;
    pin_control_0 |= (uno << pin);
    KICK0 = KICK0_KEYVAL;
    KICK1 = KICK1_KEYVAL;
    PIN_CONTROL_0 = pin_control_0;
  }
  return gpio_handle[bn];
}

CSL_GpioHandle gpio_config_output_pin( uint32_t pin )
{
  CSL_GpioHandle gpio = gpio_init( pin, NULL );
  CSL_GPIO_setPinDirOutput( gpio, pin ); // Set GPIO pin as an output pin
  return gpio;
}

CSL_GpioHandle gpio_config_input_pin( uint32_t pin )
{
  CSL_GpioHandle gpio = gpio_init( pin, NULL );
  CSL_GPIO_setPinDirInput( gpio, pin ); // Set GPIO pin as an input pin
  return gpio;
}

CSL_GpioHandle gpio_pin_clear( uint32_t pin )
{
  CSL_GpioHandle gpio = gpio_init( pin, NULL );
  CSL_GPIO_clearOutputData( gpio, pin );
  return gpio;
}

CSL_GpioHandle gpio_pin_set( uint32_t pin )
{
  CSL_GpioHandle gpio = gpio_init( pin, NULL );
  CSL_GPIO_setOutputData( gpio, pin );
  return gpio;
}

PinStatus_T gpio_read_pin( CSL_GpioHandle gpio, uint32_t pin )
{
  if( gpio == NULL ) gpio = gpio_init( pin, NULL );
  uint8_t pin_state = 0;
  CSL_GPIO_getInputData( gpio, pin, &pin_state );
  return pin_state ? High : Low;
}

CSL_GpioHandle gpio_config_irq_pin( uint32_t pin, EdgeType_T edge_type )
{
  uint8_t bank_number = 0;
  CSL_GpioHandle gpio = gpio_init( pin, &bank_number );
  if( edge_type == RisingEdge )
    CSL_GPIO_setRisingEdgeDetect( gpio, pin );       // Set interrupt detection on GPIO pin to rising edge
  else
    CSL_GPIO_setFallingEdgeDetect( gpio, pin );      // Set interrupt detection on GPIO pin to falling edge
  gpio_config_input_pin( pin );                      // Set GPIO IRQ to be input
  CSL_GPIO_bankInterruptEnable( gpio, bank_number ); // Enable GPIO per bank interrupt for bank zero
  return gpio;
}

void config_gpio_irq( int int_vec, int host_int_num, int32_t gpio_pin, void (*isr)(UArg), EdgeType_T edge_type )
{
  const int cic0 = 0;
  Hwi_Params hwi_params;
  Error_Block error_block;
  Error_init( &error_block );
  gpio_config_irq_pin( gpio_pin, edge_type );

  int input_event_num = CSL_INTC0_GPINT16;
  switch( gpio_pin )
  {
    case  2: input_event_num =    CSL_GEM_GPINT2; break; case  3: input_event_num =    CSL_GEM_GPINT3; break;
    case  4: input_event_num =    CSL_GEM_GPINT4; break; case  5: input_event_num =    CSL_GEM_GPINT5; break;
    case  6: input_event_num =    CSL_GEM_GPINT6; break; case  7: input_event_num =    CSL_GEM_GPINT7; break;
    case  8: input_event_num =    CSL_GEM_GPINT8; break; case  9: input_event_num =    CSL_GEM_GPINT9; break;
    case 10: input_event_num =   CSL_GEM_GPINT10; break; case 11: input_event_num =   CSL_GEM_GPINT11; break;
    case 12: input_event_num =   CSL_GEM_GPINT12; break; case 13: input_event_num =   CSL_GEM_GPINT13; break;
    case 14: input_event_num =   CSL_GEM_GPINT14; break; case 15: input_event_num =   CSL_GEM_GPINT15; break;
    case 16: input_event_num = CSL_INTC0_GPINT16; break; case 17: input_event_num = CSL_INTC0_GPINT17; break;
    case 18: input_event_num = CSL_INTC0_GPINT18; break; case 19: input_event_num = CSL_INTC0_GPINT19; break;
    case 20: input_event_num = CSL_INTC0_GPINT20; break; case 21: input_event_num = CSL_INTC0_GPINT21; break;
    case 22: input_event_num = CSL_INTC0_GPINT22; break; case 23: input_event_num = CSL_INTC0_GPINT23; break;
    case 24: input_event_num = CSL_INTC0_GPINT24; break; case 25: input_event_num = CSL_INTC0_GPINT25; break;
    case 26: input_event_num = CSL_INTC0_GPINT26; break; case 27: input_event_num = CSL_INTC0_GPINT27; break;
    case 28: input_event_num = CSL_INTC0_GPINT28; break; case 29: input_event_num = CSL_INTC0_GPINT29; break;
    case 30: input_event_num = CSL_INTC0_GPINT30; break; case 31: input_event_num = CSL_INTC0_GPINT31; break;
  }

  uint32_t hwi_status = Hwi_disable();
  if( gpio_pin < 16 )
  {
    EventCombiner_dispatchPlug( host_int_num, isr, host_int_num, TRUE );  // EventCombiner. An argument should be host interrupt number.
    Hwi_Params_init( &hwi_params );                                       // Initialize the Hwi parameters.
    hwi_params.eventId     = input_event_num;                             // EventId is a primary one.
    hwi_params.arg         = hwi_params.eventId;
    hwi_params.enableInt   = TRUE;
    hwi_params.maskSetting = Hwi_MaskingOption_SELF;
    Hwi_Handle hwi = Hwi_create( int_vec, isr, &hwi_params, &error_block ); // Register ISR to int_vec.
    if( Error_check( &error_block ) || hwi == NULL )
    {
      // Fehler.
    }
  }
  else
  {
    CpIntc_disableAllHostInts( cic0 );                                    // Disable CIC0 at once
    CpIntc_clearSysInt( cic0, input_event_num );                          // Clear existing event flag and enable it.
    CpIntc_mapSysIntToHostInt( cic0, input_event_num, host_int_num );     // Map system event to host interrupt
    CpIntc_dispatchPlug( input_event_num, isr, input_event_num, TRUE );   // Register ISR for input_event_num.
    Int eventId = CpIntc_getEventId( host_int_num );                      // Convert host interrupt to event for CorePac
    EventCombiner_dispatchPlug( eventId, &CpIntc_dispatch, host_int_num, TRUE );  // Register CpIntc dispatcher to
                                                                          // EventCombiner. An argument should be host
                                                                          // interrupt number.
    Hwi_Params_init( &hwi_params );                                       // Initialize the Hwi parameters.
    hwi_params.eventId     = eventId / 32;                                // EventCombiner assumes 0 <= eventId <= 3.
    hwi_params.arg         = hwi_params.eventId;
    hwi_params.enableInt   = TRUE;
    hwi_params.maskSetting = Hwi_MaskingOption_SELF;
    Hwi_Handle hwi = Hwi_create( int_vec, &EventCombiner_dispatch, &hwi_params, &error_block ); // Register EventCombiner dispatcher
    if( Error_check( &error_block ) || hwi == NULL )                                            // to int_vec.
    {
      // Fehler.
    }
    CpIntc_enableHostInt( cic0, host_int_num );                           // Enable host interrupt.
    CpIntc_enableAllHostInts( cic0 );                                     // Enable global host interrupt.
    CpIntc_enableSysInt( cic0, input_event_num );                         // Now setup completed. Enable system event.
  }
  Hwi_restore( hwi_status );
}
