text: /*!
  LTC3589: 8-Output Regulator with Sequencing and I2C
  
  @verbatim
  
  The LTC3589 is a complete power management solution for ARM and ARM-based
  processors and advanced portable microprocessor systems. The device contains
  three step-down DC/DC converters for core, memory and SoC rails, a
  buck-boost regulator for I/O at 1.8V to 5V and three 250mA LDO regulators
  for low noise analog supplies. An I2C serial port is used to control enables,
  output voltage levels, dynamic voltage scaling, operating modes and status
  reporting.
  Regulator start-up is sequenced by connecting outputs to enable pins in the
  desired order or programmed via the I2C port. System power-on, power-off,
  and reset functions are controlled by pushbutton interface, pin inputs, or
  I2C interface.
  The LTC3589 supports i.MX53/51, PXA and OMAP processors with eight
  independent rails at appropriate power levels. Other features include
  interface signals such as the VSTB pin that simultaneously toggle up to
  four rails between programmed run and standby output voltages.  The device
  is available in a low profile 40-pin 6mm x 6mm exposed pad QFN package.
  
  I2C DATA FORMAT (MSB FIRST)
  
  @endverbatim
  
  http://www.linear.com/product/LTC3589
  
  http://www.linear.com/product/LTC3589#demoboards
  
  
  Copyright 2018(c) Analog Devices, Inc.
  
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   - Neither the name of Analog Devices, Inc. nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.
   - The use of this software may or may not infringe the patent rights
     of one or more patent holders.  This license does not release you
     from the requirement that you obtain separate licenses from these
     patent holders to use this software.
   - Use of the software either in source or binary form, must be run
     on or directly connected to an Analog Devices Inc. component.
  
  THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  
  //! @ingroup Switching_Regulators
  //! @{
  //! @defgroup LTC3589 LTC3589: 8-Output Regulator with Sequencing and I2C
  //! @}
  
  /*! @file
      @ingroup LTC3589
      Library for LTC3589: 8-Output Regulator with Sequencing and I2C
  */
  
  #include <Arduino.h>
  #include <stdint.h>
  #include <math.h>
  #include "Linduino.h"
  #include "LT_I2C.h"
  #include "LTC3589.h"
  
  // Reads an 8-bit register from the LTC3589 using the standard repeated start format.
  int8_t LTC3589_register_read(uint8_t i2c_address, uint8_t register_address, uint8_t *register_data)
  {
    int8_t ack = 0;
    ack = i2c_read_byte_data(i2c_address, register_address, register_data);
    return(ack);
  }
  
  // Writes to an 8-bit register inside the LTC3589 using the standard I2C repeated start format.
  int8_t LTC3589_register_write(uint8_t i2c_address, uint8_t register_address, uint8_t register_data)
  {
    int8_t ack = 0;
    ack = i2c_write_byte_data(i2c_address, register_address, register_data);
    return(ack);
  }
  
  // Sets any bit inside the LTC3589 using the standard I2C repeated start format.
  int8_t LTC3589_bit_set(uint8_t i2c_address, uint8_t register_address, uint8_t bit_number)
  {
    uint8_t register_data;
    uint8_t bit_mask;
    int8_t ack = 0;
    bit_mask = 0x01 << bit_number;
    ack |= LTC3589_register_read(i2c_address, register_address, &register_data);  //Read register
    register_data = register_data | bit_mask;  //Set the bit at bit_address
    ack |= LTC3589_register_write(i2c_address, register_address, register_data);  //Write new data to register
    return(ack);
  }
  
  // Clears any bit inside the LTC3589 using the standard I2C repeated start format.
  int8_t LTC3589_bit_clear(uint8_t i2c_address, uint8_t register_address, uint8_t bit_number)
  {
    uint8_t register_data;
    uint8_t bit_mask;
    int8_t ack = 0;
    bit_mask = 0x01 << bit_number;
    ack |= LTC3589_register_read(i2c_address, register_address, &register_data);  //Read register
    register_data = register_data & (~bit_mask);  //Clears the bit at bit_address
    ack |= LTC3589_register_write(i2c_address, register_address, register_data);  //Write new data to register
    return(ack);
  }
  
  // Writes any bit inside the LTC3589 using the standard I2C repeated start format.
  int8_t LTC3589_bit_write(uint8_t i2c_address, uint8_t register_address, uint8_t bit_number, uint8_t bit_data)
  {
    uint8_t register_data;
    uint8_t bit_mask;
    int8_t ack = 0;
    bit_mask = 0x01 << bit_number;
    ack |= LTC3589_register_read(i2c_address, register_address, &register_data);  //Read register
    register_data = register_data & (~bit_mask);  //Clears the bit at bit_address
    register_data = register_data | ((bit_data & 0x01) << bit_number);  //Writes the new bit
    ack |= LTC3589_register_write(i2c_address, register_address, register_data);  //Write new data to register
    return(ack);
  }
  
  // Reads the value of any bit in any register or the LTC3589.
  uint8_t LTC3589_bit_is_set(uint8_t i2c_address, uint8_t register_address, uint8_t bit_number)
  {
    uint8_t register_data;
    int8_t ack = 0;
    ack |= LTC3589_register_read(i2c_address, register_address, &register_data);
    register_data = register_data >> bit_number;  //Clears everything but the bit of interest
    return (register_data & 0x01);
  }
  
  
  // Sets the output voltage of LDO2.
  float LTC3589_set_ldo2_output_voltage(uint8_t i2c_address, uint8_t register_address, float output_voltage)
  {
    uint8_t register_data;
    float fb_ref = 0;
    int8_t nack = 0;
    if (register_address == 0x32 | register_address == 0x33)
    {
      fb_ref = output_voltage / (1 + LTC3589_RES_LDO2_RTOP/LTC3589_RES_LDO2_RBOT);  //Generates desired FB reference input for given output voltage.
      return (1 + LTC3589_RES_LDO2_RTOP/LTC3589_RES_LDO2_RBOT) * LTC3589_set_ldo2_fb_ref(i2c_address, register_address, fb_ref);  //Writes new buck output fb refernce.
    }
  }
  
  // Sets the output voltage of any buck.
  float LTC3589_set_buck_output_voltage(uint8_t i2c_address, uint8_t register_address, float output_voltage)
  {
    uint8_t register_data;
    float fb_ref = 0;
    int8_t nack = 0;
    if (register_address == 0x23 | register_address == 0x24)
    {
      fb_ref = output_voltage / (1 + LTC3589_RES_BUCK1_RTOP/LTC3589_RES_BUCK1_RBOT);  //Generates desired FB reference input for given output voltage.
      return (1 + LTC3589_RES_BUCK1_RTOP/LTC3589_RES_BUCK1_RBOT) * LTC3589_set_buck_fb_ref(i2c_address, register_address, fb_ref);  //Writes new buck output fb refernce.
    }
    if (register_address == 0x26 | register_address == 0x27)
    {
      fb_ref = output_voltage / (1 + LTC3589_RES_BUCK2_RTOP/LTC3589_RES_BUCK2_RBOT);  //Generates desired FB reference input for given output voltage.
      return (1 + LTC3589_RES_BUCK2_RTOP/LTC3589_RES_BUCK2_RBOT) * LTC3589_set_buck_fb_ref(i2c_address, register_address, fb_ref);  //Writes new buck output fb refernce.
    }
    if (register_address == 0x29 | register_address == 0x2A)
    {
      fb_ref = output_voltage / (1 + LTC3589_RES_BUCK3_RTOP/LTC3589_RES_BUCK3_RBOT);  //Generates desired FB reference input for given output voltage.
      return (1 + LTC3589_RES_BUCK3_RTOP/LTC3589_RES_BUCK3_RBOT) * LTC3589_set_buck_fb_ref(i2c_address, register_address, fb_ref);  //Writes new buck output fb refernce.
    }
  }
  
  // Writes the Feedback Reference Voltage of LDO2.
  float LTC3589_set_ldo2_fb_ref(uint8_t i2c_address, uint8_t register_address, float fb_ref_voltage)
  {
    uint8_t register_data;
    uint8_t fb_ref_data = 0;
    int8_t nack = 0;
    fb_ref_data = (uint8_t) ((fb_ref_voltage + 6.25 - 362.5) / 12.5);  //Generates 5-bit code for closest allowable value to user's chosen reference voltage.
    nack |= LTC3589_register_read(i2c_address, register_address, &register_data); //Read current register value
    register_data = register_data & (~LTC3589_FB_REF_MASK); //Clears existing feedback reference bits
    register_data = register_data | fb_ref_data;
    nack |= LTC3589_register_write(i2c_address, register_address, register_data);  //Writes new register value
    nack |= LTC3589_set_go_bits(i2c_address);
    return (362.5 + (fb_ref_data * 12.5));
  }
  
  // Writes the Feedback Reference Voltage of any buck.
  float LTC3589_set_buck_fb_ref(uint8_t i2c_address, uint8_t register_address, float fb_ref_voltage)
  {
    uint8_t register_data;
    uint8_t fb_ref_data = 0;
    int8_t nack = 0;
    fb_ref_data = (uint8_t) ((fb_ref_voltage + 6.25 - 362.5) / 12.5);  //Generates 5-bit code for closest allowable value to user's chosen reference voltage.
    nack |= LTC3589_register_read(i2c_address, register_address, &register_data); //Read current register value
    register_data = register_data & (~LTC3589_FB_REF_MASK); //Clears existing feedback reference bits
    register_data = register_data | fb_ref_data;
    nack |= LTC3589_register_write(i2c_address, register_address, register_data);  //Writes new register value
    nack |= LTC3589_set_go_bits(i2c_address);
    return (362.5 + (fb_ref_data * 12.5));
  }
  
  // Calculates the maximum output voltage of LDO2 in mV based on the feedback resistors.
  float LTC3589_ldo2_vout_max()
  {
    return ((1 + (LTC3589_RES_LDO2_RTOP/LTC3589_RES_LDO2_RBOT)) * 750);
  }
  
  // Calculates the minimum output voltage of LDO2 mV based on the feedback resistors.
  float LTC3589_ldo2_vout_min()
  {
    return ((1 + (LTC3589_RES_LDO2_RTOP/LTC3589_RES_LDO2_RBOT)) * 362.5);
  }
  
  // Calculates the maximum output voltage of any buck in mV based on the feedback resistors.
  float LTC3589_buck_vout_max(uint8_t buck_number)
  {
    switch (buck_number)
    {
      case 1:
        return ((1 + (LTC3589_RES_BUCK1_RTOP/LTC3589_RES_BUCK1_RBOT)) * 750);
        break;
      case 2:
        return ((1 + (LTC3589_RES_BUCK2_RTOP/LTC3589_RES_BUCK2_RBOT)) * 750);
        break;
      case 3:
        return ((1 + (LTC3589_RES_BUCK3_RTOP/LTC3589_RES_BUCK3_RBOT)) * 750);
        break;
    }
  }
  
  // Calculates the minimum output voltage of any buck in mV based on the feedback resistors.
  float LTC3589_buck_vout_min(uint8_t buck_number)
  {
    switch (buck_number)
    {
      case 1:
        return ((1 + LTC3589_RES_BUCK1_RTOP/LTC3589_RES_BUCK1_RBOT) * 362.5);
        break;
      case 2:
        return ((1 + LTC3589_RES_BUCK2_RTOP/LTC3589_RES_BUCK2_RBOT) * 362.5);
        break;
      case 3:
        return ((1 + LTC3589_RES_BUCK3_RTOP/LTC3589_RES_BUCK3_RBOT) * 362.5);
        break;
    }
  }
  
  // Selects the reference for LDO2.
  int8_t LTC3589_select_ldo2_reference(uint8_t i2c_address, int8_t ref_char)
  {
    int8_t ack = 0;
    if (ref_char == '1')
      ack |= LTC3589_bit_clear(i2c_address, LTC3589_REG_VCCR, LTC3589_LDO2_REF_SELECT);
    else if (ref_char == '2')
      ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, LTC3589_LDO2_REF_SELECT);
    ack |= LTC3589_set_go_bits(i2c_address);
    return(ack);
  }
  
  // Selects the reference for the specified buck regulator(s).
  int8_t LTC3589_select_buck_reference(uint8_t i2c_address, uint8_t buck_number, int8_t ref_char)
  {
    int8_t ack = 0;
    if (buck_number == 0xFF)
    {
      ack |= LTC3589_select_buck_reference(i2c_address, 1, ref_char);
      ack |= LTC3589_select_buck_reference(i2c_address, 2, ref_char);
      ack |= LTC3589_select_buck_reference(i2c_address, 3, ref_char);
      return(ack);
    }
    if (ref_char == '1')
      ack |= LTC3589_bit_clear(i2c_address, LTC3589_REG_VCCR, (buck_number*2)-1);
    else if (ref_char == '2')
      ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, (buck_number*2)-1);
    ack |= LTC3589_set_go_bits(i2c_address);
    return(ack);
  }
  
  // Sets the switching mode for the specified buck regulator(s).
  int8_t LTC3589_set_buck_mode(uint8_t i2c_address, uint8_t buck_number, uint8_t mode)
  {
    uint8_t register_data;
    int8_t ack = 0;
    if (buck_number == 0xFF)
    {
      ack |= LTC3589_set_buck_mode(i2c_address, 0x01, mode);
      ack |= LTC3589_set_buck_mode(i2c_address, 0x02, mode);
      ack |= LTC3589_set_buck_mode(i2c_address, 0x03, mode);
      return(ack);
    }
    ack |= LTC3589_register_read(i2c_address, LTC3589_REG_SCR1, &register_data);  //Read register
    register_data = register_data & (~LTC3589_BUCK_MODE_MASK(buck_number));  //Clears the Mode bits in the Buck register
    register_data = register_data | (mode << ((buck_number*2)-2));
    ack |= LTC3589_register_write(i2c_address, LTC3589_REG_SCR1, register_data);  //Writes new register value
    return(ack);
  }
  
  // Sets the switching mode for the buck-boost regulator.
  int8_t LTC3589_set_buckboost_mode(uint8_t i2c_address, uint8_t mode)
  {
    uint8_t register_data;
    int8_t ack = 0;
    ack |= LTC3589_bit_write(i2c_address, LTC3589_REG_SCR1, LTC3589_BUCKBOOST_MODE, mode);  //Writes new mode-bit value
    return(ack);
  }
  
  // Sets the start-up mode for all regulators.
  int8_t LTC3589_set_startup_mode(uint8_t i2c_address, uint8_t startup_bit)
  {
    int8_t ack = 0;
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_BUCK1_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_BUCK2_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_BUCK3_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_BB_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_LDO2_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_LDO3_STARTUP, startup_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_SCR2, LTC3589_LDO4_STARTUP, startup_bit);
    return(ack);
  }
  
  // Sets the PGOOD mask bit for all bucks and LDO2.
  int8_t LTC3589_set_pgood_mask(uint8_t i2c_address, uint8_t pgood_bit)
  {
    int8_t ack = 0;
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_B1DTV1, LTC3589_BUCK_PG_MASK, pgood_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_B2DTV1, LTC3589_BUCK_PG_MASK, pgood_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_B3DTV1, LTC3589_BUCK_PG_MASK, pgood_bit);
    ack |= LTC3589_bit_write(LTC3589_I2C_ADDRESS, LTC3589_REG_L2DTV1, LTC3589_LDO2_PG_MASK, pgood_bit);
    return(ack);
  }
  
  // Sets LDO4 output voltage on the LTC3589.
  int8_t LTC3589_set_ldo4_voltage(uint8_t i2c_address, uint8_t ldo4_output_voltage_code)
  {
    int8_t ack = 0;
    uint8_t register_data;
    ack |= LTC3589_register_read(i2c_address, LTC3589_REG_L2DTV2, &register_data);  //Read register
    register_data = register_data & (~LTC3589_LDO4_VOLTAGE_MASK);  //Clears the LDO4 Output Voltage bits in the L2DTV2 register
    register_data = register_data | (ldo4_output_voltage_code << 5);
    ack |= LTC3589_register_write(i2c_address, LTC3589_REG_L2DTV2, register_data);  //Writes new register value
    return(ack);
  }
  
  // Sets the dynamic reference slew rate for the regulator(s).
  int8_t LTC3589_set_regulator_slew(uint8_t i2c_address, uint8_t regulator_mask, uint8_t slew_rate)
  {
    uint8_t register_data;
    uint8_t bit_shift = log(regulator_mask/3) / log(2);
    int8_t ack = 0;
    if (regulator_mask == 0xFF)
    {
      ack |= LTC3589_set_regulator_slew(i2c_address, LTC3589_BUCK1_SLEW_MASK, slew_rate);
      ack |= LTC3589_set_regulator_slew(i2c_address, LTC3589_BUCK2_SLEW_MASK, slew_rate);
      ack |= LTC3589_set_regulator_slew(i2c_address, LTC3589_BUCK3_SLEW_MASK, slew_rate);
      ack |= LTC3589_set_regulator_slew(i2c_address, LTC3589_LDO2_SLEW_MASK, slew_rate);
      return(ack);
    }
    ack |= LTC3589_register_read(i2c_address, LTC3589_REG_VRRCR, &register_data);  //Read register
    register_data = register_data & (~regulator_mask);  //Clears the Mode bits in the Buck register
    register_data = register_data | (slew_rate << bit_shift);
    ack |= LTC3589_register_write(i2c_address, LTC3589_REG_VRRCR, register_data);  //Writes new register value
    return(ack);
  }
  
  // Sets the switch DV/DT control for the buck regulators.
  int8_t LTC3589_set_switch_dvdt_control(uint8_t i2c_address, uint8_t dvdt_control_bits)
  {
    uint8_t register_data;
    int8_t ack = 0;
    ack |= LTC3589_register_read(i2c_address, LTC3589_REG_B1DTV1, &register_data);
    register_data = register_data & (~LTC3589_BUCK_DVDT_MASK);
    register_data = register_data | (dvdt_control_bits << 6);
    ack |= LTC3589_register_write(i2c_address, LTC3589_REG_B1DTV1, register_data);
    return(ack);
  }
  
  // Sets all four GO bits in the VCCR register.
  int8_t LTC3589_set_go_bits(uint8_t i2c_address)
  {
    int8_t ack = 0;
    ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, LTC3589_BUCK1_GO);
    ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, LTC3589_BUCK2_GO);
    ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, LTC3589_BUCK3_GO);
    ack |= LTC3589_bit_set(i2c_address, LTC3589_REG_VCCR, LTC3589_LDO2_GO);
    return(ack);
  }
  