/*
 *-----------------------------------------------------------------------------
 * The confidential and proprietary information contained in this file may
 * only be used by a person authorised under and to the extent permitted
 * by a subsisting licensing agreement from ARM Limited.
 *
 *            (C) COPYRIGHT 2010-2017   ARM Limited or its affiliates.
 *                ALL RIGHTS RESERVED
 *
 * This entire notice must be reproduced on all copies of this file
 * and copies of this file may only be made by a person if such person is
 * permitted to do so under the terms of a subsisting license agreement
 * from ARM Limited.
 *
 *      SVN Information
 *
 *      Checked In          : $Date: 2013-04-08 17:48:08 +0100 (Mon, 08 Apr 2013) $
 *
 *      Revision            : $Revision: 366193 $
 *
 *      Release Information : SSE050-r0p1-00rel0
 *-----------------------------------------------------------------------------
 */
/*
  Interrupt demonstration

  This test demonstrate interrupt generation using various peripherals.
  - using simple timer
  - using gpio with various configurations
  - using uart

*/

#include "SSE050.h"

#include "uart_stdout.h"
#include "SSE050_driver.h"
#include <stdio.h>
#include <string.h>


//
// Global Variables to track test progress
//

volatile uint32_t tx_count = 0;                 /*transmit counter */
volatile uint32_t rx_count = 0;                 /*receive counter */
const     char str_tx[12] = "hello world";      /*transmission string*/
const int uart_str_length = 11;
volatile char str_rx[12] ;             /*string that is received*/

volatile int irq_triggered;          /* Detected interrupt operation from GPIO IRQ handler */
volatile uint32_t timer_stopped = 0; /* timer irq executed and stopped */

//
// Demonstrations
//

int  TimerExample(void);    // Timer interrupt
int  GPIOIntExample(void);  // GPIO interrupt
void UartExample(void);     // UART interrupt
int  gpio0_id_check(void);  // Detect GPIO 0 present
int  timer0_id_check(void); // Detect Timer 0 present
int  uart1_id_check(void);  // Detect UART 1 present
int  Check_IRQNUM(void);    // Check number of interrupts

// ----------------------------------------------------------
// Main program
// ----------------------------------------------------------

int main (void)
{
  // UART init
  UartStdOutInit();  // Initialize UART0 for printf (retargeting)

  // Test banner message and revision number
  puts("\nSSE-050 - Interrupt Demo - revision $Revision: 366193 $\n");

  if (timer0_id_check()!=0)  puts ("Timer 0 not present. TimerExample skipped.");
  else                       TimerExample();    // Timer 0 interrupt example

  if (gpio0_id_check()!=0)   puts ("GPIO 0 not present. GPIOIntExample skipped.");
  else                       GPIOIntExample();  // GPIO PORT0 interrupt example

  if ((uart1_id_check()!=0))
     puts ("UART 1 not present. UartExample skipped.");
  else                       UartExample();     // Uart interrupt example

  if (Check_IRQNUM() == 0)
     puts ("** TEST PASSED ** \n");
  else
     puts ("** TEST FAILED ** \n");

  UartEndSimulation();    // send test end character to finish test
  /* Simulation stops in UartEndSimulation */

  return 0;
}

// ----------------------------------------------------------
// Check number of Interrupts
// ----------------------------------------------------------
int Check_IRQNUM()
{
  //
  // Number of implemented Interrupts
  //
  // Determine number of implemented interrupts by pending all interrupts
  // then reading back the pend register contents. Only configured interrupts will
  // show as pending. This checks if the number of implemented interrupts are
  // consistent with the values on NUMIRQ.
  //
  // NOTE : Higher interrupts in the context of this check refers to interrupts with
  //        a bigger exception number

  int numirq;
  int ispr_rd;
  int j;
  int EXPECTED_IRQNUM = 32;
  int fail = 1;

  // Ensure interrupts are disabled
  NVIC->ICER[0] = 0xFFFFFFFFUL;

  // Pend all interrupts
  NVIC->ISPR[0] = 0xFFFFFFFFUL;

  // Count pending interrupts
  numirq = 0;
  ispr_rd = NVIC->ISPR[0];
  for (j=0; j<32; j++)
    {
      if ((ispr_rd & 0x00000001) == 1) numirq++;
      ispr_rd >>= 1;
    }

  if (numirq != EXPECTED_IRQNUM)
    {
      printf ("\n\nIRQNUM: %u, expected %d\tFAIL\n\n", numirq, EXPECTED_IRQNUM);
      fail = 1;
    }
  else
    {
      printf ("\n\nIRQNUM: %u\t\tPASS\n\n", numirq);
      fail = 0;
    }

  // Clear all pending interrupts
  NVIC->ICPR[0] = 0xFFFFFFFFUL;
  return fail;
}

// ----------------------------------------------------------
// Timer demo
// ----------------------------------------------------------
int TimerExample(void)
{
  puts("\n\n\n");
  puts("+*************************+");
  puts("*                         *");
  puts("*  Timer0 Interrupt demo  *");
  puts("*                         *");
  puts("+*************************+\n\n");

  NVIC_ClearPendingIRQ(TIMER0_IRQn);
  NVIC_EnableIRQ(TIMER0_IRQn);

  // initialise Timer0 with internal clock, with interrupt generation
  SSE050_timer_Init_IntClock(SSE050_TIMER0, 0x100, 1);

  while (timer_stopped==0) {
    __WFE(); // enter sleep
    }
  puts("   Timer test done");  // Banner

  return 0;
}
// ----------------------------------------------------------
// UART demo
// ----------------------------------------------------------

/* Timer IRQ Driven UART Transmission of "hello world"

    - Program UART 2 to operate as transmit only, with transmit IRQ enabled
    - Program UART 3 to operate as receive only, with receive IRQ enabled
    - The first character of the "hello world" message is transmit
    - The rest of the message transmission is handled by UART transmit IRQ, until
      all characters are transmitted.
    - The receive process is also handled by UART receive IRQ.
    - A while loop is used to wait until both transmit and receive has completed the test
    - When finished then print string received
   */


// ----------------------------------------------------------
// UART interrupt test
// ----------------------------------------------------------

void UartExample(void)
{
  uint32_t transmission_complete = 0;    /*transmission complete bool*/

  puts("+*************************+");
  puts("*                         *");
  puts("*   UART Interrupt demo   *");
  puts("*                         *");
  puts("+*************************+\n\n");

  // Ensure Interrupt is not pending

  NVIC_ClearPendingIRQ(UART1_IRQn);

  // Enable Interrupts

  NVIC_EnableIRQ(UART1_IRQn);

  /* Initialize UART1 in self loop back configuration
   uint32_t SSE050_uart_init(SSE050_UART_TypeDef *SSE050_UART,
                             uint32_t            divider,
                             uint32_t            tx_en,
                             uint32_t            rx_en,
                             uint32_t            tx_irq_en,
                             uint32_t            rx_irq_en,
                             uint32_t            tx_ovrirq_en,
                             uint32_t            rx_ovrirq_en)
  */
  /* enable UARTs with selected baud rate
       UART #1 - transmit
       UART #1 - receive
  */
  SSE050_uart_init(SSE050_UART1, 0x200, 1, 1, 1, 1, 0, 0);

  rx_count = 0;
  tx_count = 0;

  printf ("Transmit message : %s\n", str_tx);

  /* Start first character transfer */
  tx_count++;
  SSE050_uart_SendChar(SSE050_UART1, str_tx[0]); // send the character
  /* The rest of the transfers are handled by interrupts */

  while(transmission_complete==0)    // loop until transmission completed
  {

    if ((tx_count==uart_str_length) && (rx_count==uart_str_length)) transmission_complete = 1;
  }

  printf ("Received message : %s\n", str_rx);

  while(strcmp((char *)str_rx, str_tx)){ // hang if received message != transmit message
  }

  NVIC_DisableIRQ(UART1_IRQn);   //disable both UART1 TX and RX IRQs

  return;
}

// ----------------------------------------------------------
// GPIO interrupt test
// ----------------------------------------------------------
/*
      GPIO interrupt example

    - Enable all pins as output
    - Set DataOut[10:7] to 0xA ready for a test of all IRQs
    - Set pin7 as a High Level, pin8 as a Low Level, pin9 as a Rising Edge and pin10 as a Falling Edge,
      then clear all pending IRQs
    - enable the SSE050 GPIO interrupt for pins 7, 8, 9, 10.
    - set Dataout[10:7] to 0xB to test the high level interrupt on pin 7
    - if irq_triggered != 0 (set in ISR) then print message saying IRQ occurred and set irq_triggered = 0
    - else amend err_code
    - set Dataout[10:7] to 0x9 to test the low level interrupt on pin 8
    - if irq_triggered != 0 (set in ISR) then print message saying IRQ occurred and set irq_triggered = 0
    - else amend err_code
    - set Dataout[10:7] to 0xD to test the rising edge interrupt on pin 9
    - if irq_triggered != 0 (set in ISR) then print message saying IRQ occurred and set irq_triggered = 0
    - else amend err_code
    - set Dataout[10:7] to 0x5 to test the falling edge interrupt on pin 10
    - if irq_triggered != 0 (set in ISR) then print message saying IRQ occurred and set irq_triggered = 0
    - else amend err_code
    - if test on all pins pass the test as a whole passes and return 0 to main
    - else return an error and print error message
*/
int GPIOIntExample(void)
{

  int irq_counter = 0;
  int err_code = 0;

  puts("\n\n\n");
  puts("+*************************+");
  puts("*                         *");
  puts("*  GPIO PORT0: Interrupt  *");
  puts("*         Example         *");
  puts("*                         *");
  puts("+*************************+\n\n");

  SSE050_gpio_SetOutEnable(SSE050_GPIO0, 0x780); //set output enable to output on ports [10:7] of GPIO 0
  // By setting the port to output the pins are controllable by software

  SSE050_GPIO0->DATAOUT = (0xA << 7);   // set current I/O port value

  SSE050_gpio_SetIntHighLevel(SSE050_GPIO0, 7);   //set pin 7 to high level interrupts
  SSE050_gpio_SetIntLowLevel(SSE050_GPIO0, 8);    //set pin 8 to low level interrupts
  SSE050_gpio_SetIntRisingEdge(SSE050_GPIO0, 9);  //set pin 9 to rising edge interrupts
  SSE050_gpio_SetIntFallingEdge(SSE050_GPIO0, 10); //set pin 10 to falling edge interrupts

  NVIC_ClearPendingIRQ(PORT0_7_IRQn);                   //clear all global NVIC PORT0 pending interrupts
  NVIC_ClearPendingIRQ(PORT0_8_IRQn);
  NVIC_ClearPendingIRQ(PORT0_9_IRQn);
  NVIC_ClearPendingIRQ(PORT0_10_IRQn);

  NVIC_EnableIRQ(PORT0_7_IRQn);                         //enable NVIC interrupts on PORT0
  NVIC_EnableIRQ(PORT0_8_IRQn);
  NVIC_EnableIRQ(PORT0_9_IRQn);
  NVIC_EnableIRQ(PORT0_10_IRQn);

  if ((NVIC->ISER[0]>>PORT0_7_IRQn)!=0x0F) {            // SSE-050 only has 16 IRQ
    printf("Not all of IRQ[%d to %d] are available.\nUse combined GPIO interrupt for test\n\n",
           PORT0_7_IRQn,PORT0_10_IRQn);
    NVIC_EnableIRQ(PORT0_ALL_IRQn);                     //enable combined NVIC interrupts on PORT0
    }

  SSE050_gpio_SetIntEnable(SSE050_GPIO0, 7);
  SSE050_GPIO0->DATAOUT = (0xB << 7); // emulating high level input on pin 0.
  SSE050_gpio_ClrIntEnable(SSE050_GPIO0, 7);

  puts("    ...Test GPIO0[7]...\n");

  if(irq_triggered){   //if irq flag set then print message else amend error code
    puts("      High Level IRQ:\n     Detected On Pin 7\n\n");
    irq_triggered = 0;
    irq_counter++;
  }
  else err_code |= (1 << irq_counter);

  SSE050_gpio_SetIntEnable(SSE050_GPIO0, 8);
  SSE050_GPIO0->DATAOUT = (0x9 << 7); // emulating low level input on pin 8.
  SSE050_gpio_ClrIntEnable(SSE050_GPIO0, 8);

  puts("    ...Test GPIO0[8]...\n");

  if(irq_triggered){  //if irq flag set then print message else amend error code
    puts("       Low Level IRQ\n     Detected On Pin 8\n\n");
    irq_triggered = 0;
    irq_counter++;
  }
  else err_code |= (1 << 1);

  SSE050_gpio_SetIntEnable(SSE050_GPIO0, 9);
  SSE050_GPIO0->DATAOUT = (0xD << 7); // emulating rising edge input on pin 9.
  SSE050_gpio_ClrIntEnable(SSE050_GPIO0, 9);

  puts("    ...Test GPIO0[9]...\n");

  if(irq_triggered){  //if irq flag set then print message else amend error code
    puts("      Rising Edge IRQ\n     Detected On Pin 9\n\n");
    irq_triggered = 0;
    irq_counter++;
  }
  else err_code |= (1 << 2);

  SSE050_gpio_SetIntEnable(SSE050_GPIO0, 10);
  SSE050_GPIO0->DATAOUT = (0x5 << 7); // emulating falling edge input on pin 10.
  SSE050_gpio_ClrIntEnable(SSE050_GPIO0, 10);

  puts("    ...Test GPIO0[10]...\n");

  if(irq_triggered){  //if irq flag set then print message else amend error code
    puts("     Falling Edge IRQ:\n     Detected On Pin 10\n\n");
    irq_triggered = 0;
    irq_counter++;
  }
   else err_code |= (1 << 3);

  /* check to see whether intstatus, for the specified pin, is 1, which corresponds to a rising edge interrupt */

  if(irq_counter == 4){
    printf("    All %d IRQs Detected\n\n", irq_counter);
  }


  // print pass or fail message depending on the status of the test

  if(err_code == 0){
    puts("\n");
    puts(" +***********************+");
    puts(" *                       *");
    puts(" *   GPIO 0 IRQ Tests    *");
    puts(" *  Passed Successfully  *");
    puts(" *                       *");
    puts(" +***********************+\n");
  }
  else{

    /*if the port did not have 1 of each IRQs as expected then display error*/

    printf("\n** TEST FAILED ** IRQ Tests Error Code: (0x%x\n", err_code);
  }

  NVIC_DisableIRQ(PORT0_7_IRQn);    //disable GPIO0 IRQ
  NVIC_DisableIRQ(PORT0_8_IRQn);
  NVIC_DisableIRQ(PORT0_9_IRQn);
  NVIC_DisableIRQ(PORT0_10_IRQn);
  NVIC_DisableIRQ(PORT0_ALL_IRQn);
  return err_code;
}

// ----------------------------------------------------------
// Peripheral detection
// ----------------------------------------------------------
/* Detect the part number to see if device is present                */

int gpio0_id_check(void)
{
#define HW32_REG(ADDRESS)  (*((volatile unsigned long  *)(ADDRESS)))
if ((HW32_REG(SSE050_GPIO0_BASE + 0xFE0) != 0x20) ||
    (HW32_REG(SSE050_GPIO0_BASE + 0xFE4) != 0xB8))
  return 1; /* part ID does not match */
else
  return 0;
}

int timer0_id_check(void)
{
if ((HW32_REG(SSE050_TIMER0_BASE + 0xFE0) != 0x22) ||
    (HW32_REG(SSE050_TIMER0_BASE + 0xFE4) != 0xB8))
  return 1; /* part ID does not match */
else
  return 0;
}

int uart1_id_check(void)
{
if ((HW32_REG(SSE050_UART1_BASE + 0xFE0) != 0x21) ||
    (HW32_REG(SSE050_UART1_BASE + 0xFE4) != 0xB8))
  return 1; /* part ID does not match */
else
  return 0;
}
// ----------------------------------------------------------
// Handlers
// ----------------------------------------------------------
// ---------------------------------
// UART 1 Interrupt service routines
// ---------------------------------
//

void UART1_Handler(void)
{
  if(SSE050_uart_GetTxIRQStatus(SSE050_UART1) & 0x1 == 1) {
    SSE050_uart_ClearTxIRQ(SSE050_UART1); // clear TX IRQ
    // If the message output is not finished, output next character
    if (tx_count < uart_str_length) {
      SSE050_uart_SendChar(SSE050_UART1,str_tx[tx_count]);
      tx_count++;
      }
  }
  if(SSE050_uart_GetRxIRQStatus(SSE050_UART1) & 0x2 == 2) {
    SSE050_uart_ClearRxIRQ(SSE050_UART1); //clear RX IRQ
    str_rx[rx_count]=SSE050_uart_ReceiveChar(SSE050_UART1); // Read data
    rx_count++;
  }
}

// ---------------------------------
// Timer 0 Interrupt service routines
// ---------------------------------

void TIMER0_Handler(void)
{
  timer_stopped = 1;                      // set timer stopped bool, so that
                                          // system does not wait for another interrupt
  SSE050_timer_StopTimer(SSE050_TIMER0);    // stop timer
  SSE050_timer_ClearIRQ(SSE050_TIMER0);     // clear timer 0 IRQ
  puts("   [Timer 0 IRQ]");
}

// ---------------------------------
// GPIO Port 0 Interrupt service routines
// ---------------------------------
//
void PORT0_7_Handler(void)
{
  irq_triggered = 1;                            /* high level */
  SSE050_GPIO0->DATAOUT = (0xA << 7);                  /* Deassert Port 0 pin 7 to 0 */
  SSE050_gpio_IntClear(SSE050_GPIO0, 7);        //clear GPIO interrupt on pin N
}

void PORT0_8_Handler(void)
{
  irq_triggered = 1;                            /*low level*/
  SSE050_GPIO0->DATAOUT = (0xB << 7);                  /* Deassert Port 0 pin 8 to 1 */
  SSE050_gpio_IntClear(SSE050_GPIO0, 8);        //clear GPIO interrupt on pin N
}

void PORT0_9_Handler(void)
{
  irq_triggered = 1;                            /*rising edge*/
  SSE050_gpio_IntClear(SSE050_GPIO0, 9);        //clear GPIO interrupt on pin N
}

void PORT0_10_Handler(void)
{
  irq_triggered = 1;                            /*falling edge*/
  SSE050_gpio_IntClear(SSE050_GPIO0, 10);        //clear GPIO interrupt on pin N
}

void PORT0_COMB_Handler(void)   /* Combined handler */
{
  irq_triggered = 1;
  if (SSE050_GPIO0->INTSTATUS & 0x80){ /* high level */
    SSE050_GPIO0->DATAOUT = (0xA << 7);                  /* Deassert Port 0 pin 7 to 0 */
    SSE050_gpio_IntClear(SSE050_GPIO0, 7);        //clear GPIO interrupt on pin N
    }
  if (SSE050_GPIO0->INTSTATUS & 0x100){ /* low level*/
    SSE050_GPIO0->DATAOUT = (0xB << 7);                  /* Deassert Port 0 pin 8 to 1 */
    SSE050_gpio_IntClear(SSE050_GPIO0, 8);        //clear GPIO interrupt on pin N
    }
  if (SSE050_GPIO0->INTSTATUS & 0x200){ /* rising edge*/
    SSE050_gpio_IntClear(SSE050_GPIO0, 9);         //clear GPIO interrupt on pin N
    }
  if (SSE050_GPIO0->INTSTATUS & 0x400){ /* falling edge*/
    SSE050_gpio_IntClear(SSE050_GPIO0, 10);         //clear GPIO interrupt on pin N
    }
  return;
}

