/* * This file is part of the coreboot project. * * Copyright 2015 Marvell Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #undef MV_DEBUG //#define MV_DEBUG #ifdef MV_DEBUG #define DB(x) x #else #define DB(x) #endif #define mv_os_printf(args...) printk(BIOS_INFO, args) /* The TWSI interface supports both 7-bit and 10-bit addressing. */ /* This enumerator describes addressing type. */ typedef enum _mv_twsi_addr_type { ADDR7_BIT, /* 7 bit address */ ADDR10_BIT /* 10 bit address */ } MV_TWSI_ADDR_TYPE; /* This structure describes TWSI address. */ typedef struct _mv_twsi_addr { uint32_t address; /* address */ MV_TWSI_ADDR_TYPE type; /* Address type */ } MV_TWSI_ADDR; /* This structure describes a TWSI slave. */ typedef struct _mv_twsi_slave { MV_TWSI_ADDR slave_addr; int valid_offset; /* whether the slave has offset (i.e. Eeprom etc.) */ uint32_t offset; /* offset in the slave. */ int more_than256; /* whether the ofset is bigger then 256 */ } MV_TWSI_SLAVE; /* This enumerator describes TWSI protocol commands. */ typedef enum _mv_twsi_cmd { MV_TWSI_WRITE, /* TWSI write command - 0 according to spec */ MV_TWSI_READ /* TWSI read command - 1 according to spec */ } MV_TWSI_CMD; static void twsi_int_flg_clr(uint8_t chan_num); static uint8_t twsi_main_int_get(uint8_t chan_num); static void twsi_ack_bit_set(uint8_t chan_num); static uint32_t twsi_sts_get(uint8_t chan_num); static void twsi_reset(uint8_t chan_num); static int twsi_addr7_bit_set(uint8_t chan_num, uint32_t device_address, MV_TWSI_CMD command); static int twsi_addr10_bit_set(uint8_t chan_num, uint32_t device_address, MV_TWSI_CMD command); static int twsi_data_transmit(uint8_t chan_num, uint8_t *p_block, uint32_t block_size); static int twsi_data_receive(uint8_t chan_num, uint8_t *p_block, uint32_t block_size); static int twsi_target_offs_set(uint8_t chan_num, uint32_t offset, uint8_t more_than256); static int mv_twsi_start_bit_set(uint8_t chan_num); static int mv_twsi_stop_bit_set(uint8_t chan_num); static int mv_twsi_addr_set(uint8_t chan_num, MV_TWSI_ADDR *twsi_addr, MV_TWSI_CMD command); static uint32_t mv_twsi_init(uint8_t chan_num, uint32_t frequency, uint32_t Tclk, MV_TWSI_ADDR *twsi_addr, uint8_t general_call_enable); static int mv_twsi_read(uint8_t chan_num, MV_TWSI_SLAVE *twsi_slave, uint8_t *p_block, uint32_t block_size); static int mv_twsi_write(uint8_t chan_num, MV_TWSI_SLAVE *twsi_slave, uint8_t *p_block, uint32_t block_size); static uint32_t who_am_i(void); static int i2c_init(unsigned bus); static void i2c_reset(unsigned bus); static int m_initialized[MAX_I2C_NUM] = {0, 0}; static uint8_t twsi_timeout_chk(uint32_t timeout, const char *p_string) { if (timeout >= TWSI_TIMEOUT_VALUE) { DB(mv_os_printf("%s", p_string)); return MV_TRUE; } return MV_FALSE; } /******************************************************************************* * mv_twsi_start_bit_set - Set start bit on the bus * * DESCRIPTION: * This routine sets the start bit on the TWSI bus. * The routine first checks for interrupt flag condition, then it sets * the start bit in the TWSI Control register. * If the interrupt flag condition check previously was set, the function * will clear it. * The function then wait for the start bit to be cleared by the HW. * Then it waits for the interrupt flag to be set and eventually, the * TWSI status is checked to be 0x8 or 0x10(repeated start bit). * * INPUT: * chan_num - TWSI channel. * * OUTPUT: * None. * * RETURN: * MV_OK if start bit was set successfuly on the bus. * MV_FAIL if start_bit not set or status does not indicate start * condition trasmitted. * *******************************************************************************/ static int mv_twsi_start_bit_set(uint8_t chan_num) { uint8_t is_int_flag = MV_FALSE; uint32_t timeout, temp; DB(mv_os_printf("TWSI: mv_twsi_start_bit_set\n")); /* check Int flag */ if (twsi_main_int_get(chan_num)) is_int_flag = MV_TRUE; /* set start Bit */ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_START_BIT); /* in case that the int flag was set before i.e. repeated start bit */ if (is_int_flag) { DB(mv_os_printf( "TWSI: mv_twsi_start_bit_set repeated start Bit\n")); twsi_int_flg_clr(chan_num); } /* wait for interrupt */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: Start Clear bit time_out.\n")) return MV_TIMEOUT; /* check that start bit went down */ if ((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) & TWSI_CONTROL_START_BIT) != 0) { mv_os_printf("TWSI: start bit didn't go down\n"); return MV_FAIL; } /* check the status */ temp = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp)); return MV_RETRY; } else if ((temp != TWSI_START_CON_TRA) && (temp != TWSI_REPEATED_START_CON_TRA)) { mv_os_printf("TWSI: status %x after Set Start Bit.\n", temp); return MV_FAIL; } return MV_OK; } /******************************************************************************* * mv_twsi_stop_bit_set - Set stop bit on the bus * * DESCRIPTION: * This routine set the stop bit on the TWSI bus. * The function then wait for the stop bit to be cleared by the HW. * Finally the function checks for status of 0xF8. * * INPUT: * chan_num - TWSI channel * * OUTPUT: * None. * * RETURN: * MV_TRUE is stop bit was set successfuly on the bus. * *******************************************************************************/ static int mv_twsi_stop_bit_set(uint8_t chan_num) { uint32_t timeout, temp; /* Generate stop bit */ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_STOP_BIT); twsi_int_flg_clr(chan_num); /* wait for stop bit to come down */ timeout = 0; while (((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) & TWSI_CONTROL_STOP_BIT) != 0) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: ERROR - Stop bit timeout\n")) return MV_TIMEOUT; /* check that the stop bit went down */ if ((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) & TWSI_CONTROL_STOP_BIT) != 0) { mv_os_printf( "TWSI: ERROR - stop bit not went down\n"); return MV_FAIL; } /* check the status */ temp = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp)); return MV_RETRY; } else if (temp != TWSI_NO_REL_STS_INT_FLAG_IS_KEPT_0) { mv_os_printf( "TWSI: ERROR - status %x after Stop Bit\n", temp); return MV_FAIL; } return MV_OK; } /******************************************************************************* * twsi_main_int_get - Get twsi bit from main Interrupt cause. * * DESCRIPTION: * This routine returns the twsi interrupt flag value. * * INPUT: * None. * * OUTPUT: * None. * * RETURN: * MV_TRUE is interrupt flag is set, MV_FALSE otherwise. * *******************************************************************************/ static uint32_t who_am_i(void) { return (read_mpidr() & 0x1); } static uint8_t twsi_main_int_get(uint8_t chan_num) { uint32_t temp; /* get the int flag bit */ temp = mrvl_reg_read(MV_TWSI_CPU_MAIN_INT_CAUSE(chan_num, who_am_i())); if (temp & (1 << CPU_MAIN_INT_TWSI_OFFS(chan_num))) return MV_TRUE; return MV_FALSE; } /******************************************************************************* * twsi_int_flg_clr - Clear Interrupt flag. * * DESCRIPTION: * This routine clears the interrupt flag. It does NOT poll the interrupt * to make sure the clear. After clearing the interrupt, it waits for at * least 1 miliseconds. * * INPUT: * chan_num - TWSI channel * * OUTPUT: * None. * * RETURN: * None. * *******************************************************************************/ static void twsi_int_flg_clr(uint8_t chan_num) { /* wait for 1ms to prevent TWSI register write after write problems */ mdelay(1); /* clear the int flag bit */ mrvl_reg_bit_reset( TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_INT_FLAG_SET); /* wait for 1 mili sec for the clear to take effect */ mdelay(1); } /******************************************************************************* * twsi_ack_bit_set - Set acknowledge bit on the bus * * DESCRIPTION: * This routine set the acknowledge bit on the TWSI bus. * * INPUT: * None. * * OUTPUT: * None. * * RETURN: * None. * *******************************************************************************/ static void twsi_ack_bit_set(uint8_t chan_num) { /*Set the Ack bit */ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_ACK); /* Add delay of 1ms */ mdelay(1); } /******************************************************************************* * twsi_init - Initialize TWSI interface * * DESCRIPTION: * This routine: * -Reset the TWSI. * -Initialize the TWSI clock baud rate according to given frequency * parameter based on Tclk frequency and enables TWSI slave. * -Set the ack bit. * -Assign the TWSI slave address according to the TWSI address Type. * * INPUT: * chan_num - TWSI channel * frequency - TWSI frequency in KHz. (up to 100_kHZ) * * OUTPUT: * None. * * RETURN: * Actual frequency. * *******************************************************************************/ static uint32_t mv_twsi_init(uint8_t chan_num, uint32_t frequency, uint32_t Tclk, MV_TWSI_ADDR *p_twsi_addr, uint8_t general_call_enable) { uint32_t n, m, freq, margin, min_margin = 0xffffffff; uint32_t power; uint32_t actual_freq = 0, actual_n = 0, actual_m = 0, val; if (frequency > 100000) die("TWSI frequency is too high!"); DB(mv_os_printf("TWSI: mv_twsi_init - Tclk = %d freq = %d\n", Tclk, frequency)); /* Calucalte N and M for the TWSI clock baud rate */ for (n = 0; n < 8; n++) { for (m = 0; m < 16; m++) { power = 2 << n; /* power = 2^(n+1) */ freq = Tclk / (10 * (m + 1) * power); margin = ABS(frequency - freq); if ((freq <= frequency) && (margin < min_margin)) { min_margin = margin; actual_freq = freq; actual_n = n; actual_m = m; } } } DB(mv_os_printf("TWSI: mv_twsi_init - act_n %u act_m %u act_freq %u\n", actual_n, actual_m, actual_freq)); /* Reset the TWSI logic */ twsi_reset(chan_num); /* Set the baud rate */ val = ((actual_m << TWSI_BAUD_RATE_M_OFFS) | actual_n << TWSI_BAUD_RATE_N_OFFS); mrvl_reg_write(TWSI_STATUS_BAUDE_RATE_REG(chan_num), val); /* Enable the TWSI and slave */ mrvl_reg_write(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_ENA | TWSI_CONTROL_ACK); /* set the TWSI slave address */ if (p_twsi_addr->type == ADDR10_BIT) { /* writing the 2 most significant bits of the 10 bit address */ val = ((p_twsi_addr->address & TWSI_SLAVE_ADDR_10_BIT_MASK) >> TWSI_SLAVE_ADDR_10_BIT_OFFS); /* bits 7:3 must be 0x11110 */ val |= TWSI_SLAVE_ADDR_10_BIT_CONST; /* set GCE bit */ if (general_call_enable) val |= TWSI_SLAVE_ADDR_GCE_ENA; /* write slave address */ mrvl_reg_write(TWSI_SLAVE_ADDR_REG(chan_num), val); /* writing the 8 least significant bits of the 10 bit address */ val = (p_twsi_addr->address << TWSI_EXTENDED_SLAVE_OFFS) & TWSI_EXTENDED_SLAVE_MASK; mrvl_reg_write(TWSI_EXTENDED_SLAVE_ADDR_REG(chan_num), val); } else { /* set the 7 Bits address */ mrvl_reg_write(TWSI_EXTENDED_SLAVE_ADDR_REG(chan_num), 0x0); val = (p_twsi_addr->address << TWSI_SLAVE_ADDR_7_BIT_OFFS) & TWSI_SLAVE_ADDR_7_BIT_MASK; mrvl_reg_write(TWSI_SLAVE_ADDR_REG(chan_num), val); } /* unmask twsi int */ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_INT_ENA); /* unmask twsi int in Interrupt source control register */ mrvl_reg_bit_set(CPU_INT_SOURCE_CONTROL_REG( CPU_MAIN_INT_CAUSE_TWSI(chan_num)), ( 1 << CPU_INT_SOURCE_CONTROL_IRQ_OFFS)); /* Add delay of 1ms */ mdelay(1); return actual_freq; } /******************************************************************************* * twsi_sts_get - Get the TWSI status value. * * DESCRIPTION: * This routine returns the TWSI status value. * * INPUT: * chan_num - TWSI channel * * OUTPUT: * None. * * RETURN: * uint32_t - the TWSI status. * *******************************************************************************/ static uint32_t twsi_sts_get(uint8_t chan_num) { return mrvl_reg_read(TWSI_STATUS_BAUDE_RATE_REG(chan_num)); } /******************************************************************************* * twsi_reset - Reset the TWSI. * * DESCRIPTION: * Resets the TWSI logic and sets all TWSI registers to their reset values. * * INPUT: * chan_num - TWSI channel * * OUTPUT: * None. * * RETURN: * None * *******************************************************************************/ static void twsi_reset(uint8_t chan_num) { /* Reset the TWSI logic */ mrvl_reg_write(TWSI_SOFT_RESET_REG(chan_num), 0); /* wait for 2 mili sec */ mdelay(2); } /******************************************************************************* * mv_twsi_addr_set - Set address on TWSI bus. * * DESCRIPTION: * This function Set address (7 or 10 Bit address) on the Twsi Bus. * * INPUT: * chan_num - TWSI channel * p_twsi_addr - twsi address. * command - read / write . * * OUTPUT: * None. * * RETURN: * MV_OK - if setting the address completed successfully. * MV_FAIL otherwmise. * *******************************************************************************/ static int mv_twsi_addr_set(uint8_t chan_num, MV_TWSI_ADDR *p_twsi_addr, MV_TWSI_CMD command) { DB(mv_os_printf( "TWSI: mv_twsi_addr7_bit_set addr %x , type %d, cmd is %s\n", p_twsi_addr->address, p_twsi_addr->type, ((command == MV_TWSI_WRITE) ? "Write" : "Read"))); /* 10 Bit address */ if (p_twsi_addr->type == ADDR10_BIT) return twsi_addr10_bit_set(chan_num, p_twsi_addr->address, command); /* 7 Bit address */ else return twsi_addr7_bit_set(chan_num, p_twsi_addr->address, command); } /******************************************************************************* * twsi_addr10_bit_set - Set 10 Bit address on TWSI bus. * * DESCRIPTION: * There are two address phases: * 1) Write '11110' to data register bits [7:3] and 10-bit address MSB * (bits [9:8]) to data register bits [2:1] plus a write(0) or read(1) *bit * to the Data register. Then it clears interrupt flag which drive * the address on the TWSI bus. The function then waits for interrupt * flag to be active and status 0x18 (write) or 0x40 (read) to be set. * 2) write the rest of 10-bit address to data register and clears * interrupt flag which drive the address on the TWSI bus. The * function then waits for interrupt flag to be active and status * 0xD0 (write) or 0xE0 (read) to be set. * * INPUT: * chan_num - TWSI channel * device_address - twsi address. * command - read / write . * * OUTPUT: * None. * * RETURN: * MV_OK - if setting the address completed successfully. * MV_FAIL otherwmise. * *******************************************************************************/ static int twsi_addr10_bit_set(uint8_t chan_num, uint32_t device_address, MV_TWSI_CMD command) { uint32_t val, timeout; /* writing the 2 most significant bits of the 10 bit address */ val = ((device_address & TWSI_DATA_ADDR_10_BIT_MASK) >> TWSI_DATA_ADDR_10_BIT_OFFS); /* bits 7:3 must be 0x11110 */ val |= TWSI_DATA_ADDR_10_BIT_CONST; /* set command */ val |= command; mrvl_reg_write(TWSI_DATA_REG(chan_num), val); /* WA add a delay */ mdelay(1); /* clear Int flag */ twsi_int_flg_clr(chan_num); /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk( timeout, (const char *)"TWSI: addr (10_bit) Int time_out.\n")) return MV_TIMEOUT; /* check the status */ val = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val)); return MV_RETRY; } else if (((val != TWSI_AD_PLS_RD_BIT_TRA_ACK_REC) && (command == MV_TWSI_READ)) || ((val != TWSI_AD_PLS_WR_BIT_TRA_ACK_REC) && (command == MV_TWSI_WRITE))) { mv_os_printf("TWSI: status %x 1st addr (10 Bit) in %s mode.\n", val, ((command == MV_TWSI_WRITE) ? "Write" : "Read")); return MV_FAIL; } /* set 8 LSB of the address */ val = (device_address << TWSI_DATA_ADDR_7_BIT_OFFS) & TWSI_DATA_ADDR_7_BIT_MASK; mrvl_reg_write(TWSI_DATA_REG(chan_num), val); /* clear Int flag */ twsi_int_flg_clr(chan_num); /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: 2nd (10 Bit) Int tim_out.\n")) return MV_TIMEOUT; /* check the status */ val = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val)); return MV_RETRY; } else if (((val != TWSI_SEC_AD_PLS_RD_BIT_TRA_ACK_REC) && (command == MV_TWSI_READ)) || ((val != TWSI_SEC_AD_PLS_WR_BIT_TRA_ACK_REC) && (command == MV_TWSI_WRITE))) { mv_os_printf("TWSI: status %x 2nd addr(10 Bit) in %s mode.\n", val, ((command == MV_TWSI_WRITE) ? "Write" : "Read")); return MV_FAIL; } return MV_OK; } /******************************************************************************* * twsi_addr7_bit_set - Set 7 Bit address on TWSI bus. * * DESCRIPTION: * This function writes 7 bit address plus a write or read bit to the * Data register. Then it clears interrupt flag which drive the address on * the TWSI bus. The function then waits for interrupt flag to be active * and status 0x18 (write) or 0x40 (read) to be set. * * INPUT: * chan_num - TWSI channel * device_address - twsi address. * command - read / write . * * OUTPUT: * None. * * RETURN: * MV_OK - if setting the address completed successfully. * MV_FAIL otherwmise. * *******************************************************************************/ static int twsi_addr7_bit_set(uint8_t chan_num, uint32_t device_address, MV_TWSI_CMD command) { uint32_t val, timeout; /* set the address */ val = (device_address << TWSI_DATA_ADDR_7_BIT_OFFS) & TWSI_DATA_ADDR_7_BIT_MASK; /* set command */ val |= command; mrvl_reg_write(TWSI_DATA_REG(chan_num), val); /* WA add a delay */ mdelay(1); /* clear Int flag */ twsi_int_flg_clr(chan_num); /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk( timeout, (const char *)"TWSI: Addr (7 Bit) int time_out.\n")) return MV_TIMEOUT; /* check the status */ val = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val)); return MV_RETRY; } else if (((val != TWSI_AD_PLS_RD_BIT_TRA_ACK_REC) && (command == MV_TWSI_READ)) || ((val != TWSI_AD_PLS_WR_BIT_TRA_ACK_REC) && (command == MV_TWSI_WRITE))) { /* only in debug, since in boot we try to read the SPD of both DRAM, and we don't want error messeges in case DIMM doesn't exist. */ DB(mv_os_printf( "TWSI: status %x addr (7 Bit) in %s mode.\n", val, ((command == MV_TWSI_WRITE) ? "Write" : "Read"))); return MV_FAIL; } return MV_OK; } /******************************************************************************* * twsi_data_write - Trnasmit a data block over TWSI bus. * * DESCRIPTION: * This function writes a given data block to TWSI bus in 8 bit * granularity. * first The function waits for interrupt flag to be active then * For each 8-bit data: * The function writes data to data register. It then clears * interrupt flag which drives the data on the TWSI bus. * The function then waits for interrupt flag to be active and status * 0x28 to be set. * * * INPUT: * chan_num - TWSI channel * p_block - Data block. * block_size - number of chars in p_block. * * OUTPUT: * None. * * RETURN: * MV_OK - if transmiting the block completed successfully, * MV_BAD_PARAM - if p_block is NULL, * MV_FAIL otherwmise. * *******************************************************************************/ static int twsi_data_transmit(uint8_t chan_num, uint8_t *p_block, uint32_t block_size) { uint32_t timeout, temp, block_size_wr = block_size; if (NULL == p_block) return MV_BAD_PARAM; /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: Read Data Int time_out.\n")) return MV_TIMEOUT; while (block_size_wr) { /* write the data */ mrvl_reg_write(TWSI_DATA_REG(chan_num), (uint32_t)*p_block); DB(mv_os_printf( "TWSI: twsi_data_transmit place = %d write %x\n", block_size - block_size_wr, *p_block)); p_block++; block_size_wr--; twsi_int_flg_clr(chan_num); /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk( timeout, (const char *)"TWSI: time_out.\n")) return MV_TIMEOUT; /* check the status */ temp = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp)); return MV_RETRY; } else if (temp != TWSI_M_TRAN_DATA_BYTE_ACK_REC) { mv_os_printf("TWSI: status %x in write trans\n", temp); return MV_FAIL; } } return MV_OK; } /******************************************************************************* * twsi_data_receive - Receive data block from TWSI bus. * * DESCRIPTION: * This function receive data block from TWSI bus in 8bit granularity * into p_block buffer. * first The function waits for interrupt flag to be active then * For each 8-bit data: * It clears the interrupt flag which allows the next data to be * received from TWSI bus. * The function waits for interrupt flag to be active, * and status reg is 0x50. * Then the function reads data from data register, and copies it to * the given buffer. * * INPUT: * chan_num - TWSI channel * block_size - number of bytes to read. * * OUTPUT: * p_block - Data block. * * RETURN: * MV_OK - if receive transaction completed successfully, * MV_BAD_PARAM - if p_block is NULL, * MV_FAIL otherwmise. * *******************************************************************************/ static int twsi_data_receive(uint8_t chan_num, uint8_t *p_block, uint32_t block_size) { uint32_t timeout, temp, block_size_rd = block_size; if (NULL == p_block) return MV_BAD_PARAM; /* wait for Int to be Set */ timeout = 0; while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: Read Data int Time out .\n")) return MV_TIMEOUT; while (block_size_rd) { if (block_size_rd == 1) /* clear ack and Int flag */ mrvl_reg_bit_reset( TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_ACK); twsi_int_flg_clr(chan_num); /* wait for Int to be Set */ timeout = 0; while ((!twsi_main_int_get(chan_num)) && (timeout++ < TWSI_TIMEOUT_VALUE)) ; /* check for timeout */ if (MV_TRUE == twsi_timeout_chk(timeout, (const char *)"TWSI: Timeout.\n")) return MV_TIMEOUT; /* check the status */ temp = twsi_sts_get(chan_num); if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) || (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) { DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp)); return MV_RETRY; } else if ((temp != TWSI_M_REC_RD_DATA_ACK_TRA) && (block_size_rd != 1)) { mv_os_printf("TWSI: status %x in read trans\n", temp); return MV_FAIL; } else if ((temp != TWSI_M_REC_RD_DATA_ACK_NOT_TRA) && (block_size_rd == 1)) { mv_os_printf("TWSI: status %x in Rd Terminate\n", temp); return MV_FAIL; } /* read the data */ *p_block = (uint8_t)mrvl_reg_read(TWSI_DATA_REG(chan_num)); DB(mv_os_printf("TWSI: twsi_data_receive place %d read %x\n", block_size - block_size_rd, *p_block)); p_block++; block_size_rd--; } return MV_OK; } /******************************************************************************* * twsi_target_offs_set - Set TWST target offset on TWSI bus. * * DESCRIPTION: * The function support TWSI targets that have inside address space (for * example EEPROMs). The function: * 1) Convert the given offset into p_block and size. * in case the offset should be set to a TWSI slave which support * more then 256 bytes offset, the offset setting will be done * in 2 transactions. * 2) Use twsi_data_transmit to place those on the bus. * * INPUT: * chan_num - TWSI channel * offset - offset to be set on the EEPROM device. * more_than256 - whether the EEPROM device support more then 256 byte *offset. * * OUTPUT: * None. * * RETURN: * MV_OK - if setting the offset completed successfully. * MV_FAIL otherwmise. * *******************************************************************************/ static int twsi_target_offs_set(uint8_t chan_num, uint32_t offset, uint8_t more_than256) { uint8_t off_block[2]; uint32_t off_size; if (more_than256 == MV_TRUE) { off_block[0] = (offset >> 8) & 0xff; off_block[1] = offset & 0xff; off_size = 2; } else { off_block[0] = offset & 0xff; off_size = 1; } DB(mv_os_printf( "TWSI: twsi_target_offs_set off_size = %x addr1 = %x addr2 = %x\n", off_size, off_block[0], off_block[1])); return twsi_data_transmit(chan_num, off_block, off_size); } /******************************************************************************* * mv_twsi_read - Read data block from a TWSI Slave. * * DESCRIPTION: * The function calls the following functions: * -) mv_twsi_start_bit_set(); * if (EEPROM device) * -) mv_twsi_addr_set(w); * -) twsi_target_offs_set(); * -) mv_twsi_start_bit_set(); * -) mv_twsi_addr_set(r); * -) twsi_data_receive(); * -) mv_twsi_stop_bit_set(); * * INPUT: * chan_num - TWSI channel * p_twsi_slave - Twsi Slave structure. * block_size - number of bytes to read. * * OUTPUT: * p_block - Data block. * * RETURN: * MV_OK - if EEPROM read transaction completed successfully, * MV_BAD_PARAM - if p_block is NULL, * MV_FAIL otherwmise. * *******************************************************************************/ static int mv_twsi_read(uint8_t chan_num, MV_TWSI_SLAVE *p_twsi_slave, uint8_t *p_block, uint32_t block_size) { int rc; int ret = MV_FAIL; uint32_t counter = 0; if ((NULL == p_block) || (NULL == p_twsi_slave)) return MV_BAD_PARAM; do { /* wait for 1 mili sec for the clear to take effect */ if (counter > 0) mdelay(1); ret = mv_twsi_start_bit_set(chan_num); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_read:mv_twsi_start_bit_set failed\n")); return MV_FAIL; } DB(mv_os_printf( "TWSI: mv_twsi_eeprom_read after mv_twsi_start_bit_set\n")); /* in case offset exsist (i.e. eeprom ) */ if (MV_TRUE == p_twsi_slave->valid_offset) { rc = mv_twsi_addr_set(chan_num, &(p_twsi_slave->slave_addr), MV_TWSI_WRITE); if (MV_RETRY == rc) continue; else if (MV_OK != rc) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_addr_set(%d,0x%x,%d) rc=%d\n", chan_num, (uint32_t) &(p_twsi_slave->slave_addr), MV_TWSI_WRITE, rc)); return MV_FAIL; } ret = twsi_target_offs_set(chan_num, p_twsi_slave->offset, p_twsi_slave->more_than256); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "TWSI: twsi_target_offs_set Failed\n")); return MV_FAIL; } DB(mv_os_printf("TWSI: after twsi_target_offs_set\n")); ret = mv_twsi_start_bit_set(chan_num); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "TWSI: mv_twsi_start_bit_set failed\n")); return MV_FAIL; } DB(mv_os_printf("TWSI: after mv_twsi_start_bit_set\n")); } ret = mv_twsi_addr_set(chan_num, &(p_twsi_slave->slave_addr), MV_TWSI_READ); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_read: mv_twsi_addr_set 2 Failed\n")); return MV_FAIL; } DB(mv_os_printf( "TWSI: mv_twsi_eeprom_read after mv_twsi_addr_set\n")); ret = twsi_data_receive(chan_num, p_block, block_size); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_read: twsi_data_receive Failed\n")); return MV_FAIL; } DB(mv_os_printf( "TWSI: mv_twsi_eeprom_read after twsi_data_receive\n")); ret = mv_twsi_stop_bit_set(chan_num); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { DB(mv_os_printf( "mv_twsi_read: mv_twsi_stop_bit_set 3 Failed\n")); return MV_FAIL; } counter++; } while ((MV_RETRY == ret) && (counter < MAX_RETRY_CNT)); if (counter == MAX_RETRY_CNT) DB(mv_os_printf("mv_twsi_write: Retry Expire\n")); twsi_ack_bit_set(chan_num); DB(mv_os_printf( "TWSI: mv_twsi_eeprom_read after mv_twsi_stop_bit_set\n")); return MV_OK; } /******************************************************************************* * mv_twsi_write - Write data block to a TWSI Slave. * * DESCRIPTION: * The function calls the following functions: * -) mv_twsi_start_bit_set(); * -) mv_twsi_addr_set(); * -)if (EEPROM device) * -) twsi_target_offs_set(); * -) twsi_data_transmit(); * -) mv_twsi_stop_bit_set(); * * INPUT: * chan_num - TWSI channel * eeprom_address - eeprom address. * block_size - number of bytes to write. * p_block - Data block. * * OUTPUT: * None * * RETURN: * MV_OK - if EEPROM read transaction completed successfully. * MV_BAD_PARAM - if p_block is NULL, * MV_FAIL otherwmise. * * NOTE: Part of the EEPROM, required that the offset will be aligned to the * max write burst supported. *******************************************************************************/ static int mv_twsi_write(uint8_t chan_num, MV_TWSI_SLAVE *p_twsi_slave, uint8_t *p_block, uint32_t block_size) { int ret = MV_FAIL; uint32_t counter = 0; if ((NULL == p_block) || (NULL == p_twsi_slave)) return MV_BAD_PARAM; do { if (counter > 0) /* wait for 1 mili sec for the clear to take effect */ mdelay(1); ret = mv_twsi_start_bit_set(chan_num); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_write: mv_twsi_start_bit_set failed\n")); return MV_FAIL; } ret = mv_twsi_addr_set(chan_num, &(p_twsi_slave->slave_addr), MV_TWSI_WRITE); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_write: mv_twsi_addr_set failed\n")); return MV_FAIL; } /* in case offset exsist (i.e. eeprom ) */ if (MV_TRUE == p_twsi_slave->valid_offset) { ret = twsi_target_offs_set(chan_num, p_twsi_slave->offset, p_twsi_slave->more_than256); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "TWSI: twsi_target_offs_set failed\n")); return MV_FAIL; } } ret = twsi_data_transmit(chan_num, p_block, block_size); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { mv_twsi_stop_bit_set(chan_num); DB(mv_os_printf( "mv_twsi_write: twsi_data_transmit failed\n")); return MV_FAIL; } ret = mv_twsi_stop_bit_set(chan_num); if (MV_RETRY == ret) continue; else if (MV_OK != ret) { DB(mv_os_printf( "mv_twsi_write: failed to set stopbit\n")); return MV_FAIL; } counter++; } while ((MV_RETRY == ret) && (counter < MAX_RETRY_CNT)); if (counter == MAX_RETRY_CNT) DB(mv_os_printf("mv_twsi_write: Retry Expire\n")); return MV_OK; } static int i2c_init(unsigned bus) { if (bus >= MAX_I2C_NUM) return 1; if (!m_initialized[bus]) { /* TWSI init */ MV_TWSI_ADDR slave; slave.type = ADDR7_BIT; slave.address = 0; mv_twsi_init(bus, TWSI_SPEED, mv_tclk_get(), &slave, 0); m_initialized[bus] = 1; } return 0; } static void i2c_reset(unsigned bus) { if (bus < MAX_I2C_NUM) m_initialized[bus] = 0; } int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count) { struct i2c_seg *seg = segments; int ret = 0; MV_TWSI_SLAVE twsi_slave; if (i2c_init(bus)) return 1; while (!ret && seg_count--) { twsi_slave.slave_addr.address = seg->chip; twsi_slave.slave_addr.type = ADDR7_BIT; twsi_slave.more_than256 = MV_FALSE; twsi_slave.valid_offset = MV_FALSE; if (seg->read) ret = mv_twsi_read(bus, &twsi_slave, seg->buf, seg->len); else ret = mv_twsi_write(bus, &twsi_slave, seg->buf, seg->len); seg++; } if (ret) { i2c_reset(bus); DB(mv_os_printf("mv_twsi_read/mv_twsi_write failed\n")); return 1; } return 0; }