/** * cdns_i2c_msend - Prepare and start a master send operation * @id: pointer to the i2c device */ static void cdns_i2c_msend(struct cdns_i2c *id) { unsigned int avail_bytes; unsigned int bytes_to_send; unsigned int ctrl_reg; unsigned int isr_status; id->p_recv_buf = NULL; id->p_send_buf = id->p_msg->buf; id->send_count = id->p_msg->len; /* Set the controller in Master transmit mode and clear the FIFO. */ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); ctrl_reg &= ~CDNS_I2C_CR_RW; ctrl_reg |= CDNS_I2C_CR_CLR_FIFO; /* * Check for the message size against FIFO depth and set the * 'hold bus' bit if it is greater than FIFO depth. */ if (id->send_count > CDNS_I2C_FIFO_DEPTH) ctrl_reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); /* Clear the interrupts in interrupt status register. */ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); /* * Calculate the space available in FIFO. Check the message length * against the space available, and fill the FIFO accordingly. * Enable the interrupts. */ avail_bytes = CDNS_I2C_FIFO_DEPTH - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); if (id->send_count > avail_bytes) bytes_to_send = avail_bytes; else bytes_to_send = id->send_count; while (bytes_to_send--) { cdns_i2c_writereg((*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET); id->send_count--; } /* * Clear the bus hold flag if there is no more data * and if it is the last message. */ if (!id->bus_hold_flag && !id->send_count) cdns_i2c_clear_bus_hold(id); /* Set the slave address in address register - triggers operation. */ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET); cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET); }
/** * cdns_i2c_mrecv - Prepare and start a master receive operation * @id: pointer to the i2c device structure */ static void cdns_i2c_mrecv(struct cdns_i2c *id) { unsigned int ctrl_reg; unsigned int isr_status; id->p_recv_buf = id->p_msg->buf; id->recv_count = id->p_msg->len; /* Put the controller in master receive mode and clear the FIFO */ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO; if (id->p_msg->flags & I2C_M_RECV_LEN) id->recv_count = I2C_SMBUS_BLOCK_MAX + 1; id->curr_recv_count = id->recv_count; /* * Check for the message size against FIFO depth and set the * 'hold bus' bit if it is greater than FIFO depth. */ if (id->recv_count > CDNS_I2C_FIFO_DEPTH) ctrl_reg |= CDNS_I2C_CR_HOLD; cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); /* Clear the interrupts in interrupt status register */ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); /* * The no. of bytes to receive is checked against the limit of * max transfer size. Set transfer size register with no of bytes * receive if it is less than transfer size and transfer size if * it is more. Enable the interrupts. */ if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) { cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE; } else { cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); } /* Clear the bus hold flag if bytes to receive is less than FIFO size */ if (!id->bus_hold_flag && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) cdns_i2c_clear_bus_hold(id); /* Set the slave address in address register - triggers operation */ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET); cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET); }
/** * cdns_i2c_isr - Interrupt handler for the I2C device * @irq: irq number for the I2C device * @ptr: void pointer to cdns_i2c structure * * This function handles the data interrupt, transfer complete interrupt and * the error interrupts of the I2C device. * * Return: IRQ_HANDLED always */ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) { unsigned int isr_status, avail_bytes, updatetx; unsigned int bytes_to_send; bool hold_quirk; struct cdns_i2c *id = ptr; /* Signal completion only after everything is updated */ int done_flag = 0; irqreturn_t status = IRQ_NONE; isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); /* Handling nack and arbitration lost interrupt */ if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) { done_flag = 1; status = IRQ_HANDLED; } /* * Check if transfer size register needs to be updated again for a * large data receive operation. */ updatetx = 0; if (id->recv_count > id->curr_recv_count) updatetx = 1; hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx; /* When receiving, handle data interrupt and completion interrupt */ if (id->p_recv_buf && ((isr_status & CDNS_I2C_IXR_COMP) || (isr_status & CDNS_I2C_IXR_DATA))) { /* Read data if receive data valid is set */ while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) { /* * Clear hold bit that was set for FIFO control if * RX data left is less than FIFO depth, unless * repeated start is selected. */ if ((id->recv_count < CDNS_I2C_FIFO_DEPTH) && !id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); *(id->p_recv_buf)++ = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET); id->recv_count--; id->curr_recv_count--; if (cdns_is_holdquirk(id, hold_quirk)) break; } /* * The controller sends NACK to the slave when transfer size * register reaches zero without considering the HOLD bit. * This workaround is implemented for large data transfers to * maintain transfer size non-zero while performing a large * receive operation. */ if (cdns_is_holdquirk(id, hold_quirk)) { /* wait while fifo is full */ while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) != (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH)) ; /* * Check number of bytes to be received against maximum * transfer size and update register accordingly. */ if (((int)(id->recv_count) - CDNS_I2C_FIFO_DEPTH) > CDNS_I2C_TRANSFER_SIZE) { cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE + CDNS_I2C_FIFO_DEPTH; } else { cdns_i2c_writereg(id->recv_count - CDNS_I2C_FIFO_DEPTH, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = id->recv_count; } } else if (id->recv_count && !hold_quirk && !id->curr_recv_count) { /* Set the slave address in address register*/ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET); if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) { cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE; } else { cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = id->recv_count; } } /* Clear hold (if not repeated start) and signal completion */ if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) { if (!id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); done_flag = 1; } status = IRQ_HANDLED; } /* When sending, handle transfer complete interrupt */ if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) { /* * If there is more data to be sent, calculate the * space available in FIFO and fill with that many bytes. */ if (id->send_count) { avail_bytes = CDNS_I2C_FIFO_DEPTH - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); if (id->send_count > avail_bytes) bytes_to_send = avail_bytes; else bytes_to_send = id->send_count; while (bytes_to_send--) { cdns_i2c_writereg( (*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET); id->send_count--; } } else { /* * Signal the completion of transaction and * clear the hold bus bit if there are no * further messages to be processed. */ done_flag = 1; } if (!id->send_count && !id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); status = IRQ_HANDLED; } /* Handling Slave monitor mode interrupt */ if (isr_status & CDNS_I2C_IXR_SLV_RDY) { unsigned int ctrl_reg; /* Read control register */ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); /* Disable slave monitor mode */ ctrl_reg &= ~CDNS_I2C_CR_SLVMON; cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); /* Clear interrupt flag for slvmon mode */ cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IDR_OFFSET); done_flag = 1; status = IRQ_HANDLED; } /* Update the status for errors */ id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK; if (id->err_status) status = IRQ_HANDLED; if (done_flag) complete(&id->xfer_done); return status; }
/** * cdns_i2c_isr - Interrupt handler for the I2C device * @irq: irq number for the I2C device * @ptr: void pointer to cdns_i2c structure * * This function handles the data interrupt, transfer complete interrupt and * the error interrupts of the I2C device. * * Return: IRQ_HANDLED always */ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) { unsigned int isr_status, avail_bytes; unsigned int bytes_to_recv, bytes_to_send; struct cdns_i2c *id = ptr; /* Signal completion only after everything is updated */ int done_flag = 0; irqreturn_t status = IRQ_NONE; isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); /* Handling nack and arbitration lost interrupt */ if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) { done_flag = 1; status = IRQ_HANDLED; } /* Handling Data interrupt */ if ((isr_status & CDNS_I2C_IXR_DATA) && (id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) { /* Always read data interrupt threshold bytes */ bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH; id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH; avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); /* * if the tranfer size register value is zero, then * check for the remaining bytes and update the * transfer size register. */ if (!avail_bytes) { if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET); else cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); } /* Process the data received */ while (bytes_to_recv--) *(id->p_recv_buf)++ = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET); if (!id->bus_hold_flag && (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) cdns_i2c_clear_bus_hold(id); status = IRQ_HANDLED; } /* Handling Transfer Complete interrupt */ if (isr_status & CDNS_I2C_IXR_COMP) { if (!id->p_recv_buf) { /* * If the device is sending data If there is further * data to be sent. Calculate the available space * in FIFO and fill the FIFO with that many bytes. */ if (id->send_count) { avail_bytes = CDNS_I2C_FIFO_DEPTH - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); if (id->send_count > avail_bytes) bytes_to_send = avail_bytes; else bytes_to_send = id->send_count; while (bytes_to_send--) { cdns_i2c_writereg( (*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET); id->send_count--; } } else { /* * Signal the completion of transaction and * clear the hold bus bit if there are no * further messages to be processed. */ done_flag = 1; } if (!id->send_count && !id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); } else { if (!id->bus_hold_flag) cdns_i2c_clear_bus_hold(id); /* * If the device is receiving data, then signal * the completion of transaction and read the data * present in the FIFO. Signal the completion of * transaction. */ while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) { *(id->p_recv_buf)++ = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET); id->recv_count--; } done_flag = 1; } status = IRQ_HANDLED; } /* Update the status for errors */ id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK; if (id->err_status) status = IRQ_HANDLED; cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); if (done_flag) complete(&id->xfer_done); return status; }