int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, uint8_t *in, int in_bytes, int flags) { int rv = EC_SUCCESS; int i; int xfer_start = flags & I2C_XFER_START; int xfer_stop = flags & I2C_XFER_STOP; #if defined(CONFIG_I2C_SCL_GATE_ADDR) && defined(CONFIG_I2C_SCL_GATE_PORT) if (port == CONFIG_I2C_SCL_GATE_PORT && slave_addr == CONFIG_I2C_SCL_GATE_ADDR) gpio_set_level(CONFIG_I2C_SCL_GATE_GPIO, 1); #endif ASSERT(out || !out_bytes); ASSERT(in || !in_bytes); /* Clear status */ if (xfer_start) { STM32_I2C_ICR(port) = STM32_I2C_ICR_ALL; STM32_I2C_CR2(port) = 0; } if (out_bytes || !in_bytes) { /* * Configure the write transfer: if we are stopping then set * AUTOEND bit to automatically set STOP bit after NBYTES. * if we are not stopping, set RELOAD bit so that we can load * NBYTES again. if we are starting, then set START bit. */ STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16) | slave_addr | ((in_bytes == 0 && xfer_stop) ? STM32_I2C_CR2_AUTOEND : 0) | ((in_bytes == 0 && !xfer_stop) ? STM32_I2C_CR2_RELOAD : 0) | (xfer_start ? STM32_I2C_CR2_START : 0); for (i = 0; i < out_bytes; i++) { rv = wait_isr(port, STM32_I2C_ISR_TXIS); if (rv) goto xfer_exit; /* Write next data byte */ STM32_I2C_TXDR(port) = out[i]; } } if (in_bytes) { if (out_bytes) { /* wait for completion of the write */ rv = wait_isr(port, STM32_I2C_ISR_TC); if (rv) goto xfer_exit; } /* * Configure the read transfer: if we are stopping then set * AUTOEND bit to automatically set STOP bit after NBYTES. * if we are not stopping, set RELOAD bit so that we can load * NBYTES again. if we were just transmitting, we need to * set START bit to send (re)start and begin read transaction. */ STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16) | STM32_I2C_CR2_RD_WRN | slave_addr | (xfer_stop ? STM32_I2C_CR2_AUTOEND : 0) | (!xfer_stop ? STM32_I2C_CR2_RELOAD : 0) | (out_bytes || xfer_start ? STM32_I2C_CR2_START : 0); for (i = 0; i < in_bytes; i++) { /* Wait for receive buffer not empty */ rv = wait_isr(port, STM32_I2C_ISR_RXNE); if (rv) goto xfer_exit; in[i] = STM32_I2C_RXDR(port); } } /* * If we are stopping, then we already set AUTOEND and we should * wait for the stop bit to be transmitted. Otherwise, we set * the RELOAD bit and we should wait for transfer complete * reload (TCR). */ rv = wait_isr(port, xfer_stop ? STM32_I2C_ISR_STOP : STM32_I2C_ISR_TCR); if (rv) goto xfer_exit; xfer_exit: /* clear status */ if (xfer_stop) STM32_I2C_ICR(port) = STM32_I2C_ICR_ALL; /* On error, queue a stop condition */ if (rv) { /* queue a STOP condition */ STM32_I2C_CR2(port) |= STM32_I2C_CR2_STOP; /* wait for it to take effect */ /* Wait up to 100 us for bus idle */ for (i = 0; i < 10; i++) { if (!(STM32_I2C_ISR(port) & STM32_I2C_ISR_BUSY)) break; udelay(10); } /* * Allow bus to idle for at least one 100KHz clock = 10 us. * This allows slaves on the bus to detect bus-idle before * the next start condition. */ udelay(10); /* re-initialize the controller */ STM32_I2C_CR2(port) = 0; STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_PE; udelay(10); STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE; } #ifdef CONFIG_I2C_SCL_GATE_ADDR if (port == CONFIG_I2C_SCL_GATE_PORT && slave_addr == CONFIG_I2C_SCL_GATE_ADDR) gpio_set_level(CONFIG_I2C_SCL_GATE_GPIO, 0); #endif return rv; }
int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, uint8_t *in, int in_bytes, int flags) { int rv = EC_SUCCESS; int i; ASSERT(out || !out_bytes); ASSERT(in || !in_bytes); /* Clear status */ STM32_I2C_ICR(port) = 0x3F38; STM32_I2C_CR2(port) = 0; if (out_bytes || !in_bytes) { /* Configure the write transfer */ STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16) | slave_addr | (in_bytes == 0 ? STM32_I2C_CR2_AUTOEND : 0); /* let's go ... */ STM32_I2C_CR2(port) |= STM32_I2C_CR2_START; for (i = 0; i < out_bytes; i++) { rv = wait_isr(port, STM32_I2C_ISR_TXIS); if (rv) goto xfer_exit; /* Write next data byte */ STM32_I2C_TXDR(port) = out[i]; } } if (in_bytes) { if (out_bytes) { /* wait for completion of the write */ rv = wait_isr(port, STM32_I2C_ISR_TC); if (rv) goto xfer_exit; } /* Configure the read transfer */ STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16) | STM32_I2C_CR2_RD_WRN | slave_addr | STM32_I2C_CR2_AUTOEND; /* START or repeated start */ STM32_I2C_CR2(port) |= STM32_I2C_CR2_START; for (i = 0; i < in_bytes; i++) { /* Wait for receive buffer not empty */ rv = wait_isr(port, STM32_I2C_ISR_RXNE); if (rv) goto xfer_exit; in[i] = STM32_I2C_RXDR(port); } } rv = wait_isr(port, STM32_I2C_ISR_STOP); if (rv) goto xfer_exit; xfer_exit: /* clear status */ STM32_I2C_ICR(port) = 0x3F38; /* On error, queue a stop condition */ if (rv) { /* queue a STOP condition */ STM32_I2C_CR2(port) |= STM32_I2C_CR2_STOP; /* wait for it to take effect */ /* Wait up to 100 us for bus idle */ for (i = 0; i < 10; i++) { if (!(STM32_I2C_ISR(port) & STM32_I2C_ISR_BUSY)) break; udelay(10); } /* * Allow bus to idle for at least one 100KHz clock = 10 us. * This allows slaves on the bus to detect bus-idle before * the next start condition. */ udelay(10); /* re-initialize the controller */ STM32_I2C_CR2(port) = 0; STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_PE; udelay(10); STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE; } return rv; }
static void i2c_event_handler(int port) { int i2c_isr; static int rx_pending, buf_idx; #ifdef TCPCI_I2C_SLAVE int addr; #endif i2c_isr = STM32_I2C_ISR(port); /* * Check for error conditions. Note, arbitration loss and bus error * are the only two errors we can get as a slave allowing clock * stretching and in non-SMBus mode. */ if (i2c_isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR)) { rx_pending = 0; tx_pending = 0; /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear error status bits */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_BERRCF | STM32_I2C_ICR_ARLOCF; } /* Transfer matched our slave address */ if (i2c_isr & STM32_I2C_ISR_ADDR) { if (i2c_isr & STM32_I2C_ISR_DIR) { /* Transmitter slave */ /* Clear transmit buffer */ STM32_I2C_ISR(port) |= STM32_I2C_ISR_TXE; /* Enable txis interrupt to start response */ STM32_I2C_CR1(port) |= STM32_I2C_CR1_TXIE; } else { /* Receiver slave */ buf_idx = 0; rx_pending = 1; } /* Clear ADDR bit by writing to ADDRCF bit */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_ADDRCF; /* Inhibit stop mode when addressed until STOPF flag is set */ disable_sleep(SLEEP_MASK_I2C_SLAVE); } /* Stop condition on bus */ if (i2c_isr & STM32_I2C_ISR_STOP) { #ifdef TCPCI_I2C_SLAVE /* * if tcpc is being addressed, and we received a stop * while rx is pending, then this is a write only to * the tcpc. */ addr = STM32_I2C_ISR_ADDCODE(STM32_I2C_ISR(port)); if (rx_pending && ADDR_IS_TCPC(addr)) i2c_process_tcpc_command(0, addr, buf_idx); #endif rx_pending = 0; tx_pending = 0; /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear STOPF bit by writing to STOPCF bit */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_STOPCF; /* No longer inhibit deep sleep after stop condition */ enable_sleep(SLEEP_MASK_I2C_SLAVE); } /* Receiver full event */ if (i2c_isr & STM32_I2C_ISR_RXNE) host_buffer[buf_idx++] = STM32_I2C_RXDR(port); /* Master requested STOP or RESTART */ if (i2c_isr & STM32_I2C_ISR_NACK) { /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear NACK */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_NACKCF; /* Resend last byte on RESTART */ if (port == I2C_PORT_EC && tx_index) tx_index--; } /* Transmitter empty event */ if (i2c_isr & STM32_I2C_ISR_TXIS) { if (port == I2C_PORT_EC) { /* host is waiting for PD response */ if (tx_pending) { if (tx_index < tx_end) { STM32_I2C_TXDR(port) = host_buffer[tx_index++]; } else { STM32_I2C_TXDR(port) = 0xec; /* * Set tx_index = 0 to prevent NACK * handler resending last buffer byte. */ tx_index = 0; tx_end = 0; /* No pending data */ tx_pending = 0; } } else if (rx_pending) { host_i2c_resp_port = port; /* * Disable TXIS interrupt, transmission will * be prepared by host command task. */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; #ifdef TCPCI_I2C_SLAVE addr = STM32_I2C_ISR_ADDCODE( STM32_I2C_ISR(port)); if (ADDR_IS_TCPC(addr)) i2c_process_tcpc_command(1, addr, buf_idx); else #endif i2c_process_command(); /* Reset host buffer after end of transfer */ rx_pending = 0; tx_pending = 1; } else { STM32_I2C_TXDR(port) = 0xec; } } } }