blob: f3fc063152aba8032a2be28c7081ed820ca5517e [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include "qm_ss_i2c.h"
#include "clk.h"
#define TX_TL (2)
#define RX_TL (5)
/* number of retries before giving up on disabling the controller */
#define I2C_POLL_COUNT (1000000)
#define I2C_POLL_MICROSECOND (1)
static uint32_t i2c_base[QM_SS_I2C_NUM] = {QM_SS_I2C_0_BASE, QM_SS_I2C_1_BASE};
static volatile const qm_ss_i2c_transfer_t *i2c_transfer[QM_SS_I2C_NUM];
static volatile uint32_t i2c_write_pos[QM_SS_I2C_NUM],
i2c_read_pos[QM_SS_I2C_NUM], i2c_read_cmd_send[QM_SS_I2C_NUM];
static void controller_enable(const qm_ss_i2c_t i2c);
static int controller_disable(const qm_ss_i2c_t i2c);
static uint32_t
i2c_fill_tx_fifo(const qm_i2c_t i2c,
const volatile qm_ss_i2c_transfer_t *const transfer,
uint32_t controller)
{
uint32_t data_cmd = 0, count_tx = (QM_SS_I2C_FIFO_SIZE - TX_TL);
uint32_t write_buffer_remaining = transfer->tx_len - i2c_write_pos[i2c];
uint32_t read_buffer_remaining = transfer->rx_len - i2c_read_pos[i2c];
while ((count_tx) && write_buffer_remaining) {
count_tx--;
write_buffer_remaining--;
/* write command -IC_DATA_CMD[8] = 0 */
/* fill IC_DATA_CMD[7:0] with the data */
data_cmd = QM_SS_I2C_DATA_CMD_PUSH |
i2c_transfer[i2c]->tx[i2c_write_pos[i2c]];
/* if transfer is a combined transfer, only
* send stop at
* end of the transfer sequence */
if (i2c_transfer[i2c]->stop && (read_buffer_remaining == 0) &&
(write_buffer_remaining == 0)) {
data_cmd |= QM_SS_I2C_DATA_CMD_STOP;
}
/* write data */
QM_SS_I2C_WRITE_DATA_CMD(controller, data_cmd);
i2c_write_pos[i2c]++;
/* TX_EMPTY INTR is autocleared when the buffer
* levels goes above the threshold
*/
}
return write_buffer_remaining;
}
static void handle_i2c_error_interrupt(const qm_ss_i2c_t i2c)
{
uint32_t controller = i2c_base[i2c];
qm_ss_i2c_status_t status = 0;
int rc = -EIO;
/* Check for TX_OVER error */
if (QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_OVER) {
status = QM_SS_I2C_TX_OVER;
/* Clear interrupt */
QM_SS_I2C_CLEAR_TX_OVER_INTR(controller);
}
/* Check for RX_UNDER error */
if (QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_RX_UNDER) {
status = QM_SS_I2C_RX_UNDER;
/* Clear interrupt */
QM_SS_I2C_CLEAR_RX_UNDER_INTR(controller);
}
/* Check for RX_OVER error */
if (QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_RX_OVER) {
status = QM_SS_I2C_RX_OVER;
/* Clear interrupt */
QM_SS_I2C_CLEAR_RX_OVER_INTR(controller);
}
/* Check for TX_ABRT error */
if ((QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_ABRT)) {
QM_ASSERT(!(QM_SS_I2C_READ_TX_ABRT_SOURCE(controller) &
QM_SS_I2C_TX_ABRT_SBYTE_NORSTRT));
status = QM_SS_I2C_TX_ABORT;
status |= (QM_SS_I2C_READ_TX_ABRT_SOURCE(controller) &
QM_SS_I2C_TX_ABRT_SOURCE_ALL_MASK);
/* Clear interrupt */
QM_SS_I2C_CLEAR_TX_ABRT_INTR(controller);
rc = (status & QM_SS_I2C_TX_ABRT_USER_ABRT) ? -ECANCELED : -EIO;
}
/* Mask interrupts */
QM_SS_I2C_MASK_ALL_INTERRUPTS(controller);
controller_disable(i2c);
if (i2c_transfer[i2c]->callback) {
i2c_transfer[i2c]->callback(i2c_transfer[i2c]->callback_data,
rc, status, 0);
}
}
static void handle_i2c_rx_avail_interrupt(const qm_ss_i2c_t i2c)
{
const volatile qm_ss_i2c_transfer_t *const transfer = i2c_transfer[i2c];
uint32_t controller = i2c_base[i2c];
uint32_t read_buffer_remaining = transfer->rx_len - i2c_read_pos[i2c];
/* RX read from buffer */
if ((QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_RX_FULL)) {
while (read_buffer_remaining &&
(QM_SS_I2C_READ_RXFLR(controller))) {
QM_SS_I2C_READ_RX_FIFO(controller);
/* IC_DATA_CMD[7:0] contains received data */
i2c_transfer[i2c]->rx[i2c_read_pos[i2c]] =
QM_SS_I2C_READ_DATA_CMD(controller);
read_buffer_remaining--;
i2c_read_pos[i2c]++;
if (read_buffer_remaining == 0) {
/* mask rx full interrupt if transfer
* complete
*/
QM_SS_I2C_MASK_INTERRUPT(
controller,
QM_SS_I2C_INTR_MASK_RX_FULL |
QM_SS_I2C_INTR_MASK_TX_EMPTY);
if (i2c_transfer[i2c]->stop) {
controller_disable(i2c);
}
if (i2c_transfer[i2c]->callback) {
i2c_transfer[i2c]->callback(
i2c_transfer[i2c]->callback_data, 0,
QM_SS_I2C_IDLE, i2c_read_pos[i2c]);
}
}
}
if (read_buffer_remaining > 0 &&
read_buffer_remaining < (RX_TL + 1)) {
/* Adjust the RX threshold so the next 'RX_FULL'
* interrupt is generated when all the remaining
* data are received.
*/
QM_SS_I2C_CLEAR_RX_TL(controller);
QM_SS_I2C_WRITE_RX_TL(controller,
(read_buffer_remaining - 1));
}
/* RX_FULL INTR is autocleared when the buffer
* levels goes below the threshold
*/
}
}
static void handle_i2c_tx_req_interrupt(const qm_ss_i2c_t i2c)
{
const volatile qm_ss_i2c_transfer_t *const transfer = i2c_transfer[i2c];
uint32_t controller = i2c_base[i2c];
uint32_t count_tx;
uint32_t read_buffer_remaining = transfer->rx_len - i2c_read_pos[i2c];
uint32_t write_buffer_remaining = transfer->tx_len - i2c_write_pos[i2c];
uint32_t missing_bytes;
if ((QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_EMPTY)) {
if ((QM_SS_I2C_READ_STATUS(controller) &
QM_SS_I2C_STATUS_TFE) &&
(i2c_transfer[i2c]->tx != NULL) &&
(write_buffer_remaining == 0) &&
(read_buffer_remaining == 0)) {
QM_SS_I2C_MASK_INTERRUPT(controller,
QM_SS_I2C_INTR_MASK_TX_EMPTY);
/* if this is not a combined
* transaction, disable the controller now
*/
if (i2c_transfer[i2c]->stop) {
controller_disable(i2c);
}
/* callback */
if (i2c_transfer[i2c]->callback) {
i2c_transfer[i2c]->callback(
i2c_transfer[i2c]->callback_data, 0,
QM_SS_I2C_IDLE, i2c_write_pos[i2c]);
}
}
write_buffer_remaining =
i2c_fill_tx_fifo(i2c, i2c_transfer[i2c], controller);
/* If missing_bytes is not null, then that means we are already
* waiting for some bytes after sending read request on the
* previous interruption. We have to take into account this
* value in order to not send too much request so we won't fall
* into rx overflow */
missing_bytes = read_buffer_remaining - i2c_read_cmd_send[i2c];
/* Sanity check: The number of read data but not processed
* cannot be more than the number of expected bytes */
QM_ASSERT(QM_SS_I2C_READ_RXFLR(controller) <= missing_bytes);
/* count_tx is the remaining size in the fifo */
count_tx =
QM_SS_I2C_FIFO_SIZE - QM_SS_I2C_READ_TXFLR(controller);
if (count_tx > missing_bytes) {
count_tx -= missing_bytes;
} else {
count_tx = 0;
}
while (i2c_read_cmd_send[i2c] &&
(write_buffer_remaining == 0) && count_tx) {
count_tx--;
i2c_read_cmd_send[i2c]--;
/* if transfer is a combined transfer, only
* send stop at
* end of
* the transfer sequence */
if (i2c_transfer[i2c]->stop &&
(i2c_read_cmd_send[i2c] == 0)) {
QM_SS_I2C_WRITE_DATA_CMD(
controller, (QM_SS_I2C_DATA_CMD_CMD |
QM_SS_I2C_DATA_CMD_PUSH |
QM_SS_I2C_DATA_CMD_STOP));
} else {
QM_SS_I2C_WRITE_DATA_CMD(
controller, (QM_SS_I2C_DATA_CMD_CMD |
QM_SS_I2C_DATA_CMD_PUSH));
}
}
/* generate a tx_empty interrupt when tx fifo is fully
* empty */
if ((write_buffer_remaining == 0) &&
(read_buffer_remaining == 0)) {
QM_SS_I2C_CLEAR_TX_TL(controller);
}
}
}
static void handle_i2c_stop_det_interrupt(const qm_ss_i2c_t i2c)
{
uint32_t controller = i2c_base[i2c];
if ((QM_SS_I2C_READ_INTR_STAT(controller) & QM_SS_I2C_INTR_STAT_STOP)) {
/* Clear interrupt */
QM_SS_I2C_CLEAR_STOP_DET_INTR(controller);
if (i2c_transfer[i2c]->callback) {
i2c_transfer[i2c]->callback(
i2c_transfer[i2c]->callback_data, 0, QM_SS_I2C_IDLE,
0);
}
}
}
QM_ISR_DECLARE(qm_ss_i2c_0_error_isr)
{
handle_i2c_error_interrupt(QM_SS_I2C_0);
}
QM_ISR_DECLARE(qm_ss_i2c_0_rx_avail_isr)
{
handle_i2c_rx_avail_interrupt(QM_SS_I2C_0);
}
QM_ISR_DECLARE(qm_ss_i2c_0_tx_req_isr)
{
handle_i2c_tx_req_interrupt(QM_SS_I2C_0);
}
QM_ISR_DECLARE(qm_ss_i2c_0_stop_det_isr)
{
handle_i2c_stop_det_interrupt(QM_SS_I2C_0);
}
QM_ISR_DECLARE(qm_ss_i2c_1_error_isr)
{
handle_i2c_error_interrupt(QM_SS_I2C_1);
}
QM_ISR_DECLARE(qm_ss_i2c_1_rx_avail_isr)
{
handle_i2c_rx_avail_interrupt(QM_SS_I2C_1);
}
QM_ISR_DECLARE(qm_ss_i2c_1_tx_req_isr)
{
handle_i2c_tx_req_interrupt(QM_SS_I2C_1);
}
QM_ISR_DECLARE(qm_ss_i2c_1_stop_det_isr)
{
handle_i2c_stop_det_interrupt(QM_SS_I2C_1);
}
static uint32_t get_lo_cnt(uint32_t lo_time_ns)
{
return (((clk_sys_get_ticks_per_us() * lo_time_ns) / 1000) - 1);
}
static uint32_t get_hi_cnt(qm_ss_i2c_t i2c, uint32_t hi_time_ns)
{
uint32_t controller = i2c_base[i2c];
return (((clk_sys_get_ticks_per_us() * hi_time_ns) / 1000) - 7 -
(QM_SS_I2C_READ_SPKLEN(controller)));
}
int qm_ss_i2c_set_config(const qm_ss_i2c_t i2c,
const qm_ss_i2c_config_t *const cfg)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(cfg != NULL, -EINVAL);
uint32_t controller = i2c_base[i2c], lcnt = 0, hcnt = 0, min_lcnt = 0,
lcnt_diff = 0;
QM_SS_I2C_WRITE_CLKEN(controller);
/* mask all interrupts */
QM_SS_I2C_MASK_ALL_INTERRUPTS(controller);
/* disable controller */
if (controller_disable(i2c)) {
return -EBUSY;
}
/* Set mode */
QM_SS_I2C_WRITE_RESTART_EN(controller);
QM_SS_I2C_WRITE_ADDRESS_MODE(controller, cfg->address_mode);
/*
* Timing generation algorithm:
* 1. compute hi/lo count so as to achieve the desired bus
* speed at 50% duty cycle
* 2. adjust the hi/lo count to ensure that minimum hi/lo
* timings are guaranteed as per spec.
*/
QM_SS_I2C_CLEAR_SPKLEN(controller);
QM_SS_I2C_CLEAR_SPEED(controller);
switch (cfg->speed) {
case QM_SS_I2C_SPEED_STD:
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_SS);
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_SS);
min_lcnt = get_lo_cnt(QM_I2C_MIN_SS_NS);
lcnt = get_lo_cnt(QM_I2C_SS_50_DC_NS);
hcnt = get_hi_cnt(i2c, QM_I2C_SS_50_DC_NS);
break;
case QM_SS_I2C_SPEED_FAST:
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_FS);
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_FS);
min_lcnt = get_lo_cnt(QM_I2C_MIN_FS_NS);
lcnt = get_lo_cnt(QM_I2C_FS_50_DC_NS);
hcnt = get_hi_cnt(i2c, QM_I2C_FS_50_DC_NS);
break;
#if HAS_SS_I2C_FAST_PLUS_SPEED
case QM_SS_I2C_SPEED_FAST_PLUS:
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_FSP);
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_FSP);
min_lcnt = get_lo_cnt(QM_I2C_MIN_FSP_NS);
lcnt = get_lo_cnt(QM_I2C_FSP_50_DC_NS);
hcnt = get_hi_cnt(i2c, QM_I2C_FSP_50_DC_NS);
break;
#endif /* HAS_SS_I2C_FAST_PLUS_SPEED */
}
if (hcnt > QM_SS_I2C_IC_HCNT_MAX || hcnt < QM_SS_I2C_IC_HCNT_MIN) {
return -EINVAL;
}
if (lcnt > QM_SS_I2C_IC_LCNT_MAX || lcnt < QM_SS_I2C_IC_LCNT_MIN) {
return -EINVAL;
}
/* Increment minimum low count to account for rounding down */
min_lcnt++;
if (lcnt < min_lcnt) {
lcnt_diff = (min_lcnt - lcnt);
lcnt += (lcnt_diff);
hcnt -= (lcnt_diff);
}
if (QM_SS_I2C_SPEED_STD == cfg->speed) {
QM_SS_I2C_CLEAR_SS_SCL_HCNT(controller);
QM_SS_I2C_CLEAR_SS_SCL_LCNT(controller);
QM_SS_I2C_WRITE_SS_SCL_HCNT(controller, hcnt);
QM_SS_I2C_WRITE_SS_SCL_LCNT(controller, lcnt);
} else { /* Fast and fast plus modes */
QM_SS_I2C_CLEAR_FS_SCL_HCNT(controller);
QM_SS_I2C_CLEAR_FS_SCL_LCNT(controller);
QM_SS_I2C_WRITE_FS_SCL_HCNT(controller, hcnt);
QM_SS_I2C_WRITE_FS_SCL_LCNT(controller, lcnt);
}
return 0;
}
int qm_ss_i2c_set_speed(const qm_ss_i2c_t i2c, const qm_ss_i2c_speed_t speed,
const uint16_t lo_cnt, const uint16_t hi_cnt)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(hi_cnt < QM_SS_I2C_IC_HCNT_MAX &&
hi_cnt > QM_SS_I2C_IC_HCNT_MIN,
-EINVAL);
QM_CHECK(lo_cnt < QM_SS_I2C_IC_LCNT_MAX &&
lo_cnt > QM_SS_I2C_IC_LCNT_MIN,
-EINVAL);
uint32_t controller = i2c_base[i2c];
QM_SS_I2C_CLEAR_SPKLEN(controller);
QM_SS_I2C_CLEAR_SPEED(controller);
switch (speed) {
case QM_SS_I2C_SPEED_STD:
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_SS);
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_SS);
QM_SS_I2C_CLEAR_SS_SCL_HCNT(controller);
QM_SS_I2C_CLEAR_SS_SCL_LCNT(controller);
QM_SS_I2C_WRITE_SS_SCL_HCNT(controller, hi_cnt);
QM_SS_I2C_WRITE_SS_SCL_LCNT(controller, lo_cnt);
break;
case QM_SS_I2C_SPEED_FAST:
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_FS);
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_FS);
QM_SS_I2C_CLEAR_FS_SCL_HCNT(controller);
QM_SS_I2C_CLEAR_FS_SCL_LCNT(controller);
QM_SS_I2C_WRITE_FS_SCL_HCNT(controller, hi_cnt);
QM_SS_I2C_WRITE_FS_SCL_LCNT(controller, lo_cnt);
break;
#if HAS_SS_I2C_FAST_PLUS_SPEED
case QM_SS_I2C_SPEED_FAST_PLUS:
QM_SS_I2C_WRITE_SPKLEN(controller, QM_SS_I2C_SPK_LEN_FSP);
QM_SS_I2C_WRITE_SPEED(controller, QM_SS_I2C_CON_SPEED_FSP);
QM_SS_I2C_CLEAR_FS_SCL_HCNT(controller);
QM_SS_I2C_CLEAR_FS_SCL_LCNT(controller);
QM_SS_I2C_WRITE_FS_SCL_HCNT(controller, hi_cnt);
QM_SS_I2C_WRITE_FS_SCL_LCNT(controller, lo_cnt);
break;
#endif /* HAS_SS_I2C_FAST_PLUS_SPEED */
}
return 0;
}
int qm_ss_i2c_get_status(const qm_ss_i2c_t i2c,
qm_ss_i2c_status_t *const status)
{
QM_CHECK(status != NULL, -EINVAL);
uint32_t controller = i2c_base[i2c];
*status = QM_SS_I2C_IDLE;
/* check if slave or master are active */
if (QM_SS_I2C_READ_STATUS(controller) & QM_SS_I2C_STATUS_BUSY_MASK) {
*status |= QM_SS_I2C_BUSY;
}
/* check for abort status */
*status |= (QM_SS_I2C_READ_TX_ABRT_SOURCE(controller) &
QM_SS_I2C_TX_ABRT_SOURCE_ALL_MASK);
return 0;
}
int qm_ss_i2c_master_write(const qm_ss_i2c_t i2c, const uint16_t slave_addr,
const uint8_t *const data, uint32_t len,
const bool stop, qm_ss_i2c_status_t *const status)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(data != NULL, -EINVAL);
QM_CHECK(len > 0, -EINVAL);
uint8_t *d = (uint8_t *)data;
uint32_t controller = i2c_base[i2c], data_cmd = 0;
int ret = 0;
/* write slave address to TAR */
QM_SS_I2C_CLEAR_TAR(controller);
QM_SS_I2C_WRITE_TAR(controller, slave_addr);
/* enable controller */
controller_enable(i2c);
while (len--) {
/* wait if FIFO is full */
while (!(QM_SS_I2C_READ_STATUS(controller) &
QM_SS_I2C_STATUS_TFNF))
;
/* write command -IC_DATA_CMD[8] = 0 */
/* fill IC_DATA_CMD[7:0] with the data */
data_cmd = *d;
data_cmd |= QM_SS_I2C_DATA_CMD_PUSH;
/* send stop after last byte */
if (len == 0 && stop) {
data_cmd |= QM_SS_I2C_DATA_CMD_STOP;
}
QM_SS_I2C_WRITE_DATA_CMD(controller, data_cmd);
d++;
}
/* this is a blocking call, wait until FIFO is empty or tx abrt
* error */
while (!(QM_SS_I2C_READ_STATUS(controller) & QM_SS_I2C_STATUS_TFE))
;
if ((QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_ABRT)) {
ret = -EIO;
}
/* disable controller */
if (true == stop) {
if (controller_disable(i2c)) {
ret = -EBUSY;
}
}
if (status != NULL) {
qm_ss_i2c_get_status(i2c, status);
}
/* Clear abort status
* The controller flushes/resets/empties
* the TX FIFO whenever this bit is set. The TX
* FIFO remains in this flushed state until the
* register IC_CLR_TX_ABRT is read.
*/
QM_SS_I2C_CLEAR_TX_ABRT_INTR(controller);
return ret;
}
int qm_ss_i2c_master_read(const qm_ss_i2c_t i2c, const uint16_t slave_addr,
uint8_t *const data, uint32_t len, const bool stop,
qm_ss_i2c_status_t *const status)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(data != NULL, -EINVAL);
QM_CHECK(len > 0, -EINVAL);
uint32_t controller = i2c_base[i2c],
data_cmd = QM_SS_I2C_DATA_CMD_CMD | QM_SS_I2C_DATA_CMD_PUSH;
uint8_t *d = (uint8_t *)data;
int ret = 0;
/* write slave address to TAR */
QM_SS_I2C_CLEAR_TAR(controller);
QM_SS_I2C_WRITE_TAR(controller, slave_addr);
/* enable controller */
controller_enable(i2c);
while (len--) {
if (len == 0 && stop) {
data_cmd |= QM_SS_I2C_DATA_CMD_STOP;
}
QM_SS_I2C_WRITE_DATA_CMD(controller, data_cmd);
/* wait if rx fifo is empty, break if tx empty and
* error*/
while (!(QM_SS_I2C_READ_STATUS(controller) &
QM_SS_I2C_STATUS_RFNE)) {
if (QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_ABRT) {
break;
}
}
if ((QM_SS_I2C_READ_INTR_STAT(controller) &
QM_SS_I2C_INTR_STAT_TX_ABRT)) {
ret = -EIO;
break;
}
QM_SS_I2C_READ_RX_FIFO(controller);
/* wait until rx fifo is empty, indicating pop is complete*/
while (
(QM_SS_I2C_READ_STATUS(controller) & QM_SS_I2C_STATUS_RFNE))
;
/* IC_DATA_CMD[7:0] contains received data */
*d = QM_SS_I2C_READ_DATA_CMD(controller);
d++;
}
/* disable controller */
if (true == stop) {
if (controller_disable(i2c)) {
ret = -EBUSY;
}
}
if (status != NULL) {
qm_ss_i2c_get_status(i2c, status);
}
/* Clear abort status
* The controller flushes/resets/empties
* the TX FIFO whenever this bit is set. The TX
* FIFO remains in this flushed state until the
* register IC_CLR_TX_ABRT is read.
*/
QM_SS_I2C_CLEAR_TX_ABRT_INTR(controller);
return ret;
}
int qm_ss_i2c_master_irq_transfer(const qm_ss_i2c_t i2c,
const qm_ss_i2c_transfer_t *const xfer,
const uint16_t slave_addr)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(NULL != xfer, -EINVAL);
uint32_t controller = i2c_base[i2c];
/* write slave address to TAR */
QM_SS_I2C_CLEAR_TAR(controller);
QM_SS_I2C_WRITE_TAR(controller, slave_addr);
i2c_write_pos[i2c] = 0;
i2c_read_pos[i2c] = 0;
i2c_read_cmd_send[i2c] = xfer->rx_len;
i2c_transfer[i2c] = xfer;
/* set threshold */
if (xfer->rx_len > 0 && xfer->rx_len < (RX_TL + 1)) {
/* If 'rx_len' is less than the default threshold, we have to
* change the threshold value so the 'RX FULL' interrupt is
* generated once all data from the transfer is received.
*/
QM_SS_I2C_CLEAR_RX_TL(controller);
QM_SS_I2C_CLEAR_TX_TL(controller);
QM_SS_I2C_WRITE_RX_TL(controller, (xfer->rx_len - 1));
QM_SS_I2C_WRITE_TX_TL(controller, TX_TL);
} else {
QM_SS_I2C_CLEAR_RX_TL(controller);
QM_SS_I2C_CLEAR_TX_TL(controller);
QM_SS_I2C_WRITE_RX_TL(controller, RX_TL);
QM_SS_I2C_WRITE_TX_TL(controller, TX_TL);
}
/* enable controller */
controller_enable(i2c);
/* Start filling tx fifo. */
i2c_fill_tx_fifo(i2c, xfer, controller);
/* unmask interrupts */
QM_SS_I2C_UNMASK_INTERRUPTS(controller);
return 0;
}
static void controller_enable(const qm_ss_i2c_t i2c)
{
uint32_t controller = i2c_base[i2c];
if (!(QM_SS_I2C_READ_ENABLE_STATUS(controller) &
QM_SS_I2C_ENABLE_STATUS_IC_EN)) {
/* enable controller */
QM_SS_I2C_ENABLE(controller);
/* wait until controller is enabled */
while (!(QM_SS_I2C_READ_ENABLE_STATUS(controller) &
QM_SS_I2C_ENABLE_STATUS_IC_EN))
;
}
/* Clear all interruption flags */
QM_SS_I2C_CLEAR_ALL_INTR(controller);
}
static int controller_disable(const qm_ss_i2c_t i2c)
{
uint32_t controller = i2c_base[i2c];
int poll_count = I2C_POLL_COUNT;
/* disable controller */
QM_SS_I2C_DISABLE(controller);
/* wait until controller is disabled */
while ((QM_SS_I2C_READ_ENABLE_STATUS(controller) &
QM_SS_I2C_ENABLE_STATUS_IC_EN) &&
poll_count--) {
clk_sys_udelay(I2C_POLL_MICROSECOND);
}
/* returns 0 if ok, meaning controller is disabled */
return (QM_SS_I2C_READ_ENABLE_STATUS(controller) &
QM_SS_I2C_ENABLE_STATUS_IC_EN);
}
int qm_ss_i2c_irq_transfer_terminate(const qm_ss_i2c_t i2c)
{
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
uint32_t controller = i2c_base[i2c];
/* Abort:
* In response to an ABORT, the controller issues a STOP and
* flushes
* the Tx FIFO after completing the current transfer, then sets
* the
* TX_ABORT interrupt after the abort operation. The ABORT bit
* is
* cleared automatically by hardware after the abort operation.
*/
QM_SS_I2C_ABORT(controller);
return 0;
}
#if (ENABLE_RESTORE_CONTEXT)
int qm_ss_i2c_save_context(const qm_ss_i2c_t i2c,
qm_ss_i2c_context_t *const ctx)
{
uint32_t controller = i2c_base[i2c];
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(ctx != NULL, -EINVAL);
ctx->i2c_fs_scl_cnt =
__builtin_arc_lr(controller + QM_SS_I2C_FS_SCL_CNT);
ctx->i2c_ss_scl_cnt =
__builtin_arc_lr(controller + QM_SS_I2C_SS_SCL_CNT);
ctx->i2c_con = __builtin_arc_lr(controller + QM_SS_I2C_CON);
return 0;
}
int qm_ss_i2c_restore_context(const qm_ss_i2c_t i2c,
const qm_ss_i2c_context_t *const ctx)
{
uint32_t controller = i2c_base[i2c];
QM_CHECK(i2c < QM_SS_I2C_NUM, -EINVAL);
QM_CHECK(ctx != NULL, -EINVAL);
__builtin_arc_sr(ctx->i2c_fs_scl_cnt,
controller + QM_SS_I2C_FS_SCL_CNT);
__builtin_arc_sr(ctx->i2c_ss_scl_cnt,
controller + QM_SS_I2C_SS_SCL_CNT);
__builtin_arc_sr(ctx->i2c_con, controller + QM_SS_I2C_CON);
return 0;
}
#else
int qm_ss_i2c_save_context(const qm_ss_i2c_t i2c,
qm_ss_i2c_context_t *const ctx)
{
(void)i2c;
(void)ctx;
return 0;
}
int qm_ss_i2c_restore_context(const qm_ss_i2c_t i2c,
const qm_ss_i2c_context_t *const ctx)
{
(void)i2c;
(void)ctx;
return 0;
}
#endif /* ENABLE_RESTORE_CONTEXT */