Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S5JS100 / i2c_api.c
/****************************************************************************
 *
 * Copyright 2020 Samsung Electronics All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/

#if DEVICE_I2C

#include "i2c_api.h"
#include "i2c_def.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
#include "mbed_wait_api.h"
/* States of a possibly combined I2C transfer */
typedef enum i2c_transfer_state_t {
    I2C_TRANSFER_SINGLE, /* Non combined transfer */
    I2C_TRANSFER_COMBINED_FIRST_MESSAGE, /*
                                          * First message of a
                                          * combined transfer
                                          */
    I2C_TRANSFER_COMBINED_INTERMEDIATE_MESSAGE, /*
                                                 * Message in the middle
                                                 * of a combined
                                                 * transfer
                                                 */
    I2C_TRANSFER_COMBINED_LAST_MESSAGE, /*
                                         * Last message of a combined
                                         * transfer
                                         */
} i2c_transfer_state_t;

/*
 * Driver private data structure that should not be shared by multiple
 * instances of the driver
 * (same driver for multiple instances of the IP)
 */
typedef struct private_i2c_t {
    /* State of a possibly combined ongoing i2c transfer */
    i2c_transfer_state_t transfer_state;
} private_i2c_t;

struct i2c_msg_s {
    uint16_t addr;              /**< Slave address */
    uint16_t flags;             /**< I2C flags; See I2C_M_* definitions */
#ifdef CONFIG_I2C_USERIO
    uint16_t length;            /**< The length of buffer */
    uint8_t *buffer;            /**< The Buffer for transferring message */
#else
    uint8_t *buffer;            /**< The Buffer for transferring message */
    int length;                 /**< The length of buffer */
#endif
};

#define S5JS100_DEFAULT_I2CXFER_CLOCK   (100 * 1000) // 100KHz
#define S5JS100_DEFAULT_I2C_TIMEOUT     10000
#define S5JS100_DEFAULT_I2CSLAVE_ADDR   0x69
#define HSI2C_INT_XFER_DONE (HSI2C_INT_XFER_DONE_NOACK_MANUAL | HSI2C_INT_XFER_DONE_MANUAL)

/*
 * Retrieve the private data of the instance related to a given IP
 */
#if 0
static private_i2c_t *get_i2c_private(i2c_t *obj)
{
    static private_i2c_t data0, data1;
    /*
     * Select which instance to give using the base
     * address of registers
     */
    switch ((intptr_t)obj->i2c) {
        case I2C0_BASE:
            return &data0;
        case I2C1_BASE:
            return &data1;
        default:
            error("i2c driver private data structure not found for this registers base address");
            return (void *)0;
    }
}
#endif

static const PinMap PinMap_I2C_SDA[] = {
    {I2C_SDA, I2C_0, 0},
    {NC, NC, 0}
};

static const PinMap PinMap_I2C_SCL[] = {
    {I2C_SCL, I2C_0, 0},
    {NC, NC, 0}
};
static void hsi2c_set_hs_timing(unsigned int base, unsigned int nClkDiv,
                                unsigned int tSTART_SU, unsigned int tSTART_HD,
                                unsigned int tSTOP_SU, unsigned int tSDA_SU,
                                unsigned int tDATA_SU, unsigned int tDATA_HD,
                                unsigned int tSCL_L, unsigned int tSCL_H,
                                unsigned int tSR_RELEASE)
{
    tSTART_SU &= 0xFF;
    tSTART_HD &= 0xFF;
    tSTOP_SU &= 0xFF;
    putreg32(((tSTART_SU << 24) | (tSTART_HD << 16) | (tSTOP_SU << 8)), base + I2C_TIMING_HS1);

    tDATA_SU &= 0xFF;
    tSCL_L &= 0xFF;
    tSCL_H &= 0xFF;
    putreg32(((tDATA_SU << 24) | (tSCL_L << 8) | (tSCL_H << 0)), base + I2C_TIMING_HS2);

    nClkDiv &= 0xFF;
    tSR_RELEASE &= 0xFF;
    putreg32(((nClkDiv << 16) | (tSR_RELEASE << 0)), base + I2C_TIMING_HS3);

    tDATA_HD &= 0xFFFF;
    putreg32(tDATA_HD, base + I2C_TIMING_SLA);

}

static void hsi2c_set_fs_timing(unsigned int base, unsigned int nClkDiv,
                                unsigned int tSTART_SU, unsigned int tSTART_HD,
                                unsigned int tSTOP_SU, unsigned int tDATA_SU,
                                unsigned int tDATA_HD, unsigned int tSCL_L,
                                unsigned int tSCL_H, unsigned int tSR_RELEASE)
{
    tSTART_SU &= 0xFF;
    tSTART_HD &= 0xFF;
    tSTOP_SU &= 0xFF;
    putreg32(((tSTART_SU << 24) | (tSTART_HD << 16) | (tSTOP_SU << 8)), base + I2C_TIMING_FS1);

    tDATA_SU &= 0xFF;
    tSCL_L &= 0xFF;
    tSCL_H &= 0xFF;
    putreg32(((tDATA_SU << 24) | (tSCL_L << 8) | (tSCL_H << 0)), base + I2C_TIMING_FS2);

    nClkDiv &= 0xFF;
    tSR_RELEASE &= 0xFF;
    putreg32(((nClkDiv << 16) | (tSR_RELEASE << 0)), base + I2C_TIMING_FS3);

    tDATA_HD &= 0xFFFF;
    putreg32(tDATA_HD, base + I2C_TIMING_SLA);
}

static void hsi2c_calculate_timing(unsigned int base, unsigned int nPclk,
                                   unsigned int nOpClk)
{
    unsigned int reg;
    unsigned int nClkDiv;
    uint32_t tFTL_CYCLE_SCL;
    int i;
    int uTemp0;
    int uTemp1;
    int uTemp2 = 0;

    reg = getreg32(base + I2C_CONF);
    reg &= ~(0x7 << 13);
    putreg32(reg, base + I2C_CONF);

    reg = getreg32(base + I2C_CONF);
    reg &= ~(0x7 << 16);
    putreg32(reg, base + I2C_CONF);

    tFTL_CYCLE_SCL = (getreg32(base + I2C_CONF) >> 16) & 0x7;

    uTemp0 = (float)(nPclk / nOpClk) - (tFTL_CYCLE_SCL + 3) * 2;

    for (i = 0; i < 256; i++) {
        uTemp1 = (uTemp0 + ((tFTL_CYCLE_SCL + 3) % (i + 1)) * 2) / (i + 1);
        if (uTemp1 < 512) {     /*  TSCL_L/H max is 512 / 2 */
            uTemp2 = uTemp1 - 2;
            break;
        }
    }

    unsigned int tSCL_H;
    nClkDiv = i;
    if (nOpClk > I2C_SPEED_400KHZ) {
        tSCL_H = ((uTemp2 + 10) / 3) - 5;
    } else {
        tSCL_H = uTemp2 / 2;
    }

    unsigned int tSCL_L = uTemp2 - tSCL_H;
    unsigned int tSTART_SU = tSCL_L;
    unsigned int tSTART_HD = tSCL_L;
    unsigned int tSTOP_SU = tSCL_L;
    unsigned int tSDA_SU = tSCL_L;
    unsigned int tDATA_SU = tSCL_L;
    unsigned int tDATA_HD = tSCL_L / 2;
    unsigned int tSR_RELEASE = uTemp2;

    if (nOpClk > I2C_SPEED_400KHZ) {
        /* 400Khz setting for Extended ID */
        hsi2c_set_fs_timing(base, 1, 38, 38, 38, 19, 19, 38, 38, 76);
        hsi2c_set_hs_timing(base, nClkDiv, tSTART_SU, tSTART_HD, tSTOP_SU,
                            tSDA_SU, tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE);
    } else {
        hsi2c_set_fs_timing(base, nClkDiv, tSTART_SU, tSTART_HD, tSTOP_SU,
                            tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE);
    }
}

static void hsi2c_conf(unsigned int base, unsigned int nOpClk)
{
    unsigned int val;

    val = getreg32(base + I2C_CONF);
    val &= ~(3 << 30);
    if (nOpClk > I2C_SPEED_400KHZ) {
        val |= (1 << 29);
    } else {
        val &= ~(1 << 29);
    }
    putreg32(val, base + I2C_CONF);
}

static void hsi2c_enable_int(unsigned int base, unsigned int bit)
{
    unsigned int val;
    val = getreg32(base + INT_EN);
    val |= bit;
    putreg32(val, base + INT_EN);
}

static void hsi2c_disable_int(unsigned int base, unsigned int bit)
{
    unsigned int val;

    val = getreg32(base + INT_EN);
    val &= ~bit;
    putreg32(val, base + INT_EN);
}

static int hsi2c_manual_fast_init(i2c_t *obj)
{
    unsigned int val;
    unsigned int base = obj->base;

    hsi2c_conf(base, obj->xfer_speed);
    hsi2c_calculate_timing(base, obj->clock, obj->xfer_speed);
    hsi2c_enable_int(base, HSI2C_INT_XFER_DONE_MANUAL | HSI2C_INT_XFER_DONE_NOACK_MANUAL);

    obj->initialized = 1;

    val = getreg32(base + I2C_CONF);
    val &= ~((1 << 31) | (1 << 29));
    putreg32(val, base + I2C_CONF);

    hsi2c_enable_int(base, HSI2C_INT_XFER_DONE);

    return 0;
}


static void hsi2c_set_slave_addr(unsigned int base, unsigned short addr,
                                 unsigned int is_master)
{
    unsigned int val;

    addr &= 0x3FF;

    val = getreg32(base + I2C_ADDR);

    if (is_master == 0) {
        val &= ~0x3ff;
        val |= addr;
    } else {
        val &= ~(0x3FF << 10);
        val |= (addr << 10);
    }
    putreg32(val, base + I2C_ADDR);
}

static int hsi2c_wait_xfer_done(i2c_t *obj)
{
    int val;
    int timeout = obj->timeout;
    unsigned int base = obj->base;

    while (timeout-- > 0) {
        val = getreg32(base + INT_STAT) & HSI2C_INT_XFER_DONE_MANUAL;
        if (val) {
            putreg32(val, base + INT_STAT);
            return (val == HSI2C_INT_XFER_DONE_MANUAL);
        }
    }

    return -1;
}

static void hsi2c_start(i2c_t *obj)
{
    putreg32(0x88, I2C0_BASE + CTL);

    putreg32(I2C_START, obj->base + I2C_MANUAL_CMD);

    hsi2c_wait_xfer_done(obj);
}

static void hsi2c_stop(i2c_t *obj)
{
    putreg32(I2C_STOP, obj->base + I2C_MANUAL_CMD);

    hsi2c_wait_xfer_done(obj);
}

static void hsi2c_repstart(i2c_t *obj)
{
    putreg32(I2C_RESTART, obj->base + I2C_MANUAL_CMD);
}

static int hsi2c_outb(i2c_t *obj, unsigned char data)
{
    unsigned int val;
    int ret;

    val = ((unsigned int)data) << 24 | I2C_SEND_DATA;
    putreg32(val, obj->base + I2C_MANUAL_CMD);

    ret = hsi2c_wait_xfer_done(obj);

    return ret;
}

static int hsi2c_inb(i2c_t *obj, unsigned char is_ack)
{
    unsigned int val = I2C_READ_DATA;
    unsigned char data;
    int ret;
    unsigned int base = obj->base;

    /* Looks awkward, but if I2C_RX_ACK is set, ACK is NOT generated */
    if (!is_ack) {
        val |= I2C_RX_ACK;
    }
    putreg32(val, base + I2C_MANUAL_CMD);

    ret = hsi2c_wait_xfer_done(obj);
    if (ret < 0) {
        return ret; /* timeout */
    }

    data = (getreg32(base + I2C_MANUAL_CMD) >> 16) & 0xff;

    return data;
}

static int sendbytes(i2c_t *obj, struct i2c_msg_s *msg)
{
    uint8_t *p = msg->buffer;
    int count = msg->length;
    int nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    int wrcount = 0, ret;

    while (count > 0) {
        ret = hsi2c_outb(obj, *p);
        if ((ret == 1) || ((ret == 0) && nak_ok)) {
            count--;
            p++;
            wrcount++;
        } else if (ret == 0) {
            /* NAK from the slave */
            return -EIO;
        } else {
            /* Timeout */
            return ret;
        }
    }

    return wrcount;
}

static int readbytes(i2c_t *obj, struct i2c_msg_s *msg)
{
    int val;
    int rdcount = 0;
    unsigned char *p = msg->buffer;
    int count = msg->length;

    while (count > 0) {
        val = hsi2c_inb(obj, (count > 1));
        if (val < 0) {
            break;
        }

        *p++ = val;
        rdcount++;
        count--;
    }

    return rdcount;
}

static int try_address(i2c_t *obj, unsigned char addr, int retries)
{
    int i, ret = 0;

    for (i = 0; i <= retries; i++) {
        ret = hsi2c_outb(obj, addr);
        if (ret == 1 || i == retries) {
            break;
        }
        hsi2c_stop(obj);
        wait_us(obj->timeout / 2);
        hsi2c_start(obj);
    }

    return ret;
}

static int do_address(i2c_t *obj, struct i2c_msg_s *msg)
{
    unsigned short flags = msg->flags;
    unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    unsigned char addr;
    int ret;
    int retries;

    retries = nak_ok ? 0 : obj->retries;

    if (flags & I2C_M_TEN) {
        /* a 10-bit address in manual mode */
        addr = 0xf0 | ((msg->addr >> 7) & 0x06);

        ret = try_address(obj, addr, retries);
        if ((ret != 1) && !nak_ok) {
            return -ENXIO;
        }

        /* the remaining 8 bit address */
        ret = hsi2c_outb(obj, msg->addr & 0xff);
        if ((ret != 1) && !nak_ok) {
            return -ENXIO;
        }

        if (flags & I2C_M_READ) {
            hsi2c_repstart(obj);
            hsi2c_wait_xfer_done(obj);
            addr |= 0x1;
            ret = try_address(obj, addr, retries);
            if ((ret != 1) && !nak_ok) {
                return -EIO;
            }
        }
    } else {
        /* 7-bit address */
        addr = msg->addr << 1;
        if (flags & I2C_M_READ) {
            addr |= 0x1;
        }

        ret = try_address(obj, addr, retries);
        if ((ret != 1) && !nak_ok) {
            return -ENXIO;
        }
    }

    return 0;
}
static void hsi2c_set_auto_config(unsigned int base, unsigned int stop,
                                  unsigned int tx, unsigned int len)
{
    unsigned int val = getreg32(base + I2C_AUTO_CONF);

    if (stop) {
        val |= (1 << 17);
    } else {
        val &= ~(1 << 17);
    }
    if (tx) {
        val &= ~(1 << 16);
    } else {
        val |= (1 << 16);
    }

    val &= ~0xFFFF;
    val |= len;
    putreg32(val, base + I2C_AUTO_CONF);
}

static void hsi2c_set_trans_mode(unsigned int base, unsigned int master,
                                 unsigned int tx)
{
    unsigned int val = getreg32(base + CTL);

    val |= (1 << 0); /* ctrl 0 bit write 1 */

    if (master) {
        val |= (1 << 3);
    } else {
        val &= ~(1 << 3);
    }

    val &= ~(3 << 6);
    if (tx) {
        val |= (1 << 7);
    } else {
        val |= (1 << 6);
    }

    putreg32(val, base + CTL);
}

static void hsi2c_set_hwacg_mode(unsigned int base, unsigned int slave)
{
    unsigned int val = getreg32(base + CTL);

    val &= ~(0x3 << 24);

    if (slave) {
        val |= (0x1 << 24);
    } else {
        val &= ~(0x1 << 24);
    }

    putreg32(val, base + CTL);
}

static void hsi2c_set_fifo_level(unsigned int base)
{
    putreg32(0x10013, base + FIFO_CTL);
}


static void hsi2c_master_setup(i2c_t *obj, unsigned int mode,
                               unsigned int speed, unsigned int slave_addr)
{
    if (obj->mode == I2C_POLLING) {
        obj->xfer_speed = speed;
        hsi2c_calculate_timing(obj->base, obj->clock, speed);
    }
#ifdef CONFIG_S5JS100_I2C_INTERRUPT_MODE
    else if (priv->mode == I2C_INTERRUPT) {
        priv->master_test_data = (struct master_data *)malloc(sizeof(struct master_data));
        /* complete_init(&priv->master_test_data->done); */
        hsi2c_set_trans_mode(obj->base, 1, 1);  /* set master mode */
        hsi2c_conf(obj->base, speed);
        hsi2c_calculate_timing(obj->base, obj->clock, speed);
        hsi2c_set_slave_addr(obj->base, slave_addr, 1);
        hsi2c_set_auto_mode(obj->base);
        hsi2c_set_fifo_level(obj->base);
    }
#endif
}

static void hsi2c_slave_setup(i2c_t *obj, unsigned int mode,
                              unsigned int speed, unsigned int slave_addr)
{
//  obj->i2c->slave_test_data = (struct slave_data *)malloc(sizeof(struct slave_data));

    /* slave mode is only support slave mode */
    hsi2c_set_trans_mode(obj->base, 0, 0);  /* set slave mode */

    /*set hwacg for slave mode */
    hsi2c_set_hwacg_mode(obj->base, 1);

    hsi2c_conf(obj->base, speed);
    hsi2c_calculate_timing(obj->base, obj->clock, speed);
    hsi2c_set_slave_addr(obj->base, slave_addr, 0);

    hsi2c_disable_int(obj->base, HSI2C_INT_ALL);
    hsi2c_enable_int(obj->base, HSI2C_INT_SLAVE_ADDR_MATCH | HSI2C_INT_RX_ALMOST_FULL);

    hsi2c_set_fifo_level(obj->base);
    hsi2c_set_auto_config(obj->base, 0, 0, 0);

    hsi2c_set_trans_mode(obj->base, 0, 0);

#ifdef CONFIG_S5JS100_I2C_INTERRUPT_MODE
    if ((priv->master == I2C_SLAVE_MODE) || (priv->mode == I2C_INTERRUPT)) {
        irq_attach(priv->config->irq, priv->config->isr, NULL);
    }
#endif
}

static int hsi2c_setup(i2c_t *obj, unsigned int master,
                       unsigned int mode, unsigned int speed, unsigned int slave_addr)
{
    obj->master = master;
    obj->mode = mode;
    obj->xfer_speed = speed;
    obj->slave_addr = slave_addr;

    hsi2c_manual_fast_init(obj);

    if (master == I2C_MASTER) {
        hsi2c_master_setup(obj, mode, speed, slave_addr);
    } else if (master == I2C_SLAVE_MODE) {
        hsi2c_slave_setup(obj, mode, speed, slave_addr);
    }

    return 0;
}

unsigned int i2c_setclock(i2c_t *obj, unsigned int frequency)
{
    /* Has the I2C bus frequency changed? */
    if (frequency != obj->xfer_speed) {
        /*
         * Calculate the clock divider that results in the highest frequency
         * that is than or equal to the desired speed.
         */
        if (obj->mode != I2C_POLLING) {
            hsi2c_conf(obj->base, frequency);
        }

        hsi2c_calculate_timing(obj->base, obj->clock, frequency);
    }

    /* Save the new I2C frequency */
    obj->xfer_speed = frequency;
    return 0;
}



static void clear_isr(i2c_t *obj)
{
    /*
     * Writing to the IRQ status register clears set bits. Therefore, to
     * clear indiscriminately, just read the register and write it back.
     */
    uint32_t reg = obj->i2c->IRQ_STATUS;
    obj->i2c->IRQ_STATUS = reg;
}

void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
    /* Determine the I2C to use */
    I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
    I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
    obj->i2c = (I2C_TypeDef *)pinmap_merge(i2c_sda, i2c_scl);

    if ((int)obj->i2c == NC) {
        error("I2C pin mapping failed");
    }

    pinmap_pinout(sda, PinMap_I2C_SDA);
    pinmap_pinout(scl, PinMap_I2C_SCL);

    /*
     * Default configuration:
     * - MS    : Master mode
     * - NEA   : Normal (7-bit) addressing
     * - ACKEN : Send ACKs when reading from slave
     * - CLR_FIFO : Not a configuration bit => clears the FIFO
     */
    uint32_t reg = I2C_CTRL_MS | \
                   I2C_CTRL_NEA | \
                   I2C_CTRL_ACKEN | \
                   I2C_CTRL_CLR_FIFO;

    obj->i2c->CONTROL = reg;
    obj->base = I2C0_BASE;
    obj->retries = 0;
    obj->timeout = S5JS100_DEFAULT_I2C_TIMEOUT;
    obj->master = I2C_MASTER;
    obj->initialized = 0;
    obj->clock = cal_clk_getrate(d1_acpu_l3);
    obj->slave_addr = S5JS100_DEFAULT_I2CSLAVE_ADDR;
    obj->mode = I2C_POLLING;
    obj->addrlen = 7;

    hsi2c_setup(obj, obj->master, obj->mode, obj->xfer_speed, obj->slave_addr);

    i2c_setclock(obj, S5JS100_DEFAULT_I2CXFER_CLOCK);
    //get_i2c_private(obj)->transfer_state = I2C_TRANSFER_SINGLE;

    //i2c_frequency(obj, 100000); /* Default to 100kHz SCL frequency */
}

int i2c_start(i2c_t *obj)
{
    return 0;
}

int i2c_stop(i2c_t *obj)
{
    /* Clear the hardware FIFO */
    obj->i2c->CONTROL |= I2C_CTRL_CLR_FIFO;
    /* Clear the HOLD bit used for performing combined transfers */
    obj->i2c->CONTROL &= ~I2C_CTRL_HOLD;
    /* Reset the transfer size (read and write) */
    obj->i2c->TRANSFER_SIZE = 0;
    /* Clear interrupts */
    clear_isr(obj);
    return 0;
}

void i2c_frequency(i2c_t *obj, int hz)
{
    /*
     * Divider is split in two halfs : A and B
     * A is 2 bits wide and B is 6 bits wide
     * The Fscl frequency (SCL clock) is calculated with the following
     * equation:
     * Fscl=SystemCoreClock/(22*(A+1)*(B+1))
     * Here, we only calculate the B divisor which already enables a
     * wide enough range of values
     */
    unsigned int frequency = hz;

    if (frequency != obj->xfer_speed) {
        if (obj->mode != I2C_POLLING) {
            hsi2c_conf(obj->base, frequency);
        }
        hsi2c_calculate_timing(obj->base, obj->clock, frequency);
    }
}

int i2c_transfer(i2c_t *obj, struct i2c_msg_s *msgv, int msgc)
{
    struct i2c_msg_s *pmsg;
    int ret = 0;
    int i;
    int nak_ok;
    int start = 1;
    int stop = 1;
    //unsigned int base = obj->base;

    /* Ensure that address or flags don't change meanwhile */
    //s5js100_i2c_sem_wait(priv);

    /* TODO: initialization */
    if (start) {
        hsi2c_start(obj);
    }

    /*
    if (obj->xfer_speed > I2C_SPEED_400KHZ) {
        hsi2c_conf(base, I2C_SPEED_400KHZ);
        putreg32((0xF << 24 | I2C_SEND_DATA), base + I2C_MANUAL_CMD);
        hsi2c_wait_xfer_noack(priv);

        hsi2c_conf(base, priv->xfer_speed);
        hsi2c_repstart(priv);
        hsi2c_wait_xfer_noack(priv);
    }
    */

    for (i = 0; i < msgc; i++) {
        pmsg = &msgv[i];
        nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
        if (!(pmsg->flags & I2C_M_NOSTART)) {
            if ((i > 0) || (start == 0)) {
                hsi2c_repstart(obj);
                hsi2c_wait_xfer_done(obj);
            }
            ret = do_address(obj, pmsg);
            if ((ret != 0) && !nak_ok) {
                goto fail;
            }
        }
        if (pmsg->flags & I2C_M_READ) {
            /* read bytes into buffer */
            ret = readbytes(obj, pmsg);
            if (ret < pmsg->length) {
                if (ret >= 0) {
                    return -EIO;
                }
                goto fail;
            }
        } else {
            /* write bytes from buffer */
            ret = sendbytes(obj, pmsg);
            if (ret < pmsg->length) {
                if (ret >= 0) {
                    ret = -EIO;
                }
                goto fail;
            }
        }
    }

fail:
    if (stop) {
        hsi2c_stop(obj);
    }

    /* Ensure that address or flags don't change meanwhile */
    //s5js100_i2c_sem_post(priv);

    return ret;
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{

    struct i2c_msg_s xfer[2];

    xfer[0].addr = (unsigned int)address;
    xfer[0].flags = 0;
    xfer[0].buffer = (unsigned char *)&data[0];
    xfer[0].length = 1;

    xfer[1].addr = (unsigned int)address;
    xfer[1].flags = I2C_M_READ;
    xfer[1].buffer = (unsigned char *)&data[1];
    xfer[1].length = length;
    wait_us(100);

    return i2c_transfer(obj, xfer, 2);
}

int i2c_write(i2c_t *obj, int address, const char *data, int length,
              int stop)
{

    struct i2c_msg_s msg;

    msg.addr = address;
    msg.flags = (obj->addrlen == 10) ? I2C_M_TEN : 0;
    msg.buffer = (unsigned char *)data;
    msg.length = length;

    return i2c_transfer(obj, &msg, 1);
}

void i2c_reset(i2c_t *obj)
{
    i2c_stop(obj);
}

int i2c_byte_read(i2c_t *obj, int last)
{
    char i2c_ret = 0;
    i2c_read(obj, obj->last_xfer_address, &i2c_ret, 1, last);
    return i2c_ret;
}

int i2c_byte_write(i2c_t *obj, int data)
{
    /* Store the number of written bytes */
    uint32_t wb = i2c_write(obj, obj->last_xfer_address, (char *)&data, 1, 0);
    if (wb == 1) {
        return 1;
    } else {
        return 0;
    }
}

void i2c_slave_mode(i2c_t *obj, int enable_slave)
{
}

int i2c_slave_receive(i2c_t *obj)
{
    return 0;
}

int i2c_slave_read(i2c_t *obj, char *data, int length)
{
    return 0;
}

int i2c_slave_write(i2c_t *obj, const char *data, int length)
{
    return 0;
}

void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
{
}

#endif // DEVICE_I2C