From 1b149f561370687ad65e3aa644a402f00dbd16ea Mon Sep 17 00:00:00 2001
From: Martin Braun <martin.braun@ettus.com>
Date: Tue, 7 Oct 2014 11:32:14 +0200
Subject: Initial commit E300 support.

---
 firmware/e300/rev_b/ltc4155.c | 402 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 402 insertions(+)
 create mode 100644 firmware/e300/rev_b/ltc4155.c

(limited to 'firmware/e300/rev_b/ltc4155.c')

diff --git a/firmware/e300/rev_b/ltc4155.c b/firmware/e300/rev_b/ltc4155.c
new file mode 100644
index 000000000..5f404e651
--- /dev/null
+++ b/firmware/e300/rev_b/ltc4155.c
@@ -0,0 +1,402 @@
+/*
+ * 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
-- 
cgit v1.2.3