/*                                              -*- mode:C++; fill-column:100 -*-
 */
#include "LPC23xx.h"		/* LPC23xx/24xx Peripheral Registers	*/
#include "clock.h"              /* For getCurrentClockConfiguration */
#include "SFBPower.h"           /* For adcSamples, etc */
#include "irq.h"
#include "timer.h"
#include "SFBWiring.h"
#include "SFBErrors.h"

/**
 * Timer interrupt design
 *
 * (Version of Thu Jun 25 12:29:57 2009).
 *
 * Goals:
 *
 * - Support #millis() efficiently, and as accurately as possible
 *
 * - Support #micros() efficiently, and as accurately as possible
 *
 * - Support continuous sampling of (certain) ADC samples.
 *
 * Strategy Part A: Configure Timer0 to count at precisely 1MHz, with no resetting.  So #micros() is
 * simply 'return current timer0 count', which is just about the best possible efficiency and
 * accuracy.
 *
 * Issue 1: Could we implement #millis() as 'micros()/1000'?  No, because (1) that would require a
 * (non-trivial, not power-of-2) division on every millis() call, and (2) because it would wrap
 * around after less than two hours.  So, instead:
 *
 * Strategy Part B: Also configure a timer 0 match register to interrupt (but not reset) 1000 usecs
 * in the future.  When the interrupt goes off, increment the #millis() counter by one, and
 * increment the match register value by 1000.  That way, the interrupt will fire at 1KHz without
 * resetting the Timer0 count used to implement #micros().
 *
 * Issue 2: What do we do, in the interrupt, if we increment the match register by 1000, but after
 * that discover it's still in the past WRT the timer0 count?  This absolutely happens, due to
 * periods of disabled interrupts.  We can't just leave the match register behind the counter,
 * because then the timer interrupt won't fire again for hours.  We have to advance the match
 * register by additional 1000's until it's ahead of the counter.  But that raises:
 *
 * Issue 3: As we increment the match register by extra 1000's to catch up after a late interrupt,
 * do we also increment the #millis() counter by extra 1's?  If we \e don't do those extra
 * increments, then #millis() will tend to fall behind micros()/1000, so it seems like we should.
 * But, if we \e do those extra increments, then from sketch level #millis() may be observed jumping
 * by more than one, even in a tight delay loop.  In the sample codes I've played with, that is bad.
 * So instead:
 *
 * Strategy Part C: Accept that #millis() may fall behind micros()/1000 due to interrupt load.  After
 * all, that's (analogous to) what happens on the Arduino.  But to counterbalance that, we provide
 * another function #milliseconds(), which is something like micros()/1000 (only better) -- in other
 * words, #milliseconds() is unaffected by interrupt load and is accurate to the same level as
 * #micros(), but at the cost of doing a non-trivial division on every call.  But that raises:
 *
 * Issue 4: Now we have a weird incomplete set of functions: #millis() vs #micros(), and
 * #milliseconds() vs nothing.  Symmetry demands we add #microseconds()!  But, #micros() was already
 * maximally fast and maximally accurate -- what could #microseconds() possibly bring to the party?
 * The answer: 
 *
 * Strategy Part D: The big liability of #micros() is that it rolls over every 70 minutes.  If
 * #milliseconds() was actually implemented as #micros()/1000, it would too.  But since we're adding
 * a new #microseconds() method anyway, with no backward-compatibility constraints, let's have it
 * return a u64.  A u64 of microseconds won't roll over for half a billion years.  And to count the
 * high order 32 bits of #microseconds(), we can just draft a second match register on timer 0, set
 * it to interrupt when timer0 rolls over, and pay very little for the added range.  So then
 * #milliseconds() is actually defined as \c microseconds()/1000 (cast to a u32).  
 */


#define SHIFT_AND_CLIP(reg) (((reg) >> 6) & 0x03ff)
#define ADC0DATA  SHIFT_AND_CLIP(AD0DR0)
#define ADC1DATA  SHIFT_AND_CLIP(AD0DR1)
#define ADC2DATA  SHIFT_AND_CLIP(AD0DR2)
#define ADC3DATA  SHIFT_AND_CLIP(AD0DR3)
#define ADC4DATA  SHIFT_AND_CLIP(AD0DR4)
#define ADC5DATA  SHIFT_AND_CLIP(AD0DR5)

volatile u32 timer_1msec_counter IN_FASTRAM = 0;
volatile u32 usecOverflows IN_BODYRAM_BSS;
u8 timer_next_vsample_idx IN_FASTRAM = 0;

#define AMUX_SELA_BITNUM 2
#define AMUX_SELB_BITNUM 1
#define AMUX_SELC_BITNUM 0

#define AMUX_SELA_BIT (1<<AMUX_SELA_BITNUM)
#define AMUX_SELB_BIT (1<<AMUX_SELB_BITNUM)
#define AMUX_SELC_BIT (1<<AMUX_SELC_BITNUM)

const u8 vsenses[VSENSE_IDX_COUNT] = {
    AMUX_SELC_BIT,               // NORTH_VSENSE==LLH
    0,                           // SOUTH_VSENSE==LLL
    AMUX_SELB_BIT|AMUX_SELC_BIT, // EAST_VSENSE=LHH
    AMUX_SELB_BIT,               // WEST_VSENSE=LHL
    AMUX_SELA_BIT                // VEE_RAIL_VSENSE=HLL
//    AMUX_SELA_BIT|AMUX_SELC_BIT  // VCC_RAIL_VSENSE=HLH
};

u64 microseconds() {

  u32 usecHi;
  u32 usecLo;

  // To avoid wraparound glitches, sample micros() up to twice, until the number of overflows before
  // the sample is the same as the number of overflows after the sample.

  for (usecHi = usecOverflows; 1; usecHi = usecOverflows) {
    usecLo = micros(); 
    if (usecHi == usecOverflows)
      break;
  } 

  return (((u64) usecHi)<<32) + usecLo;
}

u32 milliseconds() {
  return (u32) (microseconds()/1000);
}

u32 seconds() {
  return (u32) (microseconds()/1000000);
}

static u32 lastMicros = 0;

/* NOTE: This handler is currently running at 2KHz, so, 2 traps per ms!  If that's just way too slow
   for the ADC's, we'll need to reevaluate! */
static void timer0Handler (void) {  
  static u8 sample = 0;

  if (T0IR&TxIR_MR1_Interrupt) { /* MR1 matches at 0 */ 
    ++usecOverflows;             /* meaning counter overflowed.. */ 
    T0IR = TxIR_MR1_Interrupt;   /* flush that interrupt flag */
  }

  if (T0IR&TxIR_MR0_Interrupt)   /* MR0 matches for 2KHz.. */ 
    T0IR = TxIR_MR0_Interrupt;   /* Clear that flag, continue */
  else
    return;                      /* WTF?  Not our interrupt */ 

  bool incr = false;
  do {
    lastMicros += 500;         /* Advance to next interrupt time.. */ 

    if ((sample&1)==0) {       // True every other sample
      if (!incr) {
        ++timer_1msec_counter;  // No more than one ms tick per interrupt
        incr = true;
      }
    }

    sample = (sample+1)&7;   // Eight sampling periods no matter how many channels sampled

  } while (IS_EARLIER_OR_EQUAL(lastMicros,T0TC));  /* ..catching up if late */

  T0MR0 = lastMicros;           /* update to next interrupt time */

  if (isADCSensingEnabled()) {
    /* Accumulate adc measurements (face power draws + next selected voltage) */

    if (isPowerSensingEnabled()) {
      for (u32 i = 0; i < 4; ++i) {
        u16 sample = REGISTER_OFFSET(AD0_BASE_ADDR,0x10+(i<<2)); // AD0DR0..AD0DR3
        adcLatestSample[NORTH_ISENSE_ADC_IDX+i] = sample; 
        UPDATE_SMOOTHED_BUFFER(adcSmooth[NORTH_ISENSE_ADC_IDX+i],sample);
      }
    }

    /* Sample the external analog mux and set it up for next measurement */
    {
      u16 sample = AD0DR4;
      adcLatestSample[timer_next_vsample_idx] = sample;
      UPDATE_SMOOTHED_BUFFER(adcSmooth[timer_next_vsample_idx],sample);
      
      if (++timer_next_vsample_idx >= VSENSE_IDX_COUNT)
        timer_next_vsample_idx = 0;
      implDigitalWrite(BODY_AMUX_SELA_PIN, (vsenses[timer_next_vsample_idx]>>AMUX_SELA_BITNUM)&1);
      implDigitalWrite(BODY_AMUX_SELB_PIN, (vsenses[timer_next_vsample_idx]>>AMUX_SELB_BITNUM)&1);
      implDigitalWrite(BODY_AMUX_SELC_PIN, (vsenses[timer_next_vsample_idx]>>AMUX_SELC_BITNUM)&1);
    }
  }
}

void enable_timer0() {
  PCONP |= PCTIM0;
  T0TCR = 1;
  VICIntEnable = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_Timer0);
}


void disable_timer0() {
  PCONP |= PCTIM0; 
  T0TCR = 0;
  PCONP &= ~PCTIM0; 
  VICIntEnClr = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_Timer0);
}

void reset_timer0() {
  u32 regVal;
  regVal = T0TCR;
  regVal |= TxMCR_MR0R;
  T0TCR = regVal;
}

void reinitTimer0() {
  const ClockConfiguration * cc = getCurrentClockConfiguration();
  API_ASSERT_NONNULL(cc);

  T0PR = cc->pclkMHz-1;         /* prescale: pick up current pclk, set up for 1usec tick */ 
}

u32 initTimer0() {

  //  pinMode(BODY_AMUX_SELA_PIN,OUTPUT);
  //  pinMode(BODY_AMUX_SELB_PIN,OUTPUT);
  //  pinMode(BODY_AMUX_SELC_PIN,OUTPUT);
  // We are going to set up so timer0 ticks once per microsecond, to
  // give us a tiny and fast micros() implementation.  
  //
  // CCLK == 72MHz, PCLK == CCLK/4 == 18MHz, so we want to prescale
  // that down to 1MHz, so we divide PCLK by 18
  //
  // With T0TC incrementing at 1MHz, we then want the timer interrupt
  // going off every 500 ticks, to produce 2KHz sampling

  PCONP |= PCTIM0;              /* heat up the timer */
  
  reinitTimer0();                /* set up prescaler */ 

  usecOverflows = 0;            /* the usec counter has never overflowed */ 

  T0TC = 1;                     /* start micros counter at 1 to avoid immediate overflow */ 

  T0MR0 = 500-1;                /* set first interrupt point */

  T0MR1 = 0;                    /* set second interrupt point */

  lastMicros = 500;             /* record as our last 'event' */ 

  T0MCR = TxMCR_MR0I|TxMCR_MR1I; /* Interrupt on MR0 or MR1 */

  return install_irq(TIMER0_INT, timer0Handler, HIGHEST_PRIORITY);
}

