/*
 * ltc4155.c
 */ 

#ifndef CHARGER_TI

#include "config.h"
#include "ltc4155.h"

#include <util/delay.h>

#include "io.h"
#include "i2c.h"
#include "power.h"
#include "debug.h"
#include "global.h"
#include "error.h"

static io_pin_t USBPM_IRQ	= IO_PB(1);

#ifdef ATTINY88_DIP

static io_pin_t CHRG_SDA     = IO_PC(2);
static io_pin_t CHRG_SCL     = IO_PC(3);

#else

#ifdef I2C_REWORK

static io_pin_t CHRG_SDA     = IO_PC(4);
static io_pin_t CHRG_SCL     = IO_PC(5);

#else

#define CHRG_SDA	PWR_SDA
#define CHRG_SCL	PWR_SCL

#endif // I2C_REWORK

#endif // ATTINY88_DIP

const bool _ltc4155_pull_up = false;

#define LTC4155_BASE_ADDRESS    0x12
#define LTC4155_WRITE_ADDRESS   (LTC4155_BASE_ADDRESS + 0)
#define LTC4155_READ_ADDRESS    (LTC4155_BASE_ADDRESS + 1)
/*
#define LTC4155_RETRY_DELAY     1   // us MAGIC
#define LTC4155_MAX_ACK_RETRIES 10  // * LTC4155_RETRY_DELAY us

#define LTC4155_SCL_LOW_PERIOD  2   // 1.3 us
#define LTC4155_SCL_HIGH_PERIOD 1   // 0.6 us
#define LTC4155_BUS_FREE_TIME   2   // 1.3 us
#define LTC4155_STOP_TIME       1   // 0.6 us
*/
enum LTC4155Registers
{
	LTC4155_REG_USB			= 0x00,	// W/R
	LTC4155_REG_WALL		= 0x01,	// W/R
	LTC4155_REG_CHARGE		= 0x02,	// W/R
	LTC4155_REG_STATUS		= 0x03,	// R
	LTC4155_REG_GOOD		= 0x04,	// R
	LTC4155_REG_THERMISTOR	= 0x05,	// R
	LTC4155_REG_ENABLE		= 0x06,	// W/R
	LTC4155_REG_ARM_AND_SHIP= 0x07	// W
};

enum LTC4155InterruptMasks	// LTC4155_REG_ENABLE
{
	LTC4155_ENABLE_USB_OTG	= 1 << 1,
	
	LTC4155_INT_UVCL	= 1 << 2,
	LTC4155_INT_ILIMIT	= 1 << 3,
	LTC4155_INT_USB_OTG	= 1 << 4,
	LTC4155_INT_EXT_PWR	= 1 << 5,
	LTC4155_INT_FAULT	= 1 << 6,
	LTC4155_INT_CHARGER	= 1 << 7
};

enum LTC4155Options	// LTC4155_REG_USB
{
	LTC4155_USB_OTG_LOCKOUT				= 1 << 5,
	LTC4155_ENABLE_BATTERY_CONDITIONER	= 1 << 6,
	LTC4155_DISABLE_INPUT_UVCL			= 1 << 7
};

enum LTC4155Shifts
{
	LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT	= 4,
	LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE	= 2,
	LTC4155_SHIFTS_WALL_PRIORITY		= 7,
	LTC4155_SHIFTS_WALL_SAFETY_TIMER	= 5
};

enum LTC4155Statuses	// LTC4155_REG_STATUS
{
	LTC4155_LOW_BATTERY		= 1 << 0,
	LTC4155_BOOST_ENABLE	= 1 << 3,
	LTC4155_ID_PIN_DETECT	= 1 << 4,
};

enum LTC4155Goods	// LTC4155_REG_GOOD
{
	LTC4155_BAD_CELL_FAULT		= 1 << 0,
	LTC4155_OTG_FAULT			= 1 << 1,
	LTC4155_OVP_ACTIVE			= 1 << 2,
	LTC4155_INPUT_UVCL_ACTIVE	= 1 << 3,
	LTC4155_INPUT_CURRENT_LIMIT_ACTIVE = 1 << 4,
	LTC4155_WALLSNS_GOOD		= 1 << 5,
	LTC4155_USBSNS_GOOD			= 1 << 6,
	LTC4155_EXTERNAL_POWER_GOOD	= 1 << 7
};

enum LTC4155BatteryChargerStatues
{
	LTC4155_CHARGER_OFF,
	LTC4155_CHARGER_LOW_BATTERY_VOLTAGE,
	LTC4155_CHARGER_CONSTANT_CURRENT,
	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX,
	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX,
	LTC4155_CHARGER_NTC_TOO_WARM,
	LTC4155_CHARGER_NTC_TOO_COLD,
	LTC4155_CHARGER_NTC_HOT
};

enum LTC4155ThermistorStatuses
{
	LTC4155_NTC_NORMAL,
	LTC4155_NTC_TOO_COLD,
	LTC4155_NTC_TOO_WARM,
	LTC4155_NTC_FAULT
};

static const uint8_t _ltc4155_interrupt_mask =
//	LTC4155_ENABLE_USB_OTG |// Enable +5V on USB connector	// Is this causing the chip to power off the output?!
	LTC4155_INT_UVCL |
	LTC4155_INT_ILIMIT |
	LTC4155_INT_USB_OTG |
	LTC4155_INT_EXT_PWR |	// Turn up current limit
	LTC4155_INT_FAULT |		// Blink error
	LTC4155_INT_CHARGER;	// Illuminate charge LED

static bool _ltc4155_clear_irq(void)
{
	return i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, _ltc4155_interrupt_mask, _ltc4155_pull_up);
}

bool ltc4155_clear_irq(void)
{
	pmc_mask_irqs(true);
	
	bool result = _ltc4155_clear_irq();
	
	pmc_mask_irqs(false);
	
	return result;
}

static uint8_t _ltc4155_last_good, _ltc4155_last_status;

bool _ltc4155_handle_irq(void)
{
	_ltc4155_clear_irq();	// Clear frozen registers to get the real-time ones
	
	_delay_ms(50);	// Wait for registers to clear/update
	
	//////////////////
	
	uint8_t val = 0x00;
	bool result = false;
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
		goto _ltc4155_handle_fail;
	
	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
	//	goto _ltc4155_handle_fail;
	
	debug_log_ex("4155GO ", false);
	debug_log_hex(val);
	
	if (val & LTC4155_WALLSNS_GOOD)
	{
		uint8_t wall_state = 0;
		if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false)
			goto _ltc4155_handle_fail;
		
		wall_state &= ~0x1E;
		wall_state |= 0x0E;
		
		if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
			goto _ltc4155_handle_fail;
		
		debug_log("I+");
	}
	
	_ltc4155_last_good = val;
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
		goto _ltc4155_handle_fail;
	
	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
	//	goto _ltc4155_handle_fail;
	
	debug_log_ex("4155ST ", false);
	debug_log_hex(val);
	
	_ltc4155_last_status = val;
	
	val >>= 5;
	
	if (_state.blink_error == BlinkError_None)
	{
		switch (val)
		{
			case LTC4155_CHARGER_CONSTANT_CURRENT:
			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX:
			case LTC4155_CHARGER_LOW_BATTERY_VOLTAGE:	// If this persists for more than 1/2hr, BAD_CELL_FAULT is enabled and FAULT interrupt is generated
			{
				if ((_state.battery_not_present == false) &&
					(_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD)))
				{
					//charge_set_led(true);
					charge_notify(true);
					break;
				}						
			}
			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX:	// Small amount of current still charging the battery but below Vc/x threshold
			//case LTC4155_CHARGER_NTC_TOO_WARM:
			//case LTC4155_CHARGER_NTC_TOO_COLD:
			//case LTC4155_CHARGER_NTC_HOT:
			//	break;
			//case LTC4155_CHARGER_OFF:
			default:
				//charge_set_led(false);
				charge_notify(false);
		}
	}
	
//	ltc4155_dump();
	
	result = true;
_ltc4155_handle_fail:
	_ltc4155_clear_irq();	// Even though it happens first above, this is necessary otherwise future IRQs won't be detected
	
	return result;
}

#define LTC4155_CHARGE_CURRENT_LIMIT	/*0xF*/0x7	// [100%] 50%

bool ltc4155_set_charge_current_limit(uint8_t percentage)
{
	uint8_t val = 0;
	uint8_t limit = 0;
	
	if (percentage > 100)
		return false;
	else if (percentage == 100)
		percentage = 0xF;
	else if (percentage > 12)	// 0..88 -> 0..8800
	{
		uint16_t l = (((uint16_t)percentage - 12) * 100) / 586;
		limit = (uint8_t)l;
	}
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, &val, _ltc4155_pull_up) == false)
		return false;
	
	val &= ((0x1 << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT) - 1);
	//val |= (LTC4155_CHARGE_CURRENT_LIMIT << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
	val |= (limit << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
	
	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, val, _ltc4155_pull_up) == false)
		return false;
	
//ltc4155_dump();
	
	return true;
}

bool ltc4155_init(bool disable_charger)
{
	io_input_pin(USBPM_IRQ);
#if !defined(DEBUG) && !defined(ATTINY88_DIP)
	io_set_pin(USBPM_IRQ);	// Enable pull-up for Open Drain
#endif // DEBUG
#ifdef I2C_REWORK
	i2c_init_ex(CHRG_SDA, CHRG_SCL, _ltc4155_pull_up);
#endif // I2C_REWORK
	if (/*_ltc4155_clear_irq()*/_ltc4155_handle_irq() == false)	// Will set interrupt masks	// FIXME: Why does this cause instability?!
		return false;

	const uint8_t charge_state =
		(disable_charger ? 0x0 : LTC4155_CHARGE_CURRENT_LIMIT) << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT |	// Battery charger I limit = 100%
		0x3 << LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE |	// FIXME: Vbatt float = 4.05V - 4.2V for LiPo (default 0x00)
		0x0;	// Full capacity charge threshold = 10%
	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, charge_state, _ltc4155_pull_up) == false)
		return false;

	const uint8_t wall_state =
		0x0 << LTC4155_SHIFTS_WALL_PRIORITY |
		0x0 << LTC4155_SHIFTS_WALL_SAFETY_TIMER |	// Charge safety timer = 4hr	// FIXME: 8hr or Vc/x
		0xE;	// 3 amps, 0x1F - CLPROG1
	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
		return false;

	// FIXME:
	// Disable ID pin detection & autonomous startup
	// Enable OTG
	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_USB, LTC4155_USB_OTG_LOCKOUT, _ltc4155_pull_up);	// Disable autonomous startup
	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, LTC4155_ENABLE_USB_OTG, _ltc4155_pull_up);	// Enable OTG
	
	if (_ltc4155_handle_irq() == false)	// One more time (IRQ LED stays lit in dev setup)
		return false;
	
	return true;
}

bool ltc4155_has_interrupt(void)
{
	//bool state = io_test_pin(USBPM_IRQ);
	//debug_log_ex("4155IRQ", false);
	//debug_log_byte(state);
	//return (state != 1);
	return (io_test_pin(USBPM_IRQ) == false);
}

bool ltc4155_handle_irq(void)
{
	pmc_mask_irqs(true);
	
	bool result = _ltc4155_handle_irq();
	
	pmc_mask_irqs(false);
	
	return result;
}

bool ltc4155_arm_ship_and_store(void)
{
	return true;
}

bool ltc4155_get_thermistor(uint8_t* val, bool* warning)
{
	bool result = false;
	uint8_t _val = 0;
	
	pmc_mask_irqs(true);
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_THERMISTOR, &_val, _ltc4155_pull_up) == false)
		goto ltc4155_get_thermistor_fail;
	
	if (val)
		(*val) = _val >> 1;
	
	if (warning)
		(*warning) = ((_val & 0x01) != 0x00);
	
	result = true;
ltc4155_get_thermistor_fail:
	pmc_mask_irqs(false);
	return result;
}

void ltc4155_dump(void)
{
	pmc_mask_irqs(true);
	
	uint8_t val = 0x00;
	bool warning = false;
	
	if (ltc4155_get_thermistor(&val, &warning) == false)
		goto ltc4155_dump_fail;
	
	debug_log_ex("\tTHRM", false);
	if (warning)
		debug_log_ex("!", false);
	debug_log_byte(val);
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &val, _ltc4155_pull_up) == false)
		goto ltc4155_dump_fail;
	
	debug_log_ex("\tWALL", false);
	debug_log_hex(val);
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
		goto ltc4155_dump_fail;
	
	debug_log_ex("\t4155GO ", false);
	debug_log_hex(val);
	
	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
		goto ltc4155_dump_fail;
	
	debug_log_ex("\t4155ST ", false);
	debug_log_hex(val);
	
ltc4155_dump_fail:
	pmc_mask_irqs(false);
}

#endif // !CHARGER_TI