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

24c64.c (31039B)


      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  * BCM56xx I2C Device Driver for ATMEL 24LC64 EEPROM
      8  *
      9  * The AT24C32/64 provides for 32,768 or 65,536 bits of electrically
     10  * eraseable programmable read only memory (EEPROM) organized as
     11  * 4096/8192 bytes. The AT24C32/64 chips are internally organized 
     12  * as 128/256 pages of 32 bytes each, hence a page addressing mode
     13  * is available.
     14  * The top quadrant (1024/2048 bytes) can be write protected. This is
     15  * where the Board Information Structure (BIS) is stored; it is not
     16  * subject to any destructive memory integrity tests.
     17  *
     18  * See also: AT24C64 Data Sheet
     19  */
     20 #include <sal/types.h>
     21 #include <soc/drv.h>
     22 #include <soc/error.h>
     23 #include <soc/debug.h>
     24 #include <soc/i2c.h>
     25 #include <soc/iproc.h>
     26 #include <soc/cm.h>
     27 #include <shared/bsl.h>
     28 #define LC2464_WRITE_CYCLE_DELAY   (10*MILLISECOND_USEC) /* 10 ms MAX */
     29 #define LC2464_PAGE_SIZE            32    /* Bytes per page */
     30 #define LC2464_DEVICE_SIZE         (8192) /* AT24C64 */
     31 #define LC2464_DEVICE_RW_SIZE      (((LC2464_DEVICE_SIZE)*3)/4)
     32 #define LC2464_ACK_RETRY_COUNT      1000  /* Number of retry polls */
     33 #define LC2464_NAME_LEN            6
     34 
     35 
     36 
     37 
     38 
     39 typedef struct eep24c64_s{
     40     uint16 size;   /* Number of Bytes */
     41     uint16 type;   /* Reserved */
     42     char name[LC2464_NAME_LEN];  /* Device name */
     43     uint16 chksum; /* 16 bit checksum of size, type, and name */
     44 } eep24c64_t;
     45 
     46 /*
     47  * Store EEPROM data at start of prom, make all
     48  * drivers read/write beyond that. This basically
     49  * serves as a way of signing the EEPROM with data
     50  * so that we know the device is good and has been
     51  * validated.
     52  */
     53 #define LC24C64_PARAMS_SIZE (sizeof(eep24c64_t))
     54 #define LC24C64_DATA_START   LC24C64_PARAMS_SIZE
     55 
     56 /*
     57  * Function: eep24c64_ack_poll
     58  *
     59  * Purpose:  Poll the device to determine if it is ready for IO.
     60  *           When the chip writes data, it will become unresponsive
     61  *           for a period of Time Tw (Internal Write Delay). We could
     62  *           either wait the maximum amount of time for the device to
     63  *           finish it's write-cycle (10ms), or we can poll the device
     64  *           for a specified operation (r/w) until it responds with an
     65  *           ACK. This routine polls the device until it is responsive.
     66  *
     67  * Parameters:
     68  *    unit - StrataSwitch device number or I2C bus number
     69  *    bus_addr - chip IO (I2C) slave bus address byte(s)
     70  *
     71  * Returns:
     72  *     Number of Poll operations required to contact device, or
     73  *     LC24C64_ACK_RETRY_COUNT if the device is not online or responding.
     74  *
     75  *
     76  * Notes:
     77  *     See also: soc_i2c_ack_poll
     78  */
     79 STATIC INLINE int
     80 eep24c64_ack_poll(int unit, i2c_bus_addr_t bus_addr)
     81 {
     82     return soc_i2c_ack_poll(unit, bus_addr, LC2464_ACK_RETRY_COUNT);
     83 }
     84 
     85 /*
     86  * Function: eep24c64_read
     87  *
     88  * Purpose: Read len bytes of data into buffer, update len with total
     89  *          amount read.
     90  *
     91  * Parameters:
     92  *    unit - StrataSwitch device number or I2C bus number
     93  *    devno - chip device id
     94  *    addr - NVRAM memory address to read from
     95  *    data - address of data buffer to read into
     96  *    len - address containing number of bytes read into data buffer (updated
     97  *          with number of bytes read on completion).
     98  *
     99  * Returns: data bufffer filled in with data from address, number of
    100  *          bytes read is updated in len field. Status code:
    101  *
    102  *          SOC_E_NONE -- no error encounter
    103  *          SOC_E_TIMEOUT - chip timeout or data error
    104  *
    105  * Notes:
    106  *         Currently uses random address byte read to initiate the read; if
    107  *         more than one byte of data is requested at the current address, a
    108  *         sequential read operation is performed.
    109  */
    110 STATIC int
    111 eep24c64_read(int unit, int devno,
    112 	      uint16 addr, uint8* data, uint32 *len)
    113 {
    114     int rv = SOC_E_NONE;
    115     uint8 saddr_r, saddr_w, a0, a1;
    116     uint32 nbytes = 0;
    117 #ifdef BCM_CMICM_SUPPORT
    118     uint8 rx;
    119     uint32 nread;
    120     soc_timeout_t to;
    121 #endif
    122 #ifdef BCM_IPROC_SUPPORT
    123     uint32 rval;
    124     uint32 i;
    125 #endif
    126 
    127     /* Valid address, memory and size must be provided */
    128     if ( ! len || ! data )
    129 	return SOC_E_PARAM;
    130 
    131     I2C_LOCK(unit);
    132 
    133     saddr_r = SOC_I2C_RX_ADDR(soc_i2c_addr(unit, devno));
    134     saddr_w = SOC_I2C_TX_ADDR(soc_i2c_addr(unit, devno));
    135 
    136     a0 = (uint8) (addr & 0x00ff);
    137     a1 = (uint8) ((addr & 0xff00) >> 8);
    138 
    139     LOG_INFO(BSL_LS_SOC_I2C,
    140              (BSL_META_U(unit,
    141                          "eep24c64_read: addr=0x%x (a0=0x%x,a1=0x%x) len=%d\n"),
    142               addr, a0, a1, (int)*len));
    143 
    144 #ifdef BCM_IPROC_SUPPORT
    145     if (soc_feature(unit, soc_feature_eeprom_iproc)) {
    146         rval = (uint32)saddr_w;
    147         WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    148         rval = (uint32)a1;
    149         WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    150         rval = (uint32)a0;
    151         WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    152 
    153         nbytes = *len;
    154         *len = 0;
    155         for (i = 0; i < nbytes; i++) {
    156             rval = (uint32)saddr_r;
    157             soc_reg_field_set(unit, CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr, &rval,
    158                               MASTER_WR_STATUSf, 1);
    159             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    160             rval = 0;
    161             soc_reg_field_set(unit, CHIPCOMMONG_SMBUS0_SMBUS_MASTER_COMMANDr, &rval,
    162                               SMBUS_PROTOCOLf, SMBUS_BLOCK_PROCESS_CALL);
    163             soc_reg_field_set(unit, CHIPCOMMONG_SMBUS0_SMBUS_MASTER_COMMANDr, &rval,
    164                               RD_BYTE_COUNTf, 1);
    165             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_COMMANDr(unit, rval);
    166 
    167             rv = iproc_smbus_start_wait(unit);
    168             if (rv == SOC_E_NONE) {
    169                 READ_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_READr(unit, &rval);
    170                 data[i] = (uint8)(rval & 0xFF);
    171                 *len = *len + 1;
    172             } else {
    173                 LOG_INFO(BSL_LS_SOC_I2C,
    174                          (BSL_META_U(unit,
    175                                      "eep24c64_read(%d,%d,%x,%p,%d): "
    176                                      "failed to read.\n"),
    177                          unit, devno, addr, (void *)data, *len));
    178                 I2C_UNLOCK(unit);
    179                 return rv;
    180             }
    181         }
    182     } else
    183 #endif /* BCM_IPROC_SUPPORT */
    184 #ifdef BCM_CMICM_SUPPORT
    185     if(soc_feature(unit, soc_feature_cmicm) && !SOC_IS_SAND(unit)) {
    186        rv = soc_i2c_write_word(unit, soc_i2c_addr(unit, devno), addr);
    187         nbytes = *len;
    188         *len = 0;
    189         if (rv == SOC_E_NONE) {
    190             for (nread = 0; nread < nbytes; nread++) {
    191                 rv = soc_i2c_read_byte(unit, soc_i2c_addr(unit, devno), &rx);
    192                 if(rv != SOC_E_NONE) {
    193                     if (nread == 0) {
    194                         /* Might be the situation that the previous write is 
    195                          * under it's internal write-cycle. The max write-cycle 
    196                          * time could reach to 5ms.
    197                          */
    198                         soc_timeout_init(&to, 5000, 3); 
    199                         do {
    200                             /* coverity[callee_ptr_arith: FALSE] */
    201                             rv = soc_i2c_read_byte(unit, soc_i2c_addr(unit, devno), &rx);
    202                             if (rv == SOC_E_NONE) {
    203                                 break;
    204                             }
    205                         } while(!(soc_timeout_check(&to)));
    206                     }
    207                     if (rv != SOC_E_NONE) {
    208                         I2C_UNLOCK(unit);
    209                         return rv;
    210                     }
    211                 }
    212                 *len = *len + 1;
    213                 data[nread] = rx;
    214             } 
    215         }
    216     } else
    217 #endif /* BCM_CMICM_SUPPORT */
    218     {
    219         /* First, we put out the address which we would like to
    220          * read at using the SOC_I2C_TX_ADDR (saddr_w)
    221          */
    222         if ( (rv = soc_i2c_start(unit, saddr_w)) < 0) {
    223             LOG_INFO(BSL_LS_SOC_I2C,
    224                      (BSL_META_U(unit,
    225                                  "eep24c64_read(%d,%d,%x,%p,%d): "
    226                                  "failed to generate start.\n"),
    227                       unit, devno, addr, (void *)data, *len));
    228             I2C_UNLOCK(unit);
    229             return rv;
    230         }
    231         /* Word Protocol: Address MSB */
    232         if ( (rv = soc_i2c_write_one_byte(unit, a1)) < 0) {
    233             LOG_INFO(BSL_LS_SOC_I2C,
    234                      (BSL_META_U(unit,
    235                                  "eep24c64_read(%d,%d,%x,%p,%d): "
    236                                  "failed to send a1 byte.\n"),
    237                       unit, devno, addr, (void *)data, *len));
    238 
    239             goto error;
    240         }
    241         /* Address LSB */
    242         if( (rv = soc_i2c_write_one_byte(unit, a0)) < 0) {
    243             LOG_INFO(BSL_LS_SOC_I2C,
    244                      (BSL_META_U(unit,
    245                                  "eep24c64_read(%d,%d,%x,%p,%d): "
    246                                  "failed to send a0 byte.\n"),
    247                       unit, devno, addr, (void *)data, *len));
    248 
    249             goto error;
    250         }
    251         /* Now, we have sent the first and second word addresses,
    252          * we then issue a repeated start condition, followed by
    253          * the device's read address (note: saddr_r)
    254          */
    255         if( (rv = soc_i2c_rep_start(unit, saddr_r)) < 0) {
    256             LOG_INFO(BSL_LS_SOC_I2C,
    257                      (BSL_META_U(unit,
    258                                  "eep24c64_read(%d,%d,%x,%p,%d): "
    259                                  "failed to generate rep start.\n"),
    260                       unit, devno, addr, (void *)data, *len));
    261             goto error;
    262         }
    263         nbytes = *len;
    264         if ( (rv = soc_i2c_read_bytes(unit, data, (int *)&nbytes, 0) ) < 0 ) {
    265             goto error;
    266         }
    267         *len = nbytes;
    268 
    269 error:
    270 
    271         soc_i2c_stop(unit);
    272     }
    273 
    274     I2C_UNLOCK(unit);
    275     return rv ;
    276 }
    277 
    278 /*
    279  * Function: eep24c64_write
    280  * Purpose: Write len bytes of data, return the number of bytes written.
    281  * Uses PAGE mode to write up to 32 bytes at a time between address
    282  * stages for maximum performance. See AT24C64 data sheet for more info.
    283  *
    284  * Parameters:
    285  *    unit - StrataSwitch device number or I2C bus number
    286  *    devno - chip device id
    287  *    addr - NVRAM memory address to write to
    288  *    data - address of data buffer to write from
    289  *    len - number of bytes to write
    290  *
    291  * Returns:
    292  *          SOC_E_NONE -- no error encountered
    293  *          SOC_E_TIMEOUT - chip timeout or data error
    294  *
    295  *
    296  * Notes:
    297  *     Uses page mode to write in 32-byte chunks, when an address
    298  *     which does not begin on a page boundary is provided, the write
    299  *     operation handles unaligned accesses by breaking up the write
    300  *     into one write which is not page aligned, and the remainder as
    301  *     page aligned accesses.
    302  */
    303 STATIC int
    304 eep24c64_write(int unit, int devno,
    305 	      uint16 addr, uint8* data, uint32 len)
    306 {
    307     int rv = SOC_E_NONE;
    308     uint8 *ptr, a0, a1;
    309     uint32 b, numpages, cpage, nbytes, tbytes, caddr;
    310     i2c_bus_addr_t bus_addr;
    311 #if defined(BCM_CMICM_SUPPORT) || defined(BCM_IPROC_SUPPORT)
    312     uint32 rval;
    313 #endif
    314 
    315     /* User must have data to write */
    316     if ( ! data || len <= 0 ) {
    317         return SOC_E_PARAM;
    318     }
    319 
    320     I2C_LOCK(unit);
    321 
    322     /* Use PAGE mode */
    323     caddr = addr;
    324     numpages = 1 + (((caddr%LC2464_PAGE_SIZE)+len-1)/LC2464_PAGE_SIZE);
    325     ptr = data;
    326 
    327 
    328     tbytes = soc_i2c_device(unit, devno)->tbyte++;
    329 
    330     bus_addr = SOC_I2C_TX_ADDR(soc_i2c_addr(unit, devno));
    331 
    332     LOG_INFO(BSL_LS_SOC_I2C,
    333              (BSL_META_U(unit,
    334                          "eep24c64_write: addr=0x%x data=%p len=%d npages=%d\n"),
    335               caddr, (void *)data, (int)len, numpages));
    336 
    337     /* Loop over every page in buffer .. */
    338     for(cpage = 0; cpage < numpages; cpage++) {
    339         if( (caddr % LC2464_PAGE_SIZE) != 0){
    340             /* Address not page aligned, D'Oh, can't write a full page. */
    341             nbytes = LC2464_PAGE_SIZE - (caddr % LC2464_PAGE_SIZE);
    342             nbytes = (len > nbytes) ? nbytes : len;
    343             len -= nbytes;
    344         } else {
    345             /* Address is page aligned, calculate bytes to write */
    346             if ( len <= LC2464_PAGE_SIZE ) {
    347                 /* Less than a page to write */
    348                 nbytes = len;
    349             } else {
    350                 /* Wammo, full page write */
    351                 nbytes = LC2464_PAGE_SIZE;
    352                 len -= nbytes;
    353             }
    354         }
    355 
    356         /* Construct device address bytes */
    357         a1 = (uint8) ((caddr & 0xff00) >> 8);
    358         a0 = (uint8) (caddr & 0x00ff);
    359 
    360         LOG_INFO(BSL_LS_SOC_I2C,
    361                      (BSL_META_U(unit,
    362                                  "eep24c64_write: unit=%d cpage=%d START on page_addr=0x%x"
    363                                  " nbytes=%d\n"), unit, cpage, caddr, nbytes));
    364 
    365 #ifdef BCM_IPROC_SUPPORT
    366         if(soc_feature(unit, soc_feature_eeprom_iproc)) {
    367             /* Write Word is achieved with SMBUS_WRITE_BYTE, with LSB of data instead of the command */
    368             rval = SOC_I2C_TX_ADDR(soc_i2c_addr(unit, devno));
    369             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    370             rval = a1;
    371             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    372             rval = a0;
    373             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    374 
    375             for ( b = 0; b < nbytes; b++, ptr++, caddr++ ) {
    376                 rval = *ptr;
    377                 if(b == (nbytes - 1)) {
    378                     soc_reg_field_set(unit, CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr,
    379                                       &rval, MASTER_WR_STATUSf, 1); /* Last Byte */
    380                 }
    381                 WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    382                 soc_i2c_device(unit, devno)->tbyte++;
    383             }
    384 
    385             rval = 0;
    386             soc_reg_field_set(unit, CHIPCOMMONG_SMBUS0_SMBUS_MASTER_COMMANDr, &rval, SMBUS_PROTOCOLf, SMBUS_BLOCK_WRITE);
    387             WRITE_CHIPCOMMONG_SMBUS0_SMBUS_MASTER_COMMANDr(unit, rval);
    388             rv = iproc_smbus_start_wait(unit);
    389 
    390             if (rv < 0) {
    391                 I2C_UNLOCK(unit);
    392                 return (rv);
    393             }
    394 
    395             /* EEPROM after write operation requested need a period of time
    396              * (in term of WriteCycle) to finish the programming process
    397              * into EEPROM. This driver implement a usleep to wait WriteCycle.
    398              */
    399             sal_usleep(5000);
    400         } else
    401 #endif /* BCM_IPROC_SUPPORT */
    402 #ifdef BCM_CMICM_SUPPORT
    403         if(soc_feature(unit, soc_feature_cmicm) && !SOC_IS_SAND(unit)) {
    404             /* Write Word is achieved with SMBUS_WRITE_BYTE, with LSB of data instead of the command */
    405             rval = SOC_I2C_TX_ADDR(soc_i2c_addr(unit, devno));
    406             WRITE_CMIC_I2CM_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    407 
    408             rval = a1;
    409             WRITE_CMIC_I2CM_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    410             rval = a0;
    411             WRITE_CMIC_I2CM_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    412 
    413             for ( b = 0; b < nbytes; b++, ptr++, caddr++ ) {
    414                 rval = *ptr;
    415                 if(b == (nbytes-1)) {
    416                     soc_reg_field_set(unit, CMIC_I2CM_SMBUS_MASTER_DATA_WRITEr, &rval, MASTER_WR_STATUSf, 1); /* Last Byte */
    417                 }
    418                 WRITE_CMIC_I2CM_SMBUS_MASTER_DATA_WRITEr(unit, rval);
    419                 soc_i2c_device(unit, devno)->tbyte++;
    420             }
    421 
    422             rval = 0;
    423             soc_reg_field_set(unit, CMIC_I2CM_SMBUS_MASTER_COMMANDr, &rval, SMBUS_PROTOCOLf, SMBUS_BLOCK_WRITE);
    424             WRITE_CMIC_I2CM_SMBUS_MASTER_COMMANDr(unit,rval);
    425             rv = smbus_start_wait(unit);
    426 
    427             if (rv < 0) {
    428                 I2C_UNLOCK(unit);
    429                 return (rv);
    430             }
    431 
    432             /* EEPROM after write operation requested need a period of time 
    433              * (in term of WriteCycle) to finish the programming process 
    434              * into EEPROM. This driver implement a usleep to wait WriteCycle. 
    435              */
    436             sal_usleep(5000);
    437         } else
    438 #endif /* BCM_CMICM_SUPPORT */
    439         {
    440             /* Generate Start, for Write address */
    441             if( (rv = soc_i2c_start(unit, bus_addr)) < 0){
    442                 LOG_INFO(BSL_LS_SOC_I2C,
    443                          (BSL_META_U(unit,
    444                                      "eep24c64_write(%d,%d,%x,%d,%d): "
    445                                      "failed to gen start\n"),
    446                           unit, devno, caddr, *data, len));
    447                 I2C_UNLOCK(unit);
    448                 return rv;
    449             }
    450             /* Send MSB (a1), wait for ACK */
    451             if( (rv = soc_i2c_write_one_byte(unit, a1)) < 0){
    452                 LOG_INFO(BSL_LS_SOC_I2C,
    453                          (BSL_META_U(unit,
    454                                      "eep24c64_write(%d,%d,%x,%d,%d): "
    455                                      "failed to send a1 byte\n"),
    456                           unit, devno, caddr, *data, len));
    457                 goto error;
    458             }
    459             /* Send LSB (a0), wait for ACK */
    460             if( (rv = soc_i2c_write_one_byte(unit, a0)) < 0){
    461                 LOG_INFO(BSL_LS_SOC_I2C,
    462                          (BSL_META_U(unit,
    463                                      "eep24c64_write(%d,%d,%x,%d,%d): "
    464                                      "failed to send a0 byte\n"),
    465                           unit, devno, caddr, *data, len));
    466                 goto error;
    467             }
    468             /* Send up to PAGE_SIZE data bytes, wait for ACK */
    469             for ( b = 0; b < nbytes; b++, ptr++, caddr++ ) {
    470                 if ( (rv = soc_i2c_write_one_byte(unit, *ptr)) < 0){
    471                     LOG_INFO(BSL_LS_SOC_I2C,
    472                              (BSL_META_U(unit,
    473                                          "eep24c64_write(%d,%d,%d,%d,%d): "
    474                                          "tx data byte error\n"),
    475                               unit, devno, caddr, (uint32)*ptr, b));
    476                 goto error;
    477                 }
    478                 LOG_VERBOSE(BSL_LS_SOC_I2C,
    479                             (BSL_META_U(unit,
    480                                         "eep24c64_write(u=%d,id=%d,page=%d "
    481                                         "caddr=%d,data=0x%x,idx=%d)\n"),
    482                              unit, devno, cpage, caddr, (uint8)*ptr, b));
    483 
    484                 soc_i2c_device(unit, devno)->tbyte++;
    485             }
    486 
    487             /* Send STOP, also what we do on an error to free bus.. */
    488 error:
    489 
    490             soc_i2c_stop(unit);
    491 
    492             /* Acknowledge polling: When the chip enters it's
    493              * own internal write-cycle, it falls off the I2C bus
    494              * and does not ACK a start/address phase; hence, we
    495              * attempt addressing the chip until it responds, at
    496              * which point the internal write cycle has finished.
    497              */
    498             rv = eep24c64_ack_poll(unit, bus_addr);
    499             LOG_INFO(BSL_LS_SOC_I2C,
    500                      (BSL_META_U(unit,
    501                                  "eep24c64_ack_poll: "
    502                                  "%d address cycles for wr latency.\n"), rv));
    503             rv = (rv > 0 ? SOC_E_NONE: SOC_E_TIMEOUT);
    504         }
    505     }
    506 
    507     I2C_UNLOCK(unit);
    508 
    509     if (rv >= 0) {
    510         return soc_i2c_device(unit, devno)->tbyte - tbytes - 1 ;
    511     } else {
    512         return (rv);
    513     }
    514 }
    515 
    516 /*
    517  * Function: eep24c64_checksum
    518  *
    519  * Purpose: 16 bit incremental checksum; used for checking data
    520  *          validity of the configuration parameter block.
    521  * Parameters:
    522  *        partial - partial checksum, or zero if initial checksum,
    523  *                  updated on each call.
    524  *        data - data buffer to checksum
    525  *        len - size of data buffer to checksum
    526  *
    527  * Returns: updated checksum value
    528  *
    529  * Notes:
    530  *       This routine is called incrementally, one or more times,
    531  *       and each time, the new checksum value is returned. The first
    532  *       time this routine is called, pass 0 as the initial checksum
    533  *       value. On succesive calls, pass the last computed checksum
    534  *       value.
    535  */
    536 STATIC uint16
    537 eep24c64_chksum(uint16 partial, uint8* data, int len)
    538 {
    539     uint8* dp;
    540     uint8 c0, c1;
    541     int i;
    542 
    543     partial = soc_ntohs(partial);
    544 
    545     c0 = (uint8) partial;
    546     c1 = (uint8) (partial >> 8);
    547 
    548     for ( i=0, dp = (uint8*)data; i < len; i++, dp++) {
    549 	c0 += *dp;
    550 	c1 += c0;
    551     }
    552     partial = (c1 << 8) + c0;
    553     return soc_htons(partial);
    554 }
    555 
    556 /*
    557  * Function: eep24c64_get_params
    558  *
    559  * Purpose: Get EEPROM configuration block from I2C NVRAM.
    560  *          Used internally by driver, demonstrates how to reliably
    561  *          store configuration parameters with a checksum.
    562  *
    563  * Parameters:
    564  *    u - StrataSwitch device number or I2C bus number
    565  *    d - chip device id
    566  *    c - NVRAM configuration block.
    567  *
    568  * Returns:
    569  *    SOC_E_INTERNAL - checksum bad, contents invalid.
    570  *    SOC_E_NONE - checksum read and contents valid.
    571  */
    572 STATIC int
    573 eep24c64_get_params(uint32 u, uint32 d, eep24c64_t* c)
    574 {
    575     int r = SOC_E_NONE;
    576     int l = sizeof(eep24c64_t);
    577     uint16 savesum, sum;
    578 
    579     if ( !c )
    580 	return SOC_E_PARAM;
    581 
    582     if ( (r = eep24c64_read(u, d, 0, (uint8*)c, (uint32 *)&l)) < 0 ) {
    583         LOG_INFO(BSL_LS_SOC_I2C,
    584                  (BSL_META_U(u,
    585                              "eep24c64_get_params: %s\n"), soc_errmsg(r)));
    586 	return r;
    587     }
    588     /* Checksum config block */
    589     savesum = c->chksum;
    590     sum = eep24c64_chksum(0,  (uint8 *)&c->size, sizeof(uint16) );
    591     sum = eep24c64_chksum(sum,(uint8 *)&c->type, sizeof(uint16) );
    592     sum = eep24c64_chksum(sum,(uint8 *)&c->name, LC2464_NAME_LEN);
    593 
    594     if ( sum != savesum ) {
    595         LOG_VERBOSE(BSL_LS_SOC_COMMON,
    596                     (BSL_META_U(u,
    597                                 "%s: NOTICE: EEPROM contents invalid or bad checksum\n"),
    598                      soc_i2c_devname(u, d)));
    599 	return SOC_E_INTERNAL;
    600     }
    601 
    602     return r ;
    603 }
    604 
    605 /*
    606  * Function: eep24c64_set_params
    607  *
    608  * Purpose: Set EEPROM configuration block into NVRAM.
    609  *
    610  * Parameters:
    611  *    u - StrataSwitch device number or I2C bus number
    612  *    d - chip device id
    613  *    c - NVRAM configuration block.
    614  *
    615  * Returns:
    616  *    SOC_E_TIMEOUT  - write error or chip timeout
    617  *    SOC_E_NONE - configuration block and checksum updated, written to NVRAM.
    618  */
    619 STATIC int
    620 eep24c64_set_params(uint32 u, uint32 d, eep24c64_t* c)
    621 {
    622     int rv = SOC_E_NONE;
    623     int l = sizeof(eep24c64_t);
    624     uint16 sum = 0;
    625 
    626     if ( !c )
    627 	return SOC_E_PARAM;
    628 
    629     /* Checksum config block (over length and checksum) */
    630     sum = eep24c64_chksum(0,  (uint8 *)&c->size, sizeof(uint16) );
    631     sum = eep24c64_chksum(sum,(uint8 *)&c->type, sizeof(uint16) );
    632     sum = eep24c64_chksum(sum,(uint8 *)&c->name, LC2464_NAME_LEN);
    633     c->chksum = sum;
    634     return (rv = eep24c64_write(u, d, 0, (uint8*)c, l)) == l ? SOC_E_NONE : rv;
    635 }
    636 
    637 /*
    638  * Macro: GET_IO_STAT
    639  * Purpose: Used to measure I/O performance of NVRAM reads and writes.
    640  * Parameters:
    641  *   rtn - C function to call
    642  *   name - string to display
    643  *   sz - number of bytes I/O
    644  */
    645 
    646 #ifdef	COMPILER_HAS_DOUBLE
    647 #define GET_IO_STAT(rtn, sz, name, op) { \
    648 	COMPILER_DOUBLE stime, etime; \
    649 	stime = sal_time_double(); \
    650 	(rtn); \
    651 	etime = sal_time_double(); \
    652 	LOG_VERBOSE(BSL_LS_SOC_COMMON, \
    653                     (BSL_META("%s: %s took %.2f sec %.2fKB/sec\n"), \
    654 name, op, etime - stime, \
    655                      sz / (etime - stime) / 1024)); \
    656 }
    657 #else
    658 #define GET_IO_STAT(rtn, sz, name, op) { \
    659 	COMPILER_DOUBLE stime, etime; \
    660 	stime = sal_time_usecs(); \
    661 	(rtn); \
    662 	etime = sal_time_usecs(); \
    663 	LOG_VERBOSE(BSL_LS_SOC_COMMON, \
    664                     (BSL_META("%s: %s took %u usec %dKB/sec\n"), \
    665 name, op, etime - stime, \
    666                      sz * (SECOND_USEC / 1024) / (etime - stime))); \
    667 }
    668 #endif	/* COMPILER_HAS_DOUBLE */
    669 
    670 /*
    671  * Function: eep24c64_init
    672  *
    673  * Purpose: initialize the ATMEL 24C64 NVRAM chip.  Attempt to read
    674  *          checksum, if checksum is invalid, attempt to write a pattern
    675  *          to the first three quadrants of the 64K chip, and then read 
    676  *          it all back, checking each byte to verify data integrity. When
    677  *          finished, write the new configuration block to the start
    678  *          of the chip.
    679  *
    680  *          The last quadrant of the NVRAM is write protectable and
    681  *          stores read-only static information about the platform.
    682  *
    683  *          If the checksum is valid, simply display the checksum and
    684  *          number of bytes tested initially when the chip was probed.
    685  *
    686  * Parameters:
    687  *    unit - StrataSwitch device number or I2C bus number
    688  *    devno - chip device id
    689  *    data - address of test data
    690  *    len - size of test data
    691  *
    692  *
    693  * Notes: When the initial NVRAM is configured, this test destructively
    694  *       modifies the NVRAM contents. If the NVRAM ever becomes
    695  *       corrupt, this driver will clear the NVRAM with a new data
    696  *       pattern. All reads and writes should be performed past the
    697  *       NVRAM configuration block (LC24C64_DATA_START).
    698  */
    699 STATIC int
    700 eep24c64_init(int unit, int devno, void* data, int len)
    701 {
    702     eep24c64_t config;
    703     uint8 pattern;
    704     const char *devname;
    705 
    706 #ifdef CLEAR_NVRAM_BLOCK
    707     eep24c64_write(unit, devno, 0, "nvm", 3);
    708 #endif
    709 
    710     devname = soc_i2c_devname(unit, devno);
    711     soc_i2c_devdesc_set(unit, devno, "Atmel 24C64 Serial EEPROM");
    712 
    713      sal_memset(&config, 0 , sizeof(eep24c64_t));
    714 
    715     
    716     if ( eep24c64_get_params( unit, devno, &config) < 0 ) {
    717 	int i, j, rlen = len;
    718 	uint8* outbuf = (uint8*)data;
    719 	uint8* inbuf = NULL;
    720 
    721 #ifdef RANDOM_PAGE_TEST
    722 	inbuf = (uint8*)sal_alloc(len, "i2c");
    723 	/* Fragmented (page and then some) write .. */
    724 	for(i = 0; i <= LC2464_DEVICE_RW_SIZE/8; i+= len){
    725 	    LOG_INFO(BSL_LS_SOC_I2C,
    726                      (BSL_META_U(unit,
    727                                  "eep24c64_init: unit=%d bytes=%d\n"), unit, i));
    728 	    /* Write buffer to EEPROM */
    729 	    eep24c64_write(unit, devno, i, outbuf, len);
    730 	    /* Read back data just written */
    731 	    eep24c64_read(unit, devno, i, inbuf, &rlen);
    732 
    733 	    /* Verify each byte */
    734 	    for( j = 0; j < len; j++){
    735 		if ( inbuf[j] != outbuf[j]){
    736 		    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    737                                 (BSL_META_U(unit,
    738                                             "%s: ERROR: EEPROM miscompare "
    739                                             "off=%d expected=0x%x "
    740                                             "got 0x%x (addr=%d in page)\n"),
    741                                  devname,
    742                                  i+j, outbuf[j], inbuf[j], j));
    743 		}
    744 	    }
    745 	}
    746 	sal_free(inbuf);
    747 #endif
    748 
    749 	LOG_VERBOSE(BSL_LS_SOC_COMMON,
    750                     (BSL_META_U(unit,
    751                                 "%s: testing data integrity, %d bytes\n"),
    752                      devname, LC2464_DEVICE_RW_SIZE));
    753 
    754 	/* Full buffer write in one shot ...*/
    755 	len = LC2464_DEVICE_RW_SIZE;
    756 	outbuf = (uint8*)sal_alloc(len, "i2c");
    757 	if (outbuf == NULL) {
    758 	    return SOC_E_MEMORY;
    759 	}
    760 	inbuf = (uint8*)sal_alloc(len, "i2c");
    761 	if (inbuf == NULL) {
    762 	    sal_free(outbuf);
    763 	    return SOC_E_MEMORY;	
    764 	}
    765 
    766 	/* Write known pattern, clobber the parameter block */
    767 	sal_memset(inbuf, 0x0, len);
    768 	pattern = (uint8) sal_time_usecs();
    769 	sal_memset(outbuf,pattern, len);
    770 	LOG_VERBOSE(BSL_LS_SOC_COMMON,
    771                     (BSL_META_U(unit,
    772                                 "%s: writing %d bytes pattern=0x%x\n"),
    773                      devname, len, pattern));
    774 
    775 	GET_IO_STAT(rlen = eep24c64_write(unit, devno, 0, outbuf, len),
    776 		 LC2464_DEVICE_RW_SIZE, devname, "write");
    777 
    778 	if (rlen == LC2464_DEVICE_RW_SIZE )
    779 	    ;	/* write was ok */
    780 	else if (rlen > 0)
    781 	    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    782                         (BSL_META_U(unit,
    783                                     "%s: ERROR: only %d out of %d bytes written!\n"),
    784                          devname, rlen, len));
    785 	else
    786 	    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    787                         (BSL_META_U(unit,
    788                                     "%s: ERROR: write failed: %s\n"),
    789                          devname, soc_errmsg(rlen)));
    790 
    791 	/* Clear receive buffer */
    792 	sal_memset(inbuf, 0x0, len);
    793 	rlen = len;
    794 
    795 	LOG_VERBOSE(BSL_LS_SOC_COMMON,
    796                     (BSL_META_U(unit,
    797                                 "%s: reading %d bytes\n"), devname, rlen));
    798 
    799 	GET_IO_STAT(eep24c64_read(unit, devno, 0, inbuf, (uint32 *)&rlen),
    800 		    rlen, devname, "read");
    801 
    802 	if (rlen == LC2464_DEVICE_RW_SIZE)
    803 	    ;	/* read was ok */
    804 	else if (rlen > 0)
    805 	    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    806                         (BSL_META_U(unit,
    807                                     "%s: ERROR: only %d out of %d bytes read!\n"),
    808                          devname, rlen, len));
    809 	else
    810 	    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    811                         (BSL_META_U(unit,
    812                                     "%s: ERROR: read failed: %s\n"),
    813                          devname, soc_errmsg(rlen)));
    814 
    815 	i = 0;
    816 	for( j = 0; j < LC2464_DEVICE_RW_SIZE; j++){
    817 	    if ( inbuf[j] != outbuf[j]){
    818 		LOG_VERBOSE(BSL_LS_SOC_COMMON,
    819                             (BSL_META_U(unit,
    820                                         "%s: ERROR: miscompare at offset=%d "
    821                                         "expected=0x%x got 0x%x (addr=%d in page)\n"),
    822                              devname, i+j, outbuf[j], inbuf[j], j));
    823 		i = -1;
    824 		break;
    825 	    }
    826 	}
    827 
    828 	if(i == 0)
    829 	    LOG_VERBOSE(BSL_LS_SOC_COMMON,
    830                         (BSL_META_U(unit,
    831                                     "%s: test passed (%d bytes verified)\n"),
    832                          devname, len));
    833 
    834 	sal_free(inbuf);
    835 	sal_free(outbuf);
    836 
    837 	/* Write configuration block to NVRAM */
    838 	config.size = (uint16)rlen - LC24C64_DATA_START;
    839 	config.type = 0x70;
    840 	sal_memset(config.name, 0x0,LC2464_NAME_LEN);
    841 
    842 	if(sal_strlen(soc_i2c_devname(unit,devno)) > LC2464_NAME_LEN ) {
    843         LOG_WARN(BSL_LS_SOC_I2C,
    844                  (BSL_META_U(unit,
    845                              "Device name %s too long, trimming it .... \n"),
    846                   soc_i2c_devname(unit , devno)));
    847 	sal_memcpy(config.name , soc_i2c_devname(unit, devno), LC2464_NAME_LEN - 1);
    848 	config.name[LC2464_NAME_LEN - 1] = '\0';
    849 	} else {
    850         sal_strncpy(config.name, soc_i2c_devname(unit,devno), LC2464_NAME_LEN - 1);
    851         config.name[LC2464_NAME_LEN - 1] = '\0';
    852  	}
    853 	return eep24c64_set_params(unit, devno, &config);
    854 
    855     }
    856 
    857     return SOC_E_NONE;
    858 }
    859 
    860 /*
    861  * Function: eep24c64_ioctl: does miscellaenous tasks.
    862  * Purpose: Support miscellaneous chip options.
    863  *
    864  * Parameters:
    865  *    unit - StrataSwitch device number or I2C bus number
    866  *    devno - chip device id
    867  *    command - IO control operation
    868  *    data - address of user data
    869  *    len - size of test data
    870  *
    871  * Notes:
    872  *   Placeholder for write Protect, EEPROM Chip reset - NYI
    873  */
    874 STATIC int
    875 eep24c64_ioctl(int unit, int devno,
    876 	       int command, void* data, int len)
    877 {
    878     switch ( command ) {
    879 
    880     default:
    881 	break;
    882     }
    883     return SOC_E_NONE;
    884 }
    885 
    886 
    887 /* AT24C64 64K Serial EEPROM Driver callout */
    888 i2c_driver_t _soc_i2c_eep24c64_driver = {
    889     0x0,0x0, /* System assigned bytes */
    890     LC24C64_DEVICE_TYPE,
    891     eep24c64_read,
    892     eep24c64_write,
    893     eep24c64_ioctl,
    894     eep24c64_init,
    895     NULL,
    896 };
    897 
    898 
    899 
    900 
    901   
    902 
    903 
    904 
    905