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

max127.c (12164B)


      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  * MAX127 multi-range 12-bit D-A converter chip which provides for
      8  * eight (8) analog input channels that are independently software
      9  * programmable via I2C bus.
     10  * See also: MAXIM MAX127 data sheet for more details.
     11  */
     12 #include <sal/types.h>
     13 #include <soc/drv.h>
     14 #include <soc/error.h>
     15 #include <soc/i2c.h>
     16 #include <shared/bsl.h>
     17 /* Table 3: Range and Polarity Selection */
     18 #define RPSEL_0_5     0x0
     19 #define RPSEL_0_10    0x2
     20 #define RPSEL_5_5     0x1
     21 #define RPSEL_10_10   0x3
     22 
     23 /* Table 4: Power Down and Clock */
     24 #define PD_NORMAL     0x0
     25 #define PD_STANDBY    0x2
     26 #define PD_FULL_PD    0x3
     27 
     28 /*
     29  * Make a MAX127 Control Byte
     30  * See Table 1: Control Byte Format
     31  */
     32 #define MAX127_CONTROL(ch, rps, pds)\
     33   (0x80 | (ch) << 4 | (rps) << 2 | (pds))
     34 
     35 /* Decimal point formatting */
     36 #define INT_FRAC_3PT_3(x) (x) / 1000000, ((x) / 1000) % 1000
     37 
     38 /* Transform raw HW register value to binary value */
     39 #define MAX127_12BIT(x) ((((x) << 8) | ((x) >> 8)) >> 4);
     40 
     41 /* Number of samples when reading A/D channel */
     42 int soc_i2c_max127_iterations = 100;
     43 
     44  /*
     45  * Function: max127_range_str
     46  *
     47  * Purpose:
     48  *    Given an RPSEL value, return string representation of range.
     49  * Parameters:
     50  *    val - RPSEL HW value from A/D chip
     51  * Returns:
     52  *    String representing voltage range (0-5, 0-10, +/-5, +/-10)
     53  * Notes:
     54  *    None
     55  */
     56 STATIC char *
     57 max127_range_str(int rpsel)
     58 {
     59     if (rpsel == RPSEL_0_5)
     60 	return "(0-5)";
     61     if (rpsel == RPSEL_0_10)
     62 	return "(0-10)";
     63     if (rpsel == RPSEL_5_5)
     64 	return "(+/-5)";
     65     if (rpsel == RPSEL_10_10)
     66 	return "(+/-10)";
     67     return "(unknown)";
     68 }
     69 
     70 /*
     71  * Function: max127_range_from_value
     72  *
     73  * Purpose:
     74  *    Given a raw +/-10 AD output value, return appropriate range.
     75  * Parameters:
     76  *    value - HW value from A/D chip
     77  * Returns:
     78  *    Range to use for best precision (0-5, 0-10, +/-5, +/-10)
     79  * Notes:
     80  *    Initially, we perform the conversion using +/-10 range selection.
     81  *    We then find out which range selection makes sense based on the
     82  *    output value.
     83  */
     84 STATIC int
     85 max127_range_from_value(uint16 value)
     86 {
     87     int slack = 2;
     88 
     89     /* 2-complement to positive linear scale */
     90     value = (value + 2048) & 0xFFF;
     91 
     92     if (value > (3072 - slack))
     93 	return RPSEL_0_10;
     94     if (value > (2048 + slack))
     95 	return RPSEL_0_5;
     96     if (value > (1024 + slack))
     97 	return RPSEL_5_5;
     98     return RPSEL_10_10;
     99 }
    100 
    101 /*
    102  * Function: max127_value
    103  *
    104  * Purpose:
    105  *    Convert binary value from HW to actual voltage.
    106  * Parameters:
    107  *    val - HW value from A/D chip
    108  *    range - current input range
    109  * Returns:
    110  *    Actual voltage in V
    111  * Notes:
    112  *    None
    113  */
    114 #ifdef	COMPILER_HAS_DOUBLE
    115 
    116 #define RPSEL_FS0 (1.2207 * 4.096)
    117 #define RPSEL_FS1 (2.4414 * 4.096)
    118 #define RPSCALE   4096.0
    119 
    120 STATIC double
    121 max127_value(uint16 val, uint16 range)
    122 {
    123     double x = 0;
    124 
    125     if(val == 0)
    126 	return 0.0;
    127     if(range == RPSEL_0_5){
    128 	/* 	x = val / RPSCALE * RPSEL_FS0; */
    129 	x = val * 0.00122;
    130     }
    131     else if (range == RPSEL_0_10)
    132 	x = val / RPSCALE * RPSEL_FS1;
    133     else if(range == RPSEL_5_5){
    134 	x = - 10.0/4096.0 * (4096 - val);
    135     }
    136     else if (range == RPSEL_10_10){
    137 	x = - 20.0/4096.0 * (4096 - val);
    138     }
    139     return x;
    140 }
    141 #else
    142 STATIC int
    143 max127_value(uint16 val, uint16 range)
    144 {
    145     int x;
    146 
    147     if (val == 0) {
    148 	return 0;
    149     }
    150 
    151     switch (range) {
    152     case RPSEL_0_5:
    153         x = val * 5;
    154         break;
    155     case RPSEL_0_10:
    156         x = val * 10;
    157         break;
    158     case RPSEL_5_5:
    159         x = (((val + 2048) & 0xFFF) - 2048) * 10;
    160         break;
    161     case RPSEL_10_10:
    162         x = (((val + 2048) & 0xFFF) - 2048) * 20;
    163         break;
    164     default:
    165         x = 0;
    166         break;
    167     }
    168 
    169     /* Convert to uV with rounding and no overflow */
    170     x *= ((100 * 1000000) / 4096);
    171     x += 50;
    172     x /= 100;
    173 
    174     return x;
    175 }
    176 #endif
    177 
    178 /*
    179  * Data: max127_vals
    180  * Purpose: Range and Polarity selection for each A/D channel.
    181  */
    182 #ifdef COMPILER_HAS_DOUBLE
    183 STATIC
    184 max127_t max127_vals[] = {
    185     /* Initial conversion values: +/-10 */
    186     {0,10,-10,0,0,
    187       MAX127_CONTROL(I2C_ADC_CH0, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    188     {0,10,-10,0,0,
    189       MAX127_CONTROL(I2C_ADC_CH1, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    190     {0,10,-10,0,0,
    191       MAX127_CONTROL(I2C_ADC_CH2, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    192     {0,10,-10,0,0,
    193       MAX127_CONTROL(I2C_ADC_CH3, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    194     {0,10,-10,0,0,
    195       MAX127_CONTROL(I2C_ADC_CH4, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    196     {0,10,-10,0,0,
    197       MAX127_CONTROL(I2C_ADC_CH5, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    198     {0,10,-10,0,0,
    199       MAX127_CONTROL(I2C_ADC_CH6, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 },
    200     {0,10,-10,0,0,
    201       MAX127_CONTROL(I2C_ADC_CH7, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }
    202 } ;
    203 #define N_MAX127_CHAN  COUNTOF(max127_vals)
    204 #else
    205 #define N_MAX127_CHAN  8
    206 #endif
    207 
    208 /*
    209  * Function: max127_calibrate
    210  *
    211  * Purpose: Update MAX127 control word with specified RPSEL value.
    212  *
    213  * Parameters:
    214  *    ch - MAX127 A/D channel number
    215  *    rpsel - range and polarity selection for MAX127 A/D conversion.
    216  *
    217  * Returns:
    218  *    None
    219  */
    220 STATIC void
    221 max127_calibrate(int ch, max127_t *data, int rpsel)
    222 {
    223     data->cr = rpsel;
    224     data->cv = MAX127_CONTROL(ch, rpsel, PD_NORMAL);
    225 }
    226 
    227 
    228 
    229 extern uint32 soc_i2c_pci_read(int unit, uint32 addr);
    230 extern int soc_i2c_pci_write(int unit, uint32 addr, uint32 data);
    231 
    232 
    233 /*
    234  * Function: max127_conversion
    235  *
    236  * Purpose: Perform A/D conversions on a channel
    237  *
    238  * Parameters:
    239  *    unit - StrataSwitch device number or I2C bus number
    240  *    devno - chip device id
    241  *    ch - A/D channel number (0 <= channel <= 7)
    242  *    val - HW value from A/D chip for A/D conversion (raw HW value)
    243  *
    244  * Returns:
    245  *    None
    246  */
    247 STATIC int
    248 max127_conversion(int unit, int devno, int ch, max127_t *data, uint16 *val)
    249 {
    250     uint8 saddr;
    251 
    252     saddr = soc_i2c_addr(unit, devno);
    253 
    254     /* CHi start conversion, read back data */
    255     if (soc_i2c_write_byte(unit, saddr, (uint8)data->cv) < 0) {
    256         LOG_CLI((BSL_META_U(unit,
    257                             "unit %d i2c %s: could not configure channel %d\n"),
    258                  unit, soc_i2c_devname(unit,devno), ch));
    259         *val = 0;
    260         return -1;
    261     }
    262 
    263     soc_i2c_device(unit, devno)->tbyte++;
    264 
    265     /* Read back CHi conversion value */
    266     if (soc_i2c_read_word(unit, saddr, val) < 0) {
    267         LOG_CLI((BSL_META_U(unit,
    268                             "unit %d i2c %s: could not read channel %d\n"),
    269                  unit, soc_i2c_devname(unit,devno), ch));
    270         *val = 0;
    271         return -1;
    272     }
    273 
    274     soc_i2c_device(unit, devno)->rbyte += 2;
    275     return 0;
    276 }
    277 
    278 STATIC void
    279 max127_read_channel(int unit, int devno, int ch, max127_t *data)
    280 {
    281     int i;
    282     uint16 val;
    283 #ifdef COMPILER_HAS_DOUBLE
    284     double voltage;
    285 #else
    286     int voltage;
    287 #endif
    288     /* Reset sample data */
    289     data->val = 0;
    290 #ifdef COMPILER_HAS_DOUBLE
    291     data->min = 10;
    292     data->max = -10;
    293 #else
    294     data->min = 10000000; /* uV */
    295     data->max = -10000000; /* uV */
    296 #endif
    297 
    298     /* Reset/calibrate scale to +/-10 */
    299     max127_calibrate(ch, data, RPSEL_10_10);
    300 
    301     /* Perform conversion on +/-10 scale */
    302     max127_conversion(unit, devno, ch, data, &val);
    303 
    304     /* Convert to 12-bit unsigned int for conversion value */
    305     val = MAX127_12BIT(val);
    306 
    307     /* Store new current range/scale */
    308     max127_calibrate(ch, data, max127_range_from_value(val));
    309 
    310     for (i = 0; i < soc_i2c_max127_iterations; val=0, i++) {
    311 
    312         /* Perform conversion on new scale */
    313         if (max127_conversion(unit, devno, ch, data, &val) < 0) {
    314             break;
    315         }
    316 
    317         /* Convert to 12-bit unsigned int for conversion value */
    318         val = MAX127_12BIT(val);
    319 
    320         /* Compute voltage from binary value */
    321         voltage = max127_value(val, data->cr);
    322 
    323         /* Store the running total */
    324         data->val += voltage;
    325 
    326         /* Compute new min */
    327         if (voltage < data->min) {
    328             data->min = voltage;
    329         }
    330 
    331         /* Compute new max */
    332         if (voltage > data->max) {
    333             data->max = voltage;
    334         }
    335     }
    336 
    337     /* Compute average and delta */
    338     if (i > 1) {
    339         data->val /= i;
    340     }
    341     data->delta = data->max - data->min;
    342 }
    343 
    344 /*
    345  * Function: max127_ioctl
    346  *
    347  * Purpose:
    348  *    Perform A/D conversion and report data.
    349  * Parameters:
    350  *    unit - StrataSwitch device number or I2C bus number
    351  *    devno - chip device id
    352  *    ch - A/D channel
    353  *    data - buffer for storing A/D output data (max127_t)
    354  *    len - unused
    355  * Returns:
    356  *     SOC_E_NONE - no error
    357  * Notes:
    358  *    If a valid channel number is given, run conversions for
    359  *    that channel and return result in supplied buffer.
    360  *    If a negative channel number is given, simply run all 
    361  *    conversions and display results on the console.
    362  */
    363 STATIC int
    364 max127_ioctl(int unit, int devno, int cmd, void *data, int len)
    365 {
    366     int i;
    367     max127_t maxdata;
    368 
    369     switch (cmd) {
    370     case I2C_ADC_DUMP_ALL:
    371         /* Just raw dump */
    372 	for (i = 0; i < N_MAX127_CHAN; i++) {
    373 #ifdef	COMPILER_HAS_DOUBLE
    374         max127_read_channel(unit, devno, i, &max127_vals[i]);
    375         LOG_CLI((BSL_META_U(unit,
    376                             "%s ch%d: %.3fV\t%s\t"
    377                             "%d samples range %.3f..%.3f (delta %.3f)\n"),
    378                  soc_i2c_devname(unit, devno), i,
    379                  max127_vals[i].val,
    380                  max127_range_str(max127_vals[i].cr),
    381                  soc_i2c_max127_iterations,
    382                  max127_vals[i].min,
    383                  max127_vals[i].max,
    384                  max127_vals[i].delta));
    385 #else
    386         max127_t _data;
    387         max127_read_channel(unit, devno, i, &_data);
    388 	    LOG_CLI((BSL_META_U(unit,
    389                                 "%s ch%d: %d.%03dV\t%s\t"
    390                                 "%d samples range %d.%03d..%d.%03d (delta %d.%03d)\n"),
    391                      soc_i2c_devname(unit, devno), i,
    392                      INT_FRAC_3PT_3(_data.val),
    393                      max127_range_str(_data.cr),
    394                      soc_i2c_max127_iterations,
    395                      INT_FRAC_3PT_3(_data.min),
    396                      INT_FRAC_3PT_3(_data.max),
    397                      INT_FRAC_3PT_3(_data.delta)));
    398 #endif
    399         }
    400 	break;
    401     case I2C_ADC_QUERY_CHANNEL:
    402         if (data) {
    403             sal_memset(&maxdata, 0, sizeof(maxdata));
    404             /* len is channel */
    405             max127_read_channel(unit, devno, len, &maxdata);
    406             ((i2c_adc_t*)data)->max = maxdata.max;
    407             ((i2c_adc_t*)data)->min = maxdata.min;
    408             ((i2c_adc_t*)data)->val = maxdata.val;
    409             ((i2c_adc_t*)data)->delta = maxdata.delta;
    410             ((i2c_adc_t*)data)->nsamples = soc_i2c_max127_iterations;
    411         } else {
    412             return SOC_E_PARAM;
    413         }
    414         break;
    415     case I2C_ADC_SET_SAMPLES:
    416         if (data) {
    417             soc_i2c_max127_iterations = *(int*)data;
    418         } else {
    419             return SOC_E_PARAM;
    420         }
    421         break;
    422     default:
    423         LOG_CLI((BSL_META_U(unit,
    424                             "Unit:%d Dev:%d Unsupported IOCTL Cmd %x\n"),
    425                  unit, devno, cmd));
    426         return SOC_E_PARAM;
    427     }
    428     return SOC_E_NONE;
    429 
    430 }
    431 
    432 /*
    433  * Function: max127_init
    434  *
    435  * Purpose:
    436  *    Initialize the MAX127 A/D Chip
    437  * Parameters:
    438  *    unit - StrataSwitch device number or I2C bus number
    439  *    devno - chip device id
    440  *    data - not used
    441  *    len - not used
    442  * Returns:
    443  *    SOC_E_NONE - no failure
    444  */
    445 STATIC int
    446 max127_init(int unit, int devno, void *data, int len)
    447 {
    448     soc_i2c_devdesc_set(unit, devno, "MAX127 A/D Converter");
    449     return SOC_E_NONE;
    450 }
    451 
    452 /* NOT USED */
    453 STATIC int
    454 max127_read(int unit, int devno,
    455 	    uint16 addr, uint8 *data, uint32* len)
    456 {
    457     return SOC_E_NONE;
    458 }
    459 
    460 /* NOT USED */
    461 STATIC int
    462 max127_write(int unit, int devno,
    463 	     uint16 addr, uint8 *data, uint32 len)
    464 {
    465     return SOC_E_NONE;
    466 }
    467 
    468 /* MAX127 A-D converter driver callout */
    469 i2c_driver_t _soc_i2c_max127_driver = {
    470     0x0, 0x0, /* System assigned bytes */
    471     MAX127_DEVICE_TYPE,
    472     max127_read,
    473     max127_write,
    474     max127_ioctl,
    475     max127_init,
    476     NULL,
    477 };