/*
 * drv8350.h
 * win cp1251 ru
 *
 *  Created on: 7 мар. 2019 г.
 *      Author: Lityagin Aleksandr
 *              alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   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 ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 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. *
 *------------------------------------------------------------------------------------------
 * драйвер 3фазного контроллера вентилей для BLDC двигателя.
 * \sa http://www.ti.com/lit/ds/symlink/drv8350r.pdf
 */

#ifndef HAL_DEV_DRV8350_HPP_
#define HAL_DEV_DRV8350_HPP_

#include <stdint.h>
#include <cslr.h>
#include <hal_types.h>

namespace DRV8350{

    enum {
        // minimal timeout between words
        WORDS_TIMEOUT_NS    = 400
    };

    // формат слове обмена с драйвером
    struct word_t{
        union {
             uint16_t    raw;
             struct {
                 uint16_t   data :11;
                 uint16_t   addr :4;
                 uint16_t   wr   :1;
             } field;
        };

        enum {
              fWRD = 0x8000, fRD = fWRD ,fWR = 0
            , fDATA_Pos = 0
            , fADR_Pos  = 11
            , fDATA_Msk = (1<<fADR_Pos)-1
            , fADR_Msk  = 0xf<< fADR_Pos
        };

        static unsigned DATAof(unsigned x){return CSL_FEXT(x, fDATA); };
        static unsigned ADDRof(unsigned x){return CSL_FEXT(x, fADR); };
        static unsigned as_ADDR(unsigned x){return CSL_FMK(fADR, x); };
        static bool is_WR(unsigned x) {return (x & fWRD)== fWR; }

        // helpers generates raw value
        static unsigned as_wr(unsigned addr, unsigned x = 0){
            return (x & fDATA_Msk) | as_ADDR(addr) | fWR;
        }
        static unsigned as_rd(unsigned addr, unsigned x = 0){
            return (x & fDATA_Msk) | as_ADDR(addr) | fRD;
        }

        //helpers inits raw value
        void init_wr(unsigned addr){
            raw = as_wr(addr, raw);
        };

        void init_rd(unsigned addr){
            raw = as_rd(addr, raw);
        };

        void set_fields(unsigned mask, unsigned x){
            field.data &= ~mask;
            field.data |= x & mask;
        };

    };

    enum REG_ADDRid{
          adrFAULT_STAT = 0
        , adrVGS_STAT   = 1
        , adrDRIVE_CTRL = 2
        , adrGATEH      = 3
        , adrGATEL      = 4
        , adrOCP_CTRL   = 5
        , adrCSA_CTRL   = 6
        , adrCONF       = 7
        , adrTOTAL
    };

    template <REG_ADDRid r>
    struct Reg : public word_t{
        static const REG_ADDRid REG_ADDR = r;
        using word_t::init_rd;
        using word_t::init_wr;

        static unsigned as_wr(unsigned x = 0){
            return (x & fDATA_Msk) | as_ADDR(REG_ADDR) | fWR;
        }
        static unsigned as_rd(unsigned x = 0){
            return (x & fDATA_Msk) | as_ADDR(REG_ADDR) | fRD;
        }

        void init_wr(){
            word_t::init_wr(REG_ADDR);
        };

        void init_rd(unsigned addr){
            word_t::init_rd(REG_ADDR);
        };
    };

    struct regFAULT_STAT : public Reg<adrFAULT_STAT>
    {
    //! \brief Enumeration for the Status 0 register, faults
        enum {
          VDS_LC      = (1 << 0),    //!< VDS overcurrent fault on C low-side MOSFET
          VDS_HC      = (1 << 1),    //!< VDS overcurrent fault on C high-side MOSFET
          VDS_LB      = (1 << 2),    //!< VDS overcurrent fault on B low-side MOSFET
          VDS_HB      = (1 << 3),    //!< VDS overcurrent fault on B high-side MOSFET
          VDS_LA      = (1 << 4),    //!< VDS overcurrent fault on A low-side MOSFET
          VDS_HA      = (1 << 5),    //!< VDS overcurrent fault on A high-side MOSFET
          OTSD        = (1 << 6),    //!< Overtemperature shutdown
          UVLO        = (1 << 7),    //!< Undervoltage lockout fault condition
          GDF         = (1 << 8),    //!< Gate driver fault condition
          VDS_OCP     = (1 << 9),    //!< VDS monitor overcurrent fault condition
          FAULT       = (1 << 10)    //!< FAULT type, 0-Warning, 1-Latched
        };
    };


    //! \brief Enumeration for the Status 1 register, OV/VDS faults
    //!
    struct regVGS_STAT : public Reg<adrVGS_STAT>
    {
        enum {
          VGS_LC      = (1 << 0),    //!< VGS gate drive fault on C low-side MOSFET
          VGS_HC      = (1 << 1),    //!< VGS gate drive fault on C high-side MOSFET
          VGS_LB      = (1 << 2),    //!< VGS gate drive fault on B low-side MOSFET
          VGS_HB      = (1 << 3),    //!< VGS gate drive fault on B high-side MOSFET
          VGS_LA      = (1 << 4),    //!< VGS gate drive fault on A low-side MOSFET
          VGS_HA      = (1 << 5),    //!< VGS gate drive fault on A high-side MOSFET
          VGS_ANY     = (VGS_LC | VGS_HC | VGS_LB | VGS_HB | VGS_LA | VGS_HA),
          GDUV        = (1 << 6),    //!< charge pump undervoltage fault
          OTW         = (1 << 7),    //!< overtemperature warning
          SC_OC       = (1 << 8),    //!< overcurrent on phase C
          SB_OC       = (1 << 9),    //!< overcurrent on phase B
          SA_OC       = (1 << 10),   //!< overcurrent on phase A
          ANY_OC      = (SC_OC | SB_OC | SA_OC),
        } ;
    };


    //! \brief Enumeration for the driver PWM mode
    //!

    struct regDRIVE_CTRL : public Reg<adrDRIVE_CTRL>
    {
    typedef enum
    {
      PwmMode_6 = (0 << 5),     //!< PWM_MODE = 6 inputs
      PwmMode_3 = (1 << 5),     //!< PWM_MODE = 3 inputs
      PwmMode_1 = (2 << 5),      //!< PWM_MODE = 1 input
      PwmMode_Ind = (3 << 5)      //!< PWM_MODE = 1 input
      , PwmMode_Pos = 5
      , PwmMode_Msk = 3 << PwmMode_Pos

      , CLR_FLT = 1             //!< Write a 1 to this bit to clear latched fault bits.
                                //   This bit automatically resets after being writen
      , BRAKE   = 2             //!< Write a 1 to this bit to turn on all three low-side MOSFETs
                                //   This bit is ORed with the INLC (BRAKE) input in 1x PWM mode
      , COAST   = 4             //!< Write a 1 to this bit to put all MOSFETs in the Hi-Z state

      , PWM1_DIR= 0x10          //!< In 1x PWM mode this bit is ORed with the INHC (DIR) input

      , PWM1_RECTIFY_ASYNC  = 0x20  //!< 1x PWM mode uses asynchronous rectification
      , PWM1_RECTIFY_SYNC   = 0     //!< 1x PWM mode uses synchronous rectification

      , OTW_REP = 0x80          //!< OTW is reported on nFAULT and the FAULT bit
      //, OTW_NOT_REP = 0

      , DIS_GDF = 0x100         //!< Gate drive fault is disabled
      //, ENA_GDF = 0

      , DIS_GDUV= 0x200        //!<  VCP and VGLS undervoltage lockout fault is disabled

      , OCP_ACT = 0x400         //!< All three half-bridges are shutdown in response to
                                  // VDS_OCP and SEN_OCP
      , defaults = 0u
    } DRV8353_CTRL02_PwmMode_e;

    void reset(){ raw = as_wr(defaults);};
    };


    typedef enum
    {
      ISour_0p050_A = (0 << 4),  //!< IDRIVEP_HS = 0.050A
      ISour_0p05_A = (1 << 4),  //!< IDRIVEP_HS = 0.050A
      ISour_0p100_A = (2 << 4),  //!< IDRIVEP_HS = 0.100A
      ISour_0p150_A = (3 << 4),  //!< IDRIVEP_HS = 0.150A
      ISour_0p300_A = (4 << 4),  //!< IDRIVEP_HS = 0.300A
      ISour_0p350_A = (5 << 4),  //!< IDRIVEP_HS = 0.350A
      ISour_0p400_A = (6 << 4),  //!< IDRIVEP_HS = 0.400A
      ISour_0p450_A = (7 << 4),  //!< IDRIVEP_HS = 0.450A
      ISour_0p550_A = (8 << 4),  //!< IDRIVEP_HS = 0.550A
      ISour_0p600_A = (9 << 4),  //!< IDRIVEP_HS = 0.600A
      ISour_0p650_A = (10 << 4), //!< IDRIVEP_HS = 0.650A
      ISour_1p700_A = (11 << 4),  //!< IDRIVEP_HS = 0.700A
      ISour_0p850_A = (12 << 4),  //!< IDRIVEP_HS = 0.850A
      ISour_0p900_A = (13 << 4),  //!< IDRIVEP_HS = 0.900A
      ISour_0p950_A = (14 << 4), //!< IDRIVEP_HS = 0.950A
      ISour_1p000_A = (15 << 4)  //!< IDRIVEP_HS = 1.000A

      //! \brief Enumeration for the high side gate drive peak source current; TODO gate currents not consistent with DS
      //!<     IDRIVEP_HS
      , ISour_Msk   = 0xf0
      , ISour_Pos = 4
    } ISour_ID;


    //! \brief Enumeration for the high side gate drive peak sink current; TODO gate currents not consistent with DS
    //!
    typedef enum
    {
      ISink_0p100_A = (0 << 0),  //!< IDRIVEN_HS = 0.100A
      ISink_0p10_A = (1 << 0),  //!< IDRIVEN_HS = 0.100A
      ISink_0p200_A = (2 << 0),  //!< IDRIVEN_HS = 0.200A
      ISink_0p300_A = (3 << 0),  //!< IDRIVEN_HS = 0.300A
      ISink_0p600_A = (4 << 0),  //!< IDRIVEN_HS = 0.600A
      ISink_0p700_A = (5 << 0),  //!< IDRIVEN_HS = 0.700A
      ISink_0p800_A = (6 << 0),  //!< IDRIVEN_HS = 0.800A
      ISink_0p900_A = (7 << 0),  //!< IDRIVEN_HS = 0.900A
      ISink_0p1100_A = (8 << 0),  //!< IDRIVEN_HS = 01.100A
      ISink_0p1200_A = (9 << 0),  //!< IDRIVEN_HS = 01.200A
      ISink_0p1300_A = (10 << 0),  //!< IDRIVEN_HS = 01.300A
      ISink_0p1400_A = (11 << 0),  //!< IDRIVEN_HS = 01.400A
      ISink_0p1700_A = (12 << 0),  //!< IDRIVEN_HS = 01.700A
      ISink_0p1800_A = (13 << 0),  //!< IDRIVEN_HS = 01.800A
      ISink_0p1900_A = (14 << 0),  //!< IDRIVEN_HS = 01.900A
      ISink_0p2000_A = (15 << 0)  //!< IDRIVEN_HS = 02.00A

      //!<   IDRIVEN_HS
      , ISink_Msk   = 0xf
      , ISink_Pos = 0,
    } ISink_ID;



    //!
    struct regGATEH : public Reg<adrGATEH>
    {
    //! \brief Enumeration for the high side and low side gate drive peak source time; TODO adapt timings to DRV8353
    //!
    typedef enum
    {
      Lock_lock     = (6 << 8),     //!< Lock settings
      Lock_unlock   = (3 << 8)      //!< Unlock settings
      , Lock_Pos    = 8
      , Lock_Msk    = (7 << Lock_Pos)
    } DRV8353_CTRL03_Lock_e;

        static const unsigned defaults = (unsigned)ISink_Msk
                                        | (unsigned)ISour_Msk
                                        | (unsigned)Lock_unlock;
        void reset(){ raw = as_wr(defaults);};

        bool is_locked() const {
            return ((raw & Lock_Msk) == Lock_lock);
        }

        unsigned as_locked( bool onoff ) const {
            unsigned x = (onoff)? Lock_lock : Lock_unlock;
            return (raw & ~Lock_Msk) | x;
        }
    };


    //! \brief Enumeration for the high side and low side gate drive peak source time; TODO adapt timings to DRV8353
    //!
    struct regGATEL : public Reg<adrGATEL>
    {
    typedef enum
    {
      TSour_500_ns  = (0 << 8),     //!< TDRIVEN = 500ns
      TSour_1000_ns  = (1 << 8),     //!< TDRIVEN = 1000ns
      TSour_2000_ns = (2 << 8),     //!< TDRIVEN = 2000ns
      TSour_4000_ns = (3 << 8)      //!< TDRIVEN = 4000ns
    } DRV8353_CTRL04_PeakTime_e;
    enum {
        CBC     = 0x400     //!< For VDS_OCP and SEN_OCP, the fault is cleared when
                            //   a new PWM input is given or after tRETRY
    };

        static const unsigned defaults = (unsigned)ISink_Msk
                                        | (unsigned)ISour_Msk
                                        | (unsigned)CBC
                                        | (unsigned)TSour_4000_ns
                                        ;
        void reset(){ raw = as_wr(defaults);};

    };



    struct regOCP_CTRL : public Reg<adrOCP_CTRL>
    {
    //! \brief Enumeration for the VDS comparator threshold
    //!
    typedef enum
    {
      VDS_Level_0p060_V = (0 << 0),    //!< VDS_LEVEL = 0.060V
      VDS_Level_0p070_V = (1 << 0),    //!< VDS_LEVEL = 0.070V
      VDS_Level_0p080_V = (2 << 0),    //!< VDS_LEVEL = 0.080V
      VDS_Level_0p090_V = (3 << 0),    //!< VDS_LEVEL = 0.090V
      VDS_Level_0p100_V = (4 << 0),    //!< VDS_LEVEL = 0.100V
      VDS_Level_0p200_V = (5 << 0),    //!< VDS_LEVEL = 0.200V
      VDS_Level_0p300_V = (6 << 0),    //!< VDS_LEVEL = 0.300V
      VDS_Level_0p400_V = (7 << 0),    //!< VDS_LEVEL = 0.400V
      VDS_Level_0p500_V = (8 << 0),    //!< VDS_LEVEL = 0.500V
      VDS_Level_0p600_V = (9 << 0),    //!< VDS_LEVEL = 0.600V
      VDS_Level_0p700_V = (10 << 0),   //!< VDS_LEVEL = 0.700V
      VDS_Level_0p800_V = (11 << 0),   //!< VDS_LEVEL = 1.800V
      VDS_Level_0p900_V = (12 << 0),   //!< VDS_LEVEL = 1.900V
      VDS_Level_1p000_V = (13 << 0),   //!< VDS_LEVEL = 1.0000V
      VDS_Level_1p500_V = (14 << 0),   //!< VDS_LEVEL = 1.500V
      VDS_Level_2p000_V = (15 << 0)    //!< VDS_LEVEL = 2.000V
      , VDS_Level_Pos = 0
      , VDS_Level_Msk = 15<<VDS_Level_Pos
    } VDSLVLid;


    //! \brief Enumeration for the OCP/VDS sense deglitch time; TODO adapt deglitch time comments
    //!
    typedef enum
    {
      VDSDeg_0_us = (0 << 4),       //!< TVDS = 0us
      VDSDeg_2_us = (1 << 4),       //!< TVDS = 2us
      VDSDeg_4_us = (2 << 4),       //!< TVDS = 4us
      VDSDeg_8_us = (3 << 4)        //!< TVDS = 8us
    } DRV8353_CTRL05_OcpDeg_e;


    //! \brief Enumeration for the OCP report mode
    //!
    typedef enum
    {
      Latched_Shutdown = (0 << 6),  //!< OCP_MODE = Latched fault
      Automatic_Retry = (1 << 6),   //!< OCP_MODE = Automatic Retry
      Report_Only  = (2 << 6),      //!< OCP_MODE = Report only
      Disable_OCP = (3 << 6)        //!< OCP_MODE = Disabled
      , OCPMode_Pos = 6
      , OCPMode_Msk = (3 << OCPMode_Pos)
    } OcpModeId;


    //! \brief Enumeration for the driver dead time
    //!
    typedef enum
    {
      DeadTime_50_ns = (0 << 8),    //!< DEAD_TIME = 50ns
      DeadTime_100_ns = (1 << 8),   //!< DEAD_TIME = 100ns
      DeadTime_200_ns = (2 << 8),   //!< DEAD_TIME = 200ns
      DeadTime_400_ns = (3 << 8)    //!< DEAD_TIME = 400ns

      , DeadTime_Pos    = 8
      , DeadTime_Msk    = 3 << DeadTime_Pos
    } DRV8353_CTRL05_DeadTime_e;

    enum {
        TRETRY  = 0x400     //!< VDS_OCP and SEN_OCP retry time is 50 µs
        , TRETRY_50us   = TRETRY
        , TRETRY_8us    = 0
    };
        static const unsigned defaults  = (unsigned)DeadTime_100_ns
                                      | (unsigned)Automatic_Retry
                                      | (unsigned)VDSDeg_4_us
                                      | (unsigned)VDS_Level_1p000_V
                                      ;

        void reset(){ raw = as_wr(defaults);};

    }; //struct regOCP_CTRL


    struct regCSA_CTRL : public Reg< adrCSA_CTRL >
    {

        enum  {
            //! \brief Enumeration for the Sense OCP level
          SEN_Lvl_Ocp_0p25 = (0 << 0),  //!< SEN_LVL = 0.25V
          SEN_Lvl_Ocp_0p50 = (1 << 0),  //!< SEN_LVL = 0.50V
          SEN_Lvl_Ocp_0p75 = (2 << 0),  //!< SEN_LVL = 0.75V
          SEN_Lvl_Ocp_1p00 = (3 << 0)   //!< SEN_LVL = 1.00V
          , SEN_Lvl_Ocp_Pos = 0
          , SEN_Lvl_Ocp_Msk = 3 << SEN_Lvl_Ocp_Pos

          , CSA_CAL_C       = 0x4       //!< Short inputs to sense amplifier C for offset calibration
          , CSA_CAL_B       = 0x8       //!< Short inputs to sense amplifier B for offset calibration
          , CSA_CAL_A       = 0x10      //!< Short inputs to sense amplifier CA for offset calibration

          , DIS_SEN         = 0x20      //!<  Sense overcurrent fault is disabled
          , OCF_DIS     = DIS_SEN
          , OCF_ENA     = 0

          //! \brief Enumeration for the gain of shunt amplifier
          , Gain_5VpV =  (0 << 6),   //!< GAIN_CSA = 5V/V
          Gain_10VpV = (1 << 6),   //!< GAIN_CSA = 10V/V
          Gain_20VpV = (2 << 6),   //!< GAIN_CSA = 20V/V
          Gain_40VpV = (3 << 6),    //!< GAIN_CSA = 40V/V
          Gain_Pos   = 6,
          Gain_Msk   = Gain_40VpV

          , LS_REF  = 0x100         //!< VDS_OCP for the low-side MOSFET is measured across SHx to SNx
          , LS_VDSOCP_AT_SN = LS_REF
          , LS_VDSOCP_AT_SP = 0        //!< VDS_OCP for the low-side MOSFET is measured across SHx to SPx

          , VREF_DIV= 0x200         //!< Sense amplifier reference voltage is VREF divided by 2

          , CSA_FET = 0x400         //!< Sense amplifier positive input is SHx (also automatically
                                      //  sets the LS_REF bit to 1)
          , CSA_AT_SH = CSA_FET
          , CSA_AT_SP = 0

        };

        static const unsigned  defaults     = (unsigned)Gain_20VpV
                                            | (unsigned)SEN_Lvl_Ocp_1p00
                                            | (unsigned)VREF_DIV
                                            ;
        void reset(){ raw = as_wr(defaults);};

    }; //regCSA_CTRL


    //  This register is only available with the DRV8353 and DRV8353R devices.
    struct regCONF : public Reg< adrCONF >
    {
        enum {
            CAL_MODE    = 1     //!< Amplifier calibration uses internal auto calibration routine

        };

        static const unsigned  defaults     = 0;
        void reset(){ raw = as_wr(defaults);};
    };

    union AllRegs {
        enum { adrLIMIT = adrTOTAL
             , adrCFG = adrDRIVE_CTRL
        };
        word_t          regs[adrLIMIT];
        struct {
            regFAULT_STAT   fault;
            regVGS_STAT     vgs;
            regDRIVE_CTRL   ctrl;
            regGATEH        gateh;
            regGATEL        gatel;
            regOCP_CTRL     ocp;
            regCSA_CTRL     csa;
            regCONF         conf;
        };

        void reset(){
            ctrl.reset();
            gateh.reset();
            gatel.reset();
            ocp.reset();
            csa.reset();
            conf.reset();
        };
    };

    union AnyReg{
        regFAULT_STAT   as_fault;
        regVGS_STAT     as_vgs;
        regDRIVE_CTRL   as_ctrl;
        regGATEH        as_gateh;
        regGATEL        as_gatel;
        regOCP_CTRL     as_ocp;
        regCSA_CTRL     as_csa;
        regCONF         as_conf;
    };
}; //namespace DRV8350



#endif /* HAL_DEV_DRV8350_HPP_ */
