static int ms200i_i2c_access()

in platform/innovium/sonic-platform-modules-cel/midstone-200i/modules/200i_cpld.c [453:710]


static int ms200i_i2c_access(struct i2c_adapter *a, u16 addr,
              unsigned short flags, char rw, u8 cmd,
              int size, union i2c_smbus_data *data)
{
        int error = 0;

        struct ms200i_i2c_data *new_data;

        mutex_lock(&cpld_data->cpld_lock);

        /* Write the command register */
        new_data = i2c_get_adapdata(a);

        unsigned int  portid = new_data->portid;

#ifdef DEBUG_KERN
        printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|CMD %2.2X |DAT %4.4X"
            ,portid,addr,flags,rw,rw == 1 ? "READ ":"WRITE"
            ,size,                  size == 0 ? "QUICK" :
                                    size == 1 ? "BYTE" :
                                    size == 2 ? "BYTE_DATA" :
                                    size == 3 ? "WORD_DATA" :
                                    size == 4 ? "PROC_CALL" :
                                    size == 5 ? "BLOCK_DATA" :  "ERROR"
            ,cmd,data->word);
#endif
        /* Map the size to what the chip understands */
        switch (size) {
            case I2C_SMBUS_QUICK:
            case I2C_SMBUS_BYTE:
            case I2C_SMBUS_BYTE_DATA:
            case I2C_SMBUS_WORD_DATA:
            case I2C_SMBUS_BLOCK_DATA:
                break;
            default:
                printk(KERN_INFO "Unsupported transaction %d\n", size);
                error = -EOPNOTSUPP;
                goto Done;
        }

        unsigned int REG_FDR0;
        unsigned int REG_CR0;
        unsigned int REG_SR0;
        unsigned int REG_DR0;
        unsigned int REG_ID0;

        REG_FDR0  = new_data->REG_FDR0;
        REG_CR0   = new_data->REG_CR0;
        REG_SR0   = new_data->REG_SR0;
        REG_DR0   = new_data->REG_DR0;
        REG_ID0   = new_data->REG_ID0;
        outb(portid,REG_ID0);

        int timeout=0;
        int cnt=0;

        ////[S][ADDR/R]
        //Clear status register
        outb( 0 , REG_SR0);
        outb( 1 << I2C_CR_BIT_MIEN | 1 << I2C_CR_BIT_MTX | 1 << I2C_CR_BIT_MSTA ,REG_CR0);
        SET_REG_BIT_H(REG_CR0,I2C_CR_BIT_MEN);

        if(rw == I2C_SMBUS_READ &&
                (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)){
            // sent device address with Read mode
            outb(addr << 1 | 0x01,REG_DR0);
        }else{
            // sent device address with Write mode
            outb(addr << 1 | 0x00,REG_DR0);
        }



        info( "MS Start");

        //// Wait {A}
        error = i2c_wait_ack(a,50000,1);
        if(error<0){
            info( "get error %d",error);
            goto Done;
        }

        //// [CMD]{A}
        if(size == I2C_SMBUS_BYTE_DATA ||
            size == I2C_SMBUS_WORD_DATA ||
            size == I2C_SMBUS_BLOCK_DATA ||
            (size == I2C_SMBUS_BYTE && rw == I2C_SMBUS_WRITE)){

            //sent command code to data register
            outb(cmd,REG_DR0);
            info( "MS Send CMD 0x%2.2X",cmd);

            // Wait {A}
            error = i2c_wait_ack(a,50000,1);
            if(error<0){
                info( "get error %d",error);
                goto Done;
            }
        }

        switch(size){
            case I2C_SMBUS_BYTE_DATA:
                    cnt = 1;  break;
            case I2C_SMBUS_WORD_DATA:
                    cnt = 2;  break;
            case I2C_SMBUS_BLOCK_DATA:
            // in block data mode keep number of byte in block[0]
                    cnt = data->block[0];
                              break;
            default:
                    cnt = 0;  break;
        }

        // [CNT]  used only bloack data write
        if(size == I2C_SMBUS_BLOCK_DATA && rw == I2C_SMBUS_WRITE){

            outb(cnt,REG_DR0);
            info( "MS Send CNT 0x%2.2X",cnt);

            // Wait {A}
            error = i2c_wait_ack(a,50000,1);
            if(error<0){
                info( "get error %d",error);
                goto Done;
            }
        }

        // [DATA]{A}
        if( rw == I2C_SMBUS_WRITE && (
                    size == I2C_SMBUS_BYTE ||
                    size == I2C_SMBUS_BYTE_DATA ||
                    size == I2C_SMBUS_WORD_DATA ||
                    size == I2C_SMBUS_BLOCK_DATA
            )){
            int bid=0;
            info( "MS prepare to sent [%d bytes]",cnt);
            if(size == I2C_SMBUS_BLOCK_DATA ){
                bid=1;      // block[0] is cnt;
                cnt+=1;     // offset from block[0]
            }
            for(;bid<cnt;bid++){

                outb(data->block[bid],REG_DR0);
                info( "   Data > %2.2X",data->block[bid]);
                // Wait {A}
                error = i2c_wait_ack(a,50000,1);
                if(error<0){
                    goto Done;
                }
            }

        }

        //REPEATE START
        if( rw == I2C_SMBUS_READ && (
                size == I2C_SMBUS_BYTE_DATA ||
                size == I2C_SMBUS_WORD_DATA ||
                size == I2C_SMBUS_BLOCK_DATA
            )){
            info( "MS Repeated Start");

            SET_REG_BIT_L(REG_CR0,I2C_CR_BIT_MEN);
            outb(1 << I2C_CR_BIT_MIEN |
                1 << I2C_CR_BIT_MTX |
                1 << I2C_CR_BIT_MSTA |
                1 << I2C_CR_BIT_RSTA ,REG_CR0);
            SET_REG_BIT_H(REG_CR0,I2C_CR_BIT_MEN);

            // sent Address with Read mode
            outb( addr<<1 | 0x1 ,REG_DR0);

            // Wait {A}
            error = i2c_wait_ack(a,50000,1);
            if(error<0){
                goto Done;
            }

        }

        if( rw == I2C_SMBUS_READ && (
                size == I2C_SMBUS_BYTE ||
                size == I2C_SMBUS_BYTE_DATA ||
                size == I2C_SMBUS_WORD_DATA ||
                size == I2C_SMBUS_BLOCK_DATA
            )){

            switch(size){
                case I2C_SMBUS_BYTE:
                case I2C_SMBUS_BYTE_DATA:
                        cnt = 1;  break;
                case I2C_SMBUS_WORD_DATA:
                        cnt = 2;  break;
                case I2C_SMBUS_BLOCK_DATA:
                    //will be changed after recived first data
                        cnt = 3;  break;
                default:
                        cnt = 0;  break;
            }

            int bid = 0;
            info( "MS Receive");

            //set to Receive mode
            outb(1 << I2C_CR_BIT_MEN |
                1 << I2C_CR_BIT_MIEN |
                1 << I2C_CR_BIT_MSTA , REG_CR0);

            for(bid=-1;bid<cnt;bid++){

                // Wait {A}
                error = i2c_wait_ack(a,50000,0);
                if(error<0){
                    goto Done;
                }

                if(bid == cnt-2){
                    info( "SET NAK");
                    SET_REG_BIT_H(REG_CR0,I2C_CR_BIT_TXAK);
                }

                if(bid<0){
                    inb(REG_DR0);
                    info( "READ Dummy Byte" );
                }else{

                    if(bid==cnt-1){
                        info ( "SET STOP in read loop");
                        SET_REG_BIT_L(REG_CR0,I2C_CR_BIT_MSTA);
                    }
                    data->block[bid] = inb(REG_DR0);

                    info( "DATA IN [%d] %2.2X",bid,data->block[bid]);

                    if(size==I2C_SMBUS_BLOCK_DATA && bid == 0){
                        cnt = data->block[0] + 1;
                    }
                }
            }
        }


Stop:
        //[P]
        SET_REG_BIT_L(REG_CR0,I2C_CR_BIT_MSTA);
        info( "MS STOP");

Done:
        outb(1<<I2C_CR_BIT_MEN,REG_CR0);
        check(REG_CR0);
        check(REG_SR0);
#ifdef DEBUG_KERN
        printk(KERN_INFO "END --- Error code  %d",error);
#endif

        mutex_unlock(&cpld_data->cpld_lock);

        return error;
}