diff options
Diffstat (limited to 'firmware/e300/rev_c/ltc4155.c')
-rw-r--r-- | firmware/e300/rev_c/ltc4155.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/firmware/e300/rev_c/ltc4155.c b/firmware/e300/rev_c/ltc4155.c new file mode 100644 index 000000000..5f404e651 --- /dev/null +++ b/firmware/e300/rev_c/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 |