openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

ltm4678.c (17207B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * I2C Device Driver for LTM4678 an integrated power control IC.
      8  */
      9 
     10 #include <sal/types.h>
     11 #include <soc/debug.h>
     12 #include <soc/drv.h>
     13 #include <soc/error.h>
     14 #include <soc/i2c.h>
     15 #include <shared/bsl.h>
     16 #include <sal/appl/sal.h>
     17  /* Divided by 2^12 for voltage conversion */
     18 #define L16_TO_V(val)  ((val)*100000/4096/100000)
     19 #define V_TO_L16(val)  ((val)*4096)
     20 #define L16_TO_UV(val) ((val)*100000/4096*10)
     21 #define UV_TO_L16(val) ((val)/10*4096/100000)
     22 
     23 #define L16_RANGE_UPPER(L16, range) ((L16) + (int)(range)*L16/100000)
     24 #define L16_RANGE_LOWER(L16, range) ((L16) - (int)(range)*L16/100000)
     25 
     26 #define MAX_VS_CONFIG    2
     27 
     28 #define POWER(exponent, input_val)  exponent < 0 ? \
     29                             (int)((int) (input_val) << (exponent*(-1))): \
     30                             (int)((int) (input_val*1000000) >> \
     31 			    exponent)/1000000
     32 
     33 typedef struct device_data_s {
     34     sal_mutex_t lock;   /* mutex lock for device operation */
     35     dac_calibrate_t *dac_params;  /*dac params for each device channel*/
     36     int dac_param_len;
     37     int flags;
     38     i2c_saddr_t rail_saddr;
     39 } device_data_t;
     40 
     41 #define DEV_DAC_PARAMS(dev) \
     42     (((device_data_t *)(((i2c_device_t *)(dev))->priv_data))->dac_params)
     43 #define DEV_DAC_PARAM_LEN(dev) \
     44     (((device_data_t *)(((i2c_device_t *)(dev))->priv_data))->dac_param_len)   
     45 #define DEV_FLAGS(dev) \
     46     (((device_data_t *)(((i2c_device_t *)(dev))->priv_data))->flags)   
     47 #define DEV_RAIL_SADDR(dev) \
     48     (((device_data_t *)(((i2c_device_t *)(dev))->priv_data))->rail_saddr)   
     49 
     50 
     51 #define DEV_CHECK_RETURN(dev) \
     52 { \
     53     if ((dev) == NULL) { \
     54         return SOC_E_INTERNAL; \
     55     } \
     56 }
     57 
     58 #define DEV_PRIVDATA_CHECK_RETURN(dev) \
     59 { \
     60     DEV_CHECK_RETURN(dev) \
     61     if ((dev)->priv_data == NULL) { \
     62         return SOC_E_INTERNAL; \
     63     } \
     64 }
     65 
     66 
     67 static sal_mutex_t ioctl_lock = NULL;
     68 
     69 /*
     70  * Convert a floating point value into a
     71  * LinearFloat5_11 formatted word
     72  */
     73 int
     74 #ifdef  COMPILER_HAS_DOUBLE
     75 ltm4678_float_to_L11(double input_val, uint16* data)
     76 #else
     77 ltm4678_float_to_L11(int input_val, uint16* data)
     78 #endif
     79 {
     80     uint16 uExponent, uMantissa;
     81     /* set exponent to -16 */
     82     int16 exponent = -16;
     83     /* extract mantissa from input value */
     84     int mantissa = POWER(exponent, input_val); 
     85 
     86     /* Search for an exponent that produces
     87      * a valid 11-bit mantissa */
     88     do
     89     {
     90         if((mantissa >= -1024) &&
     91                 (mantissa <= +1023))
     92         {
     93             break; /* stop if mantissa valid */
     94         }
     95         exponent++;
     96         mantissa = POWER(exponent, input_val);
     97     } while (exponent < +15);
     98 
     99     /* Format the exponent of the L11 */
    100     uExponent = exponent << 11;
    101     /* Format the mantissa of the L11 */
    102     uMantissa = mantissa & 0x07FF;
    103     /* Compute value as exponent | mantissa */
    104     *(data) = uExponent | uMantissa;
    105     return SOC_E_NONE;
    106 }
    107 
    108 /*
    109  * Convert a LinearFloat5_11 formatted word
    110  * into a floating point value
    111  */
    112 int
    113 ltm4678_L11_to_float(uint16 input_val, void *data)
    114 {
    115     /* extract exponent as MS 5 bits */
    116     int8 exponent = input_val >> 11;
    117     /* extract mantissa as LS 11 bits */
    118     int16 mantissa = input_val & 0x7ff;
    119     /* sign extend exponent from 5 to 8 bits */
    120     if( exponent > 0x0F ) exponent |= 0xE0;
    121     /* sign extend mantissa from 11 to 16 bits */
    122     if( mantissa > 0x03FF ) mantissa |= 0xF800;
    123 #ifdef  COMPILER_HAS_DOUBLE
    124     /* compute value as mantissa * 2^(exponent) */
    125     *(double *)data= exponent < 0 ?
    126                       (double) ((mantissa*1000000) >>
    127                                 (exponent*(-1)))/1000000:
    128                       (double) (mantissa << exponent);
    129 #else
    130     *(int *)data= exponent < 0 ?
    131                    (((mantissa)*1000000) >> (exponent*(-1))):
    132                    ((mantissa) << exponent)*100000;
    133 #endif
    134     return SOC_E_NONE;
    135 }
    136 
    137 STATIC int
    138 ltm4678_wait_for_not_busy(int unit, int devno)
    139 {
    140     int rv = SOC_E_NONE;
    141     uint8 mfr_status, saddr;
    142     uint32 usec, wait_usec;
    143 
    144     wait_usec = 0;
    145     usec = 10;
    146 
    147     saddr = soc_i2c_addr(unit, devno);
    148 
    149     while(wait_usec < 1000000) {
    150         SOC_IF_ERROR_RETURN
    151             (soc_i2c_read_byte_data(unit, saddr,PMBUS_CMD_MFR_COMMON,
    152                                     &mfr_status));
    153         soc_i2c_device(unit, devno)->rbyte++;
    154         if ((mfr_status & 0x70) == 0x70) {
    155             /* Bit 6 : Chip not busy */
    156             /* Bit 5 : calculations not pending */
    157             /* Bit 4 : OUTPUT not in transition */
    158             break;
    159         } else {
    160             sal_udelay(usec);
    161             wait_usec += usec;
    162         }
    163     }
    164 
    165     if ((mfr_status & 0x70) != 0x70) {
    166         LOG_WARN(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "unit %d i2c %s :ltm4678 is busy !\n"),
    167                  unit, soc_i2c_devname(unit, devno)));
    168         rv = SOC_E_TIMEOUT;
    169     }
    170     return rv;
    171 }
    172 
    173 
    174 STATIC int
    175 ltm4678_read(int unit, int devno, uint16 addr, uint8* data, uint32* len)
    176 {
    177     int rv = SOC_E_NONE;
    178     uint8 saddr;
    179 
    180     saddr = soc_i2c_addr(unit, devno);
    181 
    182     if (*len == 0) {
    183         return SOC_E_NONE;
    184     }
    185     if (*len == 1) {
    186         /* reads a single byte from a device, from a designated register*/
    187         rv = soc_i2c_read_byte_data(unit, saddr, addr,data);
    188         soc_i2c_device(unit, devno)->rbyte++;
    189          LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    190                     "unit %d i2c %s: LTM4678_read: "
    191                     "saddr = 0x%x, addr = 0x%x, data = 0x%x, len = %d, "
    192                     "rv = %d\n"),
    193                     unit, soc_i2c_devname(unit,devno),
    194                     saddr, addr, *data, *len, rv));
    195     } else if (*len == 2) {
    196         /* reads a single word from a device, from a designated register*/
    197         rv = soc_i2c_read_word_data(unit, saddr, addr,(uint16 *)data);
    198         soc_i2c_device(unit, devno)->rbyte +=2;
    199         LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    200                     "unit %d i2c %s: LTM4678_read: "
    201                     "saddr = 0x%x, addr = 0x%x, data = 0x%x, len = %d, "
    202                     "rv = %d\n"),
    203                     unit, soc_i2c_devname(unit,devno),
    204                     saddr, addr, *(uint16 *)data, *len, rv));
    205     } else {
    206         /* not supported for now */
    207          LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    208                     "unit %d i2c %s: LTM4678_read fail: "
    209                     "saddr = 0x%x, addr = 0x%x, data = 0x%x, len = %d\n"),
    210                     unit, soc_i2c_devname(unit,devno),
    211                     saddr, addr, *data, *len));
    212     }
    213     return rv;
    214 }
    215 
    216 STATIC int
    217 _ltm4678_write(int unit, int devno, uint8 saddr, uint16 addr, uint8* data, uint32 len)
    218 {
    219     int rv = SOC_E_NONE;
    220     unsigned short val;
    221 
    222     if (len == 0) {
    223         /* simply writes command code to device */
    224         LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "i2c %s: LTM4678 write: "
    225                     "saddr = 0x%x, addr = 0x%x, len = %d\n"),
    226                     soc_i2c_devname(unit, devno), saddr, addr, len));
    227         rv = soc_i2c_write_byte(unit, saddr, addr);
    228     } else if (len == 1) {
    229         LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "i2c %s: LTM4678 write: "
    230                     "saddr = 0x%x, addr = 0x%x, data = 0x%x, len = %d\n"),
    231                     soc_i2c_devname(unit, devno), saddr, addr, *data, len));
    232         rv = soc_i2c_write_byte_data(unit, saddr, addr, *data);
    233         soc_i2c_device(unit, devno)->tbyte++;
    234     } else if (len == 2) {
    235          LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "i2c %s: LTM4678 write: "
    236                     "saddr = 0x%x, addr = 0x%x, data = 0x%x, len = %d\n"),
    237                     soc_i2c_devname(unit, devno),
    238                     saddr, addr, *(uint16 *)data, len));
    239         val = *(unsigned short *)data;
    240         rv = soc_i2c_write_word_data(unit, saddr, addr, val);
    241         soc_i2c_device(unit, devno)->tbyte += 2;
    242     }
    243     return rv;
    244 }
    245 
    246 STATIC int
    247 ltm4678_write(int unit, int devno, uint16 addr, uint8* data, uint32 len)
    248 {
    249     uint8 saddr;
    250 
    251     saddr = soc_i2c_addr(unit, devno);
    252 
    253     return (_ltm4678_write(unit, devno, saddr, addr, data, len));
    254 
    255 }
    256 
    257 STATIC int
    258 ltm4678_rail_write(int unit, int devno, uint16 addr, uint8* data, uint32 len)
    259 {
    260     uint8 rail_saddr;
    261 
    262     rail_saddr = DEV_RAIL_SADDR(soc_i2c_device(unit, devno));
    263 
    264     if (rail_saddr == 0) {
    265         LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    266                     "Rail saddr not set for dev %s\n"),
    267                     soc_i2c_devname(unit, devno)));
    268         return SOC_E_INTERNAL;
    269     }
    270     return (_ltm4678_write(unit, devno, rail_saddr, addr, data, len));
    271 
    272 }
    273 
    274 STATIC int
    275 ltm4678_check_page(int unit, int devno, int ch)
    276 {
    277      int rv;
    278      uint8 page;
    279      uint32 len;
    280 
    281      len = sizeof(char);
    282      rv = ltm4678_read(unit, devno, PMBUS_CMD_PAGE, &page, &len);
    283      if (rv != SOC_E_NONE) {
    284          return rv;
    285      }
    286 
    287      if (page != ch) {
    288          page = ch;
    289          LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "LTM4678 %d set page to %d\n"),
    290                      soc_i2c_addr(unit, devno), page));
    291          rv = ltm4678_write(unit, devno, PMBUS_CMD_PAGE, &page, sizeof(char));
    292      }
    293      return rv;
    294 }
    295 
    296 /*
    297  * NOTE NOTE NOTE:
    298  * All tables (dac_calibrate_t) passed to the ioctl() have size > 1
    299  * and the index is always within this range.
    300  */
    301 STATIC int
    302 ltm4678_ioctl(int unit, int devno, int opcode,
    303               void* data, int len)
    304 {
    305     int rv = SOC_E_NONE;
    306 #ifdef COMPILER_HAS_DOUBLE
    307     double fval;
    308 #else
    309     int fval;
    310 #endif
    311     uint16 dac;
    312     uint32 datalen = 2;
    313     unsigned short voltage;
    314     /* Using mutex lock to ensure thread-safe for ioctl operations */
    315     sal_mutex_take(ioctl_lock, sal_mutex_FOREVER);
    316 
    317      /* length field is actually used as an index into the dac_params table*/
    318      switch (opcode) {
    319          case I2C_LTC_IOC_READ_VOUT:
    320              if ((rv=ltm4678_check_page(unit, devno, len)) < 0) {
    321                  cli_out("Error: failed to set page %d in LTM4678 device.\n", len);
    322                  break;
    323              }
    324              if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    325                  cli_out("Error: Device LTM4678 is busy.\n");
    326                  break;
    327              }
    328              datalen=2;
    329              if ((rv=ltm4678_read(unit,devno, PMBUS_CMD_READ_VOUT, (void *)&dac, &datalen)) < 0) {
    330                  cli_out("Error: Failed to read VOUT of LTM4678 Device.\n");
    331                  break;
    332              }
    333              fval=dac;
    334 #ifdef COMPILER_HAS_DOUBLE
    335              fval=L16_TO_V(fval );
    336              *(double *)data=fval;
    337 #else
    338              fval=L16_TO_UV(fval);
    339              *(int *)data=(fval);
    340 #endif
    341              break;
    342 
    343          case I2C_LTC_IOC_READ_IOUT:
    344              if ((rv=ltm4678_check_page(unit, devno, len)) < 0) {
    345                  cli_out("Error: failed to set page %d in LTM4678 device.\n", len);
    346                  break;
    347              }
    348              if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    349                  cli_out("Error: LTM4678 Device is busy.\n");
    350                  break;
    351              }
    352              if ((rv=ltm4678_read(unit, devno,PMBUS_CMD_READ_IOUT, (void *)&dac, &datalen)) < 0) {
    353                  cli_out("Error:Failed to read current in LTM4678 Device.\n");
    354                  break;
    355              }
    356              rv=ltm4678_L11_to_float(dac, &fval);
    357 #ifdef COMPILER_HAS_DOUBLE
    358              *(double *)data=fval*1000;
    359 #else
    360              *(int *)data=fval;
    361 #endif
    362              break;
    363 
    364          case I2C_LTC_IOC_READ_POUT:
    365             datalen=2;
    366             if ((rv=ltm4678_check_page(unit, devno, len)) < 0) {
    367                 cli_out("Error: failed to set page %d in LTM4678 device.\n", len);
    368                 break;
    369             }
    370             if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    371                 cli_out("Error: LTM4678 Device is busy.\n");
    372                 sal_mutex_give(ioctl_lock);
    373                 return rv;
    374             }
    375 
    376             if ((rv=ltm4678_read(unit,devno, PMBUS_CMD_READ_POUT, (void *)&dac, &datalen)) < 0) {
    377                 cli_out("Error: failed to read power in LTM4678 device.\n");
    378                 break;
    379             }
    380              rv=ltm4678_L11_to_float(dac, &fval);
    381 #ifdef COMPILER_HAS_DOUBLE
    382              *(double *)data=fval*1000;
    383 #else
    384              *(int *)data=fval;
    385 #endif
    386              break;
    387 
    388     case I2C_LTC_IOC_SET_VOUT:
    389             /* Conversion of output voltage */
    390 #ifdef COMPILER_HAS_DOUBLE
    391             fval = *((double*)data);
    392             voltage = (fval*4096);
    393 #else
    394             fval = *((int*)data);
    395             /*2^12 conversion and changing from uVolt */
    396             voltage = (fval*4096)/1000000;
    397 #endif
    398             dac = voltage;
    399             /* Show what we are doing, for now ... */
    400             LOG_VERBOSE(BSL_LS_SOC_I2C, (BSL_META_U(unit,
    401                         "unit %d i2c %s: LTM4678 ioctl "
    402                         "I2C_DAC_IOC_SET_VOUT : voltage = %d, len = %d\n"),
    403                         unit, soc_i2c_devname(unit,devno), voltage, len));
    404 
    405 	        rv = ltm4678_rail_write(unit, devno, PMBUS_CMD_VOUT_COMMAND,(void *) &dac, 2);
    406              break;
    407 
    408          default:
    409               LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    410                  "unit %d i2c %s: ltm4678_ioctl: invalid opcode (%d)\n"),
    411                  unit, soc_i2c_devname(unit,devno), opcode));
    412              break;
    413     }
    414     sal_mutex_give(ioctl_lock);
    415     return rv;
    416 }
    417 
    418 STATIC int
    419 ltm4678_init(int unit, int devno, void* data, int len)
    420 {
    421     int rv = SOC_E_NONE;
    422     i2c_device_t *dev = soc_i2c_device(unit, devno);
    423     char *devname;
    424     uint8 data8;
    425 
    426     if (dev == NULL) {
    427         return SOC_E_INTERNAL;
    428     }
    429     devname = (char *)soc_i2c_devname(unit, devno);
    430     if (dev->priv_data == NULL) {
    431         dev->priv_data = sal_alloc(sizeof(device_data_t), devname);
    432         if (dev->priv_data == NULL) {
    433             LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    434                         "Fail to allocate private data fo dev %s\n"),
    435                         soc_i2c_devname(unit, devno)));
    436             return SOC_E_MEMORY;
    437         }
    438         sal_memset(dev->priv_data, 0, sizeof(device_data_t));
    439     }
    440 
    441     if (ioctl_lock == NULL) {
    442         ioctl_lock = sal_mutex_create("ltm4678_ioctl_lock");
    443         if (ioctl_lock == NULL) {
    444             LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    445                     "Fail to create ltm4678_ioctl_lock\n")));
    446             rv = SOC_E_MEMORY;
    447         }
    448     }
    449 
    450     /* Using mutex lock to ensure thread-safe for ioctl operations */
    451     sal_mutex_take(ioctl_lock, sal_mutex_FOREVER);
    452     /* bit3 1: control the LTM4678 output through VOUT command
    453      *      0: control the output via VID input pins which is controlled by
    454      *         a PCF8574 device
    455      */
    456     if ((rv=ltm4678_write(unit, devno,
    457                     PMBUS_CMD_CLEAR_FAULTS,(void *) &len, 0)) < 0) {
    458         cli_out("Error: Failed to clear the faults of LTM4678 device.\n");
    459         sal_mutex_give(ioctl_lock);
    460         return rv;
    461     }
    462     if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    463         cli_out("Error: LTM4678 Device is busy.\n");
    464         sal_mutex_give(ioctl_lock);
    465         return rv;
    466     }
    467 
    468     len = 1;
    469     if ((rv=ltm4678_read(unit, devno, 0xFA, (uint8 *)&DEV_RAIL_SADDR(dev), (void *)&len)) < 0) {
    470         cli_out("Error: Failed to read 0xFA of LTM4678 device.\n");
    471         sal_mutex_give(ioctl_lock);
    472         return rv;
    473     }
    474 
    475     /* bsl_printf("SADDR = 0x%02x DEV_RAIL_SADDR = 0x%02x\n", soc_i2c_addr(unit, devno), DEV_RAIL_SADDR(dev)); */
    476     soc_i2c_devdesc_set(unit, devno, "LTM4678 Voltage Control");
    477      LOG_VERBOSE(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "ltm4678_init: %s, devNo=0x%x\n"),
    478              soc_i2c_devname(unit,devno), devno));
    479 
    480     /* Setting Fault response to zero to prevent shut-down of device */
    481     data8 =0x00;
    482     if ((rv=ltm4678_write(unit, devno,
    483                     PMBUS_CMD_VOUT_OV_FAULT_RES, &data8, 1)) < 0) {
    484         cli_out("Error: failed to set OV fault response of LTM4678.\n");
    485         sal_mutex_give(ioctl_lock);
    486         return rv;
    487     }
    488     if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    489         cli_out("Error: LTM4678 device is busy.\n");
    490         sal_mutex_give(ioctl_lock);
    491         return rv;
    492     }
    493 
    494     data8 =0x0A;
    495     if ((rv=ltm4678_write(unit, devno, PMBUS_CMD_ON_OFF_CONFIG, &data8, 1)) < 0) {
    496         cli_out("Error: failed to set CONFIG register of LTM4678 device.\n");
    497         sal_mutex_give(ioctl_lock);
    498         return rv;
    499     }
    500     /* Switching on LTC4678 device */
    501     data8 =0x80;
    502     if ((rv=ltm4678_write(unit, devno, PMBUS_CMD_OPERATION, &data8, 1)) < 0) {
    503         cli_out("Error: failed to set operation register of LTM4678 device.\n");
    504         sal_mutex_give(ioctl_lock);
    505         return rv;
    506     }
    507     if ((rv=ltm4678_wait_for_not_busy(unit, devno)) < 0) {
    508         cli_out("Error: LTM4678 Device is busy.\n");
    509         sal_mutex_give(ioctl_lock);
    510         return rv;
    511     }
    512 
    513     sal_mutex_give(ioctl_lock);
    514     return rv;
    515 }
    516 
    517 /* ltm4678 voltage control Chip Driver callout */
    518 i2c_driver_t _soc_i2c_ltm4678_driver = {
    519     0x0, 0x0, /* System assigned bytes */
    520     LTM4678_DEVICE_TYPE,
    521     ltm4678_read,
    522     ltm4678_write,
    523     ltm4678_ioctl,
    524     ltm4678_init,
    525     NULL,
    526 };