#include "AP_ICEngine_config.h"

/*
  support for TCA9554 for starter control on I2C
 */

#if AP_ICENGINE_TCA9554_STARTER_ENABLED
#include "AP_ICEngine.h"

extern const AP_HAL::HAL& hal;

/*
 * TCA9554 output register mapping for PMB Rev E
 * P0 = PMU_EN - PMU output ON/OFF  (CN6 pin 2)
 * P1 = ECU_EN - Unused (previously Engine Kill Switch)
 * P2 = I2C_P2 - Unused
 * P3 = LED (active low)
 * P4 = PMU_START - Crank Direction (CN6 pin 5)
 * P5 = PMU_ARM  - Crank Signal (CN6 pin 6)
 * P6 = PMU_STAT_IN - Unused
 * P7 = PMU_STAT - Unused
 */
#define TCA9554_I2C_BUS      1
#define TCA9554_I2C_ADDR     0x20
#define TCA9554_OUTPUT       0x01  // Output Port register address. Outgoing logic levels
#define TCA9554_OUT_DEFAULT  0x30  // 0011 0000
#define TCA9554_CONF         0x03  // Configuration Port register address [0 = Output]
#define TCA9554_PINS         0xC2  // Set all used ports to outputs = 1100 0010

/*
  initialise TCA9554
 */
bool AP_ICEngine_TCA9554::TCA9554_init()
{
    dev_TCA9554 = std::move(hal.i2c_mgr->get_device(TCA9554_I2C_BUS, TCA9554_I2C_ADDR));
    if (!dev_TCA9554) {
        return false;
    }
    WITH_SEMAPHORE(dev_TCA9554->get_semaphore());

    // setup 1 checked registers
    dev_TCA9554->setup_checked_registers(1);

    dev_TCA9554->set_retries(10);

    // set outputs
    bool ret = dev_TCA9554->write_register(TCA9554_OUTPUT, TCA9554_OUT_DEFAULT);
    if (!ret) {
        return false;
    }
    ret = dev_TCA9554->write_register(TCA9554_CONF, TCA9554_PINS, true);
    if (!ret) {
        return false;
    }

    dev_TCA9554->set_retries(1);
    return true;
}

/*
  set the state of the i2c controller
 */
void AP_ICEngine_TCA9554::TCA9554_set(TCA9554_state_t value)
{
    const uint32_t now_ms = AP_HAL::millis();
    if (now_ms - last_reg_check_ms > 100) {
        /*
          register checking at 10Hz allows us to cope with the i2c
          device being power cycled after boot
         */
        last_reg_check_ms = now_ms;
        WITH_SEMAPHORE(dev_TCA9554->get_semaphore());
        dev_TCA9554->check_next_register();
    }

    if (value != last_state) {
        WITH_SEMAPHORE(dev_TCA9554->get_semaphore());
        // set outputs and status leds
        if (dev_TCA9554->write_register(TCA9554_OUTPUT, (~(value<<2) & 0x0C) | value)) {
            last_state = value;
        }
    }
}

void AP_ICEngine_TCA9554::set_starter(bool on)
{
    if (!initialised) {
        initialised = TCA9554_init();
        if (!initialised) {
            // waiting for power to PMU
            return;
        }
    }
    TCA9554_set(on? STARTER_ON : STARTER_OFF);
}

#endif // AP_ICENGINE_TCA9554_STARTER_ENABLED