/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2019 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"
#include "avr/pgmspace.h"

static uint8_t calibrating;
static uint8_t analog_config_bits = 10;
static uint8_t analog_num_average = 4;


const uint8_t pin_to_channel[] = { // pg 482
	7,	// 0/A0  AD_B1_02
	8,	// 1/A1  AD_B1_03
	12,	// 2/A2  AD_B1_07
	11,	// 3/A3  AD_B1_06
	6,	// 4/A4  AD_B1_01
	5,	// 5/A5  AD_B1_00
	15,	// 6/A6  AD_B1_10
	0,	// 7/A7  AD_B1_11
	13,	// 8/A8  AD_B1_08
	14,	// 9/A9  AD_B1_09
	1,	// 24/A10 AD_B0_12 
	2,	// 25/A11 AD_B0_13
	128+3,	// 26/A12 AD_B1_14 - only on ADC2, 3
	128+4,	// 27/A13 AD_B1_15 - only on ADC2, 4
	7,	// 14/A0  AD_B1_02
	8,	// 15/A1  AD_B1_03
	12,	// 16/A2  AD_B1_07
	11,	// 17/A3  AD_B1_06
	6,	// 18/A4  AD_B1_01
	5,	// 19/A5  AD_B1_00
	15,	// 20/A6  AD_B1_10
	0,	// 21/A7  AD_B1_11
	13,	// 22/A8  AD_B1_08
	14,	// 23/A9  AD_B1_09
	1,	// 24/A10 AD_B0_12
	2,	// 25/A11 AD_B0_13
	128+3,	// 26/A12 AD_B1_14 - only on ADC2, 3
	128+4,	// 27/A13 AD_B1_15 - only on ADC2, 4
#ifdef ARDUINO_TEENSY41
	255,	// 28
	255,	// 29
	255,	// 30
	255,	// 31
	255,	// 32
	255,	// 33
	255,	// 34
	255,	// 35
	255,	// 36
	255,	// 37
	128+1,	// 38/A14 AD_B1_12 - only on ADC2, 1
	128+2,	// 39/A15 AD_B1_13 - only on ADC2, 2
	9,	// 40/A16 AD_B1_04
	10,	// 41/A17 AD_B1_05
#endif
};


static void wait_for_cal(void)
{
	//printf("wait_for_cal\n");
	while ((ADC1_GC & ADC_GC_CAL) || (ADC2_GC & ADC_GC_CAL)) {
		yield();
	}
	// TODO: check CALF, but what do to about CAL failure?
	calibrating = 0;
	//printf("cal complete\n");
}


int analogRead(uint8_t pin)
{
	// TODO: what happens if a program calls analogRead() from both main
	// program and interrupts?  On Teensy 3.x this came up and code was
	// added to allow analogRead() to work (or at least not hang) in
	// when used from interrupts & main program.
	if (pin > sizeof(pin_to_channel)) return 0;
	if (calibrating) wait_for_cal();
	uint8_t ch = pin_to_channel[pin];
	if (ch == 255) return 0;
	// check if pin has input "keeper"
	volatile uint32_t *pad = portControlRegister(pin);
	uint32_t padval = *pad;
	if ((padval & (IOMUXC_PAD_PUE | IOMUXC_PAD_PKE)) == IOMUXC_PAD_PKE) {
		// disable keeper, as it messes up analog with higher source impedance
		// but don't touch user's setting if ordinary pullup, which some
		// people use together with capacitors or other circuitry
		*pad = padval & ~IOMUXC_PAD_PKE;
	}
//	printf("%d\n", ch);
//	if (ch > 15) return 0;
	if(!(ch & 0x80)) {
		ADC1_HC0 = ch;
		while (!(ADC1_HS & ADC_HS_COCO0)) {
			yield(); // TODO: what happens if yield-called code uses analogRead()
		}
		return ADC1_R0;
	} else {
		ADC2_HC0 = ch & 0x7f;
		while (!(ADC2_HS & ADC_HS_COCO0)) {
			yield(); // TODO: what happens if yield-called code uses analogRead()
		}
		return ADC2_R0;
	}
}

void analogReference(uint8_t type __attribute__((unused)))
{
}

void analogReadRes(unsigned int bits)
{
  uint32_t tmp32, mode;

   if (bits == 8) {
    // 8 bit conversion (17 clocks) plus 8 clocks for input settling
    mode = ADC_CFG_MODE(0) | ADC_CFG_ADSTS(3);
  } else if (bits == 10) {
    // 10 bit conversion (17 clocks) plus 20 clocks for input settling
    mode = ADC_CFG_MODE(1) | ADC_CFG_ADSTS(2) | ADC_CFG_ADLSMP;
  } else {
    // 12 bit conversion (25 clocks) plus 24 clocks for input settling
    mode = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP;
  }

  tmp32  = (ADC1_CFG & (0xFFFFFC00));
  tmp32 |= (ADC1_CFG & (0x03));  // ADICLK
  tmp32 |= (ADC1_CFG & (0xE0));  // ADIV & ADLPC

  tmp32 |= mode; 
  ADC1_CFG = tmp32;
  
  tmp32  = (ADC2_CFG & (0xFFFFFC00));
  tmp32 |= (ADC2_CFG & (0x03));  // ADICLK
  tmp32 |= (ADC2_CFG & (0xE0));  // ADIV & ADLPC

  tmp32 |= mode; 
  ADC2_CFG = tmp32;
}

void analogReadAveraging(unsigned int num)
{
  uint32_t mode, mode1;
  
  //disable averaging, ADC1 and ADC2
  ADC1_GC &= ~0x20;
  mode = ADC1_CFG & ~0xC000;
  ADC2_GC &= ~0x20;
  mode1 = ADC2_CFG & ~0xC000;
  
    if (num >= 32) {
      mode |= ADC_CFG_AVGS(3);
      mode1 |= ADC_CFG_AVGS(3);

    } else if (num >= 16) {
      mode |= ADC_CFG_AVGS(2);
      mode1 |= ADC_CFG_AVGS(2);

    } else if (num >= 8) {
      mode |= ADC_CFG_AVGS(1);
      mode1 |= ADC_CFG_AVGS(1);

    } else if (num >= 4) {
      mode |= ADC_CFG_AVGS(0);
      mode1 |= ADC_CFG_AVGS(0);

    } else {
      mode |= 0;
      mode1 |= 0;
    }

  ADC1_CFG = mode;
  ADC2_CFG = mode1;
  
  if(num >= 4){
      ADC1_GC |= ADC_GC_AVGE;// turns on averaging
      ADC2_GC |= ADC_GC_AVGE;// turns on averaging
  }
}

#define MAX_ADC_CLOCK 20000000

FLASHMEM void analog_init(void)
{
	uint32_t mode, avg=0;

	printf("analogInit\n");

	CCM_CCGR1 |= CCM_CCGR1_ADC1(CCM_CCGR_ON);
	CCM_CCGR1 |= CCM_CCGR1_ADC2(CCM_CCGR_ON);
	
	if (analog_config_bits == 8) {
		// 8 bit conversion (17 clocks) plus 8 clocks for input settling
		mode = ADC_CFG_MODE(0) | ADC_CFG_ADSTS(3);
	} else if (analog_config_bits == 10) {
		// 10 bit conversion (17 clocks) plus 20 clocks for input settling
		mode = ADC_CFG_MODE(1) | ADC_CFG_ADSTS(2) | ADC_CFG_ADLSMP;
	} else {
		// 12 bit conversion (25 clocks) plus 24 clocks for input settling
		mode = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP;
	}
	if (analog_num_average >= 4) {
		if (analog_num_average >= 32) {
			mode |= ADC_CFG_AVGS(3);
		} else if (analog_num_average >= 16) {
			mode |= ADC_CFG_AVGS(2);
		} else if (analog_num_average >= 8) {
			mode |= ADC_CFG_AVGS(1);
		}
		avg = ADC_GC_AVGE;
	}
#if 1
	mode |= ADC_CFG_ADIV(1) | ADC_CFG_ADICLK(3); // async clock
#else
	uint32_t clock = F_BUS;
	if (clock > MAX_ADC_CLOCK*8) {
		mode |= ADC_CFG_ADIV(3) | ADC_CFG_ADICLK(1); // use IPG/16
	} else if (clock > MAX_ADC_CLOCK*4) {
		mode |= ADC_CFG_ADIV(2) | ADC_CFG_ADICLK(1); // use IPG/8
	} else if (clock > MAX_ADC_CLOCK*2) {
		mode |= ADC_CFG_ADIV(1) | ADC_CFG_ADICLK(1); // use IPG/4
	} else if (clock > MAX_ADC_CLOCK) {
		mode |= ADC_CFG_ADIV(0) | ADC_CFG_ADICLK(1); // use IPG/2
	} else {
		mode |= ADC_CFG_ADIV(0) | ADC_CFG_ADICLK(0); // use IPG
	}
#endif
	//ADC1
	ADC1_CFG = mode | ADC_CFG_ADHSC;
	ADC1_GC = avg | ADC_GC_CAL;		// begin cal
	calibrating = 1;
	while (ADC1_GC & ADC_GC_CAL) {
		//yield();
	}
	calibrating = 0;
	//ADC2
	ADC2_CFG = mode | ADC_CFG_ADHSC;
	ADC2_GC = avg | ADC_GC_CAL;		// begin cal
	calibrating = 1;
	while (ADC2_GC & ADC_GC_CAL) {
		//yield();
	}
	calibrating = 0;
}


