/****************************************************************************************//** * \file i2cDriver.c * * \brief This unit is a simple driver library for I2C bus based on the Open-Core I2C-master. * * \author PYD : 12/6/2013 * \version 0.1 * \date 12/6/2013 * PYD : 12/6/2013 initial version * PYD : 01/10/2018 remove code for ES12 and ES3 versions *//******************************************************************************************/ #include <stdio.h> #include <error.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define offsetReg(r) (r*4) //========================= SYSTEM CONFIGURATION ================================= #include <systemConfig.h> #include <ecs.h> //================================================================================ // USER ORIENTED PROCEDURES FOR EXTERNAL USE #include <i2cDriver.h> //=============================================================================== //#define I2C_FREQ 0xc7 ;#(199dec) value for 100MHz clock to match 100kHz SCL //#define I2C_FREQ 0x31 ;#(49dec) value for 100MHz clock to match 400kHz SCL //#define I2C_FREQ 0x18 ;#(24dec) value for 100MHz clock to match 800kHz SCL // addresses of I2C registers (start at 0xa40 in qsys) #define I2C_PRELOREG 0x0 #define I2C_PREHIREG 0x1 #define I2C_CTRLREG 0x2 #define I2C_TSMTREG 0x3 #define I2C_RCVREG 0x3 #define I2C_CMDREG 0x4 #define I2C_STATREG 0x4 // Control register #define I2C_ENA 0x80 #define I2C_DIS 0x00 // Command register bits #define I2C_STA 0x80 #define I2C_STO 0x40 #define I2C_RD 0x20 #define I2C_WR 0x10 #define I2C_NACK 0x08 #define I2C_IACK 0x01 // Status register bits #define I2C_TIP 0x02 #define I2C_BUSY 0x40 /***********************************************************************//** * \fn int unsigned I2C_MakeAvagoAdd(compNb) * \brief Function to build a device address * * For TX(pair) the address is 0101hjkx (hjk is hard add) (x is 1=R 0=W) * * For RX(odd) the address is 0110hjkx (hjk is hard add) (x is 1=R 0=W) * * NOTE the x is added in Read/Write function * * \return the I2c address *//***********************************************************************/ /* static unsigned i2c_MakeAvagoAdd(int compNb) { unsigned i2cAdd; // partern 0 + 0101/TX or 0110/RX + fixAdd on 3digits if (compNb%2) i2cAdd = 5<<3; else i2cAdd = 6<<3; i2cAdd = i2cAdd + compNb; return(i2cAdd); } */ unsigned lastSetInCLK[MAX_DEV]; unsigned LastSetSCLfreq[MAX_DEV]; static int getBusBase(int bus){ switch(bus){ case I2C_BUS_TEMPERATURES: return(I2C_BUS_BASE_TEMPERATURES); break; case I2C_BUS_PLL_CLEANER1: return(I2C_BUS_BASE_PLL_CLEANER1); break; case I2C_BUS_PLL_CLEANER2: return(I2C_BUS_BASE_PLL_CLEANER2); break; case I2C_BUS_PLL_TFC: return(I2C_BUS_BASE_PLL_TFC); break; case I2C_BUS_SFP1: return(I2C_BUS_BASE_SFP1); break; case I2C_BUS_SFP2: return(I2C_BUS_BASE_SFP2); break; case I2C_BUS_MINIPODS: return(I2C_BUS_BASE_MINIPODS); break; case I2C_BUS_TEMP_MEZZANINE: return(I2C_BUS_BASE_TEMP_MEZZANINE); break; case I2C_BUS_CURRENT1: return(I2C_BUS_BASE_CURRENT1); break; case I2C_BUS_CURRENT2: return(I2C_BUS_BASE_CURRENT2); break; case I2C_BUS_FANOUT: return(I2C_BUS_BASE_FANOUT); break; case I2C_BUS_FPGA_EEPROM: return(I2C_BUS_BASE_FPGA_EEPROM); default: printf("ERROR: unknown i2c bus number %d in file %s line %d\n",bus,__FILE__,__LINE__); return(99); } } /***********************************************************************//** * \fn void i2c_enable(int dev, int bus) * \brief Function to activate the i2c-opencore IP. * \param dev: the board number * \param bus: the target i2c bus number *//***********************************************************************/ static void i2c_enable(int dev, int bus) { unsigned cval = I2C_ENA; ecs_iowrI2c_slow( dev, getBusBase(bus)+ (I2C_CTRLREG*4), &cval); } /***********************************************************************//** * \fn void i2c_disable(int dev,int bus) * \param dev: the board number * \brief Function to deactivate the i2c-opencore IP. * \param bus: the target i2c bus number *//***********************************************************************/ static void i2c_disable(int dev,int bus) { unsigned cval = I2C_DIS; ecs_iowrI2c_slow( dev, getBusBase(bus)+ (I2C_CTRLREG*4), &cval); } /***********************************************************************//** * \fn void i2c_stop(int dev,int bus) * \param dev: the board number * \brief Function to generate the stop condition on the i2c bus. * \param bus: the target i2c bus number *//***********************************************************************/ static void i2c_stop(int dev, int bus) { unsigned cval = I2C_STO; ecs_iowrI2c_slow( dev, getBusBase(bus)+ (I2C_CMDREG*4), &cval); } /****************************************************************************//** * \fn void i2c_setSpeed(int dev, int bus, int value) * \brief Function to program the prescale the SCL clock line. Should be called * before the I2C use. * \param dev: the board number * \param bus: the target i2c bus number * \param value: the value to write in the prescale register *//**************************************************************************/ static void i2c_setSpeed(int dev, int bus, int value ) { unsigned cval = value>>8; int base = getBusBase(bus); ecs_iowrI2c_slow(dev, base + (I2C_PREHIREG*4), &cval); cval =value & 0xFF; ecs_iowrI2c_slow(dev, base + (I2C_PRELOREG*4), &cval); } /****************************************************************************//** * \fn int i2c_init(int dev, int bus, unsigned inCLK, unsigned SCLfreq) * \brief Function to open access to i2c opencore and set speed * \ if it is specified * \param dev: the board number * \param bus: the target i2c bus number * \param inCLK: the input system clock ferquency in Hz * \param SCLfreq: the target SCL clock frequency in Hz * \return 0 success,-1 error *//**************************************************************************/ int i2c_init(int dev, int bus, unsigned inCLK, unsigned SCLfreq) { long int prescale; // printf("Simulation init with clock %d fequency %d \n",inCLK, SCLfreq); return(0); if (ecs_openLli(dev)<0){ printf("ERROR: Can't init I2C\n"); return(-1); } i2c_stop(dev, bus); i2c_disable(dev, bus); i2c_enable(dev, bus); // set prescale register if (inCLK!=0 && SCLfreq !=0){ prescale = (inCLK/(5*SCLfreq))-1; // printf("PYD i2c i2c_init() bus %d applied speed is %ld\n", bus, prescale); i2c_setSpeed(dev, bus, prescale); lastSetInCLK[dev] = inCLK; LastSetSCLfreq[dev] = SCLfreq; } return(0); } /****************************************************************************//** * \fn int i2c_getInit(int dev, int bus, unsigned &inCLK, unsigned &SCLfreq) * \brief Read the last values in the prescale of the SCL clock line. * * \param dev: the board number * \param bus: the target i2c bus number * \param inCLK: the system clock ferquency in Hz * \param SCLfreq: the target SCL clock frequency in Hz *//**************************************************************************/ void i2c_getInit(int dev, int bus, unsigned *inCLK, unsigned *SCLfreq) { *inCLK = lastSetInCLK[dev]; *SCLfreq = LastSetSCLfreq[dev]; } /***********************************************************************************//** * \fn int _waitTIP() * \brief function to wait for the status register TIP bit value 0 "transfer complete" * * \param dev: the borad number * \param bus: the target i2c bus number * \return 0 success,-1 error *//********************************************************************************/ static int i2c_waitTIP(int dev, int bus) { unsigned val, step = 0; int base = getBusBase(bus); while(1){ usleep(10); //MJ POUR TEMPO_FERNAND LE 12 SEPT 2017 // printf("step %d\n", step); step = step + 1; if(ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &val)==0){ // printf("PYD status 0x%x\n", val); if ((val & I2C_TIP) == 0){ //transfer complete return(0); } else { // transfer in progress if (step > 500){ printf("ERROR: timeout in i2c_waitTIP()\n"); return(-1); } else { usleep(10); // printf("PYD wait %d\n", step); // for (i=0; i<loop; i++) dum++; } } } else { // read error printf("i2c_waitTIP firmware level read error KO\n"); return(-1); } }//while } /***********************************************************************//** * \fn int i2c_readMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, unsigned *val) * \brief Function to read one single register * * \param dev: the board number * \param bus: the target i2c bus number * \param slaveAdd: the address of the device to write in the I2C space * \param regIndex: the number of the register in the component * \param val : a pointer to receive the read value * \return 0 success,-1 error *//***********************************************************************/ int i2c_readMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, unsigned *val) { unsigned cval; int base = getBusBase(bus); // generate start command // write $slaveAdd (slaveAdd+WR bit) in transmitReg // // printf("PYD bus=%d i2c_readMem() at base 0x%x subadd 0x%x register %d\n", bus, base, slaveAdd, regIndex); // printf("Simulation\n"); *val=0x55; return(0); cval = slaveAdd<<1; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set STA bit and WR bit in commandReg cval = I2C_STA | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus=%d i2c_readMem() first transaction slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $regIndex in transmitReg // cval = regIndex; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set WR bit in commandReg cval = I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR:bus %d i2c_readMem() second transaction slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $slaveAdd (slaveAdd+RD bit) in transmitReg // cval = slaveAdd<<1 | 1; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set STA bit and WR bit in commandReg cval = I2C_STA | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus %d i2c_readMem() third transaction slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // set RD bit, set ACK bit =1(NACK) set STO bit in commandReg // cval = I2C_RD | I2C_NACK | I2C_STO; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait for TIP flag in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); return( ecs_iordI2c_slow(dev, base + (I2C_RCVREG*4), val) ); } /***********************************************************************//** * \fn int i2c_writeMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, unsigned *val) * \brief Function to write a value in a register * * \param dev: the board number * \param bus: the target i2c bus number * \param slaveAdd: the address of the device to write in the I2C space * \param regIndex: the number of the register in the component * \param val : a pointer to the value to write * \return 0 success,-1 error *//***********************************************************************/ int i2c_writeMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, unsigned *val){ unsigned cval; int base = getBusBase(bus); // generate start command // printf("bus %d i2c_writeMem() at add 0x%x register %d val %x\n", bus, slaveAdd, regIndex, *val); // printf("Simulation\n"); return(0); // write $slaveAdd (slaveAdd+WR bit) in transmitReg cval = slaveAdd<<1; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set STA bit and WR bit in commandReg cval = I2C_STA | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus %d slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $regIndex in transmitReg // cval = regIndex; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set WR bit in commandReg cval = I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus %d slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $slaveAdd (slaveAdd+RD bit) in transmitReg // ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), val); // set STA bit and WR bit in commandReg cval = I2C_STO | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); return(0); } /***********************************************************************//** * \fn int i2c_multReadMemInit(int dev, int bus, unsigned slaveAdd, unsigned regIndex) * \brief Function to initialize a multiread sequence from slave at * i2c address slaveAdd starting at device internal register regIndex * * \param dev: the board number * \param bus: the target i2c bus number * \param slaveAdd: the address of the device to read from in the I2C space * \param regIndex: the number of the first register in the sequence to read * \return 0 success,-1 error *//***********************************************************************/ static int i2c_multReadMemInit(int dev, int bus, unsigned slaveAdd, unsigned regIndex) { unsigned cval; int base = getBusBase(bus); // generate start command // write $slaveAdd (slaveAdd+WR bit) in transmitReg // // printf("PYD start i2c_multReadMemInit\n"); cval = slaveAdd<<1; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set STA bit and WR bit in commandReg cval = I2C_STA | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // printf("PYD wait for TIP flag in statusReg\n"); // wait for TIP flag in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 // printf("PYD read statusReg to verify RxACK=0\n"); ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus %d slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $regIndex in transmitReg // // printf("PYD write $regIndex in transmitReg\n"); cval = regIndex; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set WR bit in commandReg cval = I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // read statusReg to verify RxACK=0 ecs_iordI2c_slow(dev, base + (I2C_STATREG*4), &cval); if ((cval & I2C_NACK) == I2C_NACK){ printf("ERROR: bus %d slave address not acknowledge in I2C_Write\n",bus); return(-1); } // // write $slaveAdd (slaveAdd+RD bit) in transmitReg // // printf("PYD write $slaveAdd (slaveAdd+RD bit) in transmitReg\n"); cval = slaveAdd<<1 | 1; ecs_iowrI2c_slow(dev, base + (I2C_TSMTREG*4), &cval); // set STA bit and WR bit in commandReg cval = I2C_STA | I2C_WR; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait fir TIP flaf in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); // printf("PYD end i2c_multReadMemInit\n"); return(0); } /******************************************************************************//** * \fn int i2c_multReadMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, int nb) * \brief Function to read/print a sequence of nb conscecutive registers from slave at * i2c address slaveAdd starting at device internal register regIndex * Used with nb=1 is a function to read one register value * * This function allocate the array to store the read data in. The user * should free it after use. * * \param dev: the board number * \param bus: the target i2c bus number * \param slaveAdd: the address of the device to read from in the I2C space * \param regIndex: the number of the first register in the sequence to read * \param nb : the number of conscecutives registers to read * \param data : a pointer to receive the data array reference * \return 0 success,-1 error *//******************************************************************************/ int i2c_multReadMem(int dev, int bus, unsigned slaveAdd, unsigned regIndex, int nb, unsigned **data) { unsigned cval; int i; unsigned *bloc; int base = getBusBase(bus); if (i2c_multReadMemInit(dev, bus, slaveAdd, regIndex) != 0) return(-1); bloc = malloc(nb*sizeof(unsigned)); if (bloc==0){ perror("Can't malloc in i2c_multReadMem()\n"); return(-1); } /* printf("Reading bytes at slave add 0x%x (%d) starting at internal address 0x%x (%d)\n", slaveAdd, slaveAdd, regIndex, regIndex); */ // Read and display values cval = I2C_RD; // printf("PYD read loop\n"); for (i=0; i<(nb-1); i++){ ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait for TIP flag in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); ecs_iordI2c_slow(dev, base + (I2C_RCVREG*4), &bloc[i]); // printf("PYD 0x%x "); } // printf("PYD\n"); // last byte to transfer cval = I2C_RD | I2C_NACK | I2C_STO; ecs_iowrI2c_slow(dev, base + (I2C_CMDREG*4), &cval); // wait for TIP flag in statusReg if ( i2c_waitTIP(dev, bus) != 0) return(-1); ecs_iordI2c_slow(dev, base + (I2C_RCVREG*4), &bloc[nb-1]); *data = bloc; return(0); } #ifdef MAIN_DRIVER int main(){ unsigned val, i2cAdd; unsigned cval[3]; int i, loop; unsigned val1[] = { 0x4, 0x5, 0xc0 }; unsigned val2[] = { 0xa, 0xb, 0x00 }; unsigned i2cAddTab[] = {0x28, 0x31, 0x2A, 0x33, 0x2C, 0x35}; unsigned *data; int base; int dev = 0; if (ecs_openLli(dev) != 0){ perror("Can't access i2c ocore\n"); exit(0); } printf("TEST on PCIe access to ocore regsiters\n"); printf("======================================\n"); base = I2C_BASE_0; // raw test for pcie access to opencore registers ecs_iordI2c(dev, base + (I2C_PRELOREG*4), &cval[0]); ecs_iordI2c(dev, base + (I2C_PREHIREG*4), &cval[1]); ecs_iordI2c(dev, base + (I2C_CTRLREG*4), &cval[2]); printf("Before ...\n"); for (i=0; i<3; i++) printf("Ox%x ", cval[i]); printf("\n"); ecs_iowrI2c(dev, base + (I2C_PRELOREG*4), &val1[0]); ecs_iowrI2c(dev, base + (I2C_PREHIREG*4), &val1[1]); ecs_iowrI2c(dev, base + (I2C_CTRLREG*4), &val1[2]); printf("After first loop ... should be equal\n"); ecs_iordI2c(dev, base + (I2C_PRELOREG*4), &cval[0]); ecs_iordI2c(dev, base + (I2C_PREHIREG*4), &cval[1]); ecs_iordI2c(dev, base + (I2C_CTRLREG*4), &cval[2]); for (i=0; i<3; i++) printf(" Ox%x =?= 0x%x |", cval[i], val1[i]); printf("\n"); exit(0); ecs_iowrI2c(dev, base + (I2C_PRELOREG*4), &val2[0]); ecs_iowrI2c(dev, base + (I2C_PREHIREG*4), &val2[1]); ecs_iowrI2c(dev, base + (I2C_CTRLREG*4), &val2[2]); printf("After second loop ... should be equal\n"); ecs_iordI2c(dev, base + (I2C_PRELOREG*4), &cval[0]); ecs_iordI2c(dev, base + (I2C_PREHIREG*4), &cval[1]); ecs_iordI2c(dev, base + (I2C_CTRLREG*4), &cval[2]); for (i=0; i<3; i++) printf("Ox%x =?= 0x%x |", cval[i], val2[i]); printf("\n"); printf("\nTEST on I2C access to avago registers\n"); printf("=====================================\n"); // init and set address for TX 0 // i2c_init(dev, 100000000, 100000); // for TCL/TK because jtag master set this clock i2c_init(dev, 125000000, 100000); // fast clock with PCI // i2c_init(dev, 62500000, 100000); // slow clock with PCI // set i2c address of avago number 0 (a TX) printf("Select avago number 0 (a TX) as target\n"); i2cAdd = 5<<3; // comp 0 TX printf("Read register 0 should be 0x00 ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 0, &val); printf("(0x%x)\n", val); printf("Write NULL in page select register\n"); val = 0; // select page 0 i2c_writeMem(dev, i2cAdd, 127, &val); printf("Read read page select register should be NULL ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 127, &val); printf("(0x%x)\n", val); printf("Write 1 in page select register\n"); val = 1; // select page 0 i2c_writeMem(dev, i2cAdd, 127, &val); printf("Read read page select register should be 1 ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 127, &val); printf("(0x%x)\n", val); printf("Write NULL in page select register\n"); val = 0; // select page 0 i2c_writeMem(dev, i2cAdd, 127, &val); printf("Read read page select register should be NULL ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 127, &val); printf("(0x%x)\n", val); printf("Write 0xff in register 94 squelch\n"); val = 0xff; // select page 0 i2c_writeMem(dev, i2cAdd, 94, &val); printf("Read squelch ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 94, &val); printf("(0x%x)\n", val); printf("Write 0xff in register 95 squelch\n"); val = 0xff; // select page 0 i2c_writeMem(dev, i2cAdd, 95, &val); printf("Read squelch ---> read "); val = 0xaa; i2c_readMem(dev, i2cAdd, 95, &val); printf("(0x%x)\n", val); printf("Read Vendor Name\n"); loop = 16; // test to read vendor name at reg 152 to 167 (16bytes) // in module at address 0 for (i=0; i<loop; i++){ val = 1; i2c_readMem(dev, i2cAdd, 152+i, &val); // printf("0x%x %c ", val, (char)val); printf("%c", (char)val); } printf("\n"); i2c_multReadMem(dev, i2cAdd, 152, 16, &data); for (i=0; i<loop; i++) printf("%c", (char)data[i]); printf("\n"); return(0); } #endif