/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2011 by Amaury Pouly
 * 
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "system.h"
#include "kernel.h"
#include "dma-imx233.h"
#include "i2c-imx233.h"
#include "pinctrl-imx233.h"
#include "string.h"

#include "regs/i2c.h"

/**
 * Driver Architecture:
 * The driver has two interfaces: the good'n'old i2c_* api and a more
 * advanced one specific to the imx233 dma architecture. The i2c_* api is
 * implemented with the imx233_i2c_* one.
 * Since each i2c transfer must be split into several dma transfers and we
 * cannot do dynamic allocation, we allow for at most I2C_NR_STAGES stages.
 * A typical read memory transfer will require 3 stages thus 4 is safe:
 * - one with start, device address and memory address
 * - one with repeated start and device address
 * - one with data read and stop
 * To make the interface easier to use and to handle the DMA/cache related
 * issues, all the data transfers are done in a statically allocated buffer
 * which is managed by the driver. The driver will ensure that all transfers
 * are cache aligned and will copy back the data to user buffers at the end.
 * The I2C_BUFFER_SIZE define controls the size of the buffer. All transfers
 * should probably fit within 512 bytes.
 *
 * On top of this, transfers are queued using the 'next' field of imx233_i2c_xfer_t.
 * Each time a transfer is programmed, it is translated to dma transfers using
 * the dma API.
 */

/**
 * Internal DMA API to build the transfer.
 * NOTE the api does not perform any locking, it is up to the caller to ensure
 * that there only one transfer beint built at any time.
 */

/* start building a transfer */
static void imx233_i2c_begin(void);
/* add a stage
 * NOTE for transmit, the data is copied to a buffer so the buffer can be freed
 * afer this function return. For receive, buffer must exists until transfer is
 * complete. This function assumes any receive transfer is final (master will
 * send NAK). */
static void imx233_i2c_add(bool start, bool transmit, void *buffer, unsigned size, bool stop);
/* end building a transfer and start the transfer */
static void imx233_i2c_kick(void);
/* abort transfer (will call imx233_i2c_transfer_complete) */
static void imx233_i2c_abort(void);
/* set speed */
static void imx233_i2c_set_speed(bool fast_mode);
/* callback function when transfer is finished */
static void imx233_i2c_transfer_complete(enum imx233_i2c_error_t status);

/**
 * Advanced API
 */

/* NOTE these variables are not marked as volatile because all functions
 * do all operation with IRQ disabled, so they won't change their value
 * in the middle of a function */
static struct imx233_i2c_xfer_t *i2c_head; /* pointer to the current transfer */
static struct imx233_i2c_xfer_t *i2c_tail; /* pointer to the last transfer */
static struct timeout i2c_tmo; /* timeout */

/* timeout callback */
static int imx233_i2c_timeout(struct timeout *tmo);

/* called in IRQ context or with IRQ disabled */
static void imx233_i2c_start(void)
{
    uint8_t addr_wr = i2c_head->dev_addr;
    uint8_t addr_rd = i2c_head->dev_addr | 1;
    /* translate transfer using DMA API */
    imx233_i2c_set_speed(i2c_head->fast_mode);
    imx233_i2c_begin();
    if(i2c_head->mode == I2C_WRITE)
    {
        /* START + addr */
        imx233_i2c_add(true, true, &addr_wr, 1, false);
        /* data + stop if no second stage */
        imx233_i2c_add(false, true, i2c_head->data[0], i2c_head->count[0], i2c_head->count[1] == 0);
        /* (if second stage) data + stop */
        if(i2c_head->count[1] > 0)
            imx233_i2c_add(false, true, i2c_head->data[1], i2c_head->count[1], true);
    }
    else /* I2C_READ */
    {
        /* (if write stage) */
        if(i2c_head->count[0] > 0)
        {
            /* START + addr */
            imx233_i2c_add(true, true, &addr_wr, 1, false);
            /* data */
            imx233_i2c_add(false, true, i2c_head->data[0], i2c_head->count[0], false);
        }
        /* START + addr */
        imx233_i2c_add(true, true, &addr_rd, 1, false);
        /* read data + stop */
        imx233_i2c_add(false, false, i2c_head->data[1], i2c_head->count[1], true);
    }
    /* kick transfer */
    imx233_i2c_kick();
    /* setup timer for timeout */
    if(i2c_head->tmo_ms > 0)
        timeout_register(&i2c_tmo, imx233_i2c_timeout, i2c_head->tmo_ms * HZ / 1000, 0);
}

/* unqueue head and notify completion, called with IRQ disabled */
static void imx233_i2c_unqueue_head(enum imx233_i2c_error_t status)
{
    /* notify */
    if(i2c_head->callback)
        i2c_head->callback(i2c_head, status);
    /* unqueue */
    i2c_head = i2c_head->next;
}

/* callback function when transfer is finished, called with IRQ disabled */
static void imx233_i2c_transfer_complete(enum imx233_i2c_error_t status)
{
    /* cancel timeout
     * NOTE because IRQ are disabled, the timeout callback will not be called
     * until we enable them back, at which point we will have disabled the timeout
     * so the completion routine will not be called twice. */
    if(i2c_head->tmo_ms > 0)
        timeout_cancel(&i2c_tmo);
    /* notify completion and unqueue
     * WARNING completion callback can queue other transfers, so the only part
     * of the queue that cannot change is this transaction, everything else can
     * change */
    struct imx233_i2c_xfer_t *this_xfer = i2c_head;
    struct imx233_i2c_xfer_t *last_xfer = i2c_head->last; /* in transaction */
    /* unqueue head */
    imx233_i2c_unqueue_head(status);
    /* in case of failure, skip others */
    if(status != I2C_SUCCESS && this_xfer != last_xfer)
        while(i2c_head != last_xfer)
                imx233_i2c_unqueue_head(I2C_SKIP);
    /* if there is anything left, start it */
    if(i2c_head)
        imx233_i2c_start();
}

static int imx233_i2c_timeout(struct timeout *tmo)
{
    (void) tmo;
    imx233_i2c_abort();
    return 0; /* do not fire again */
}

void imx233_i2c_transfer(struct imx233_i2c_xfer_t *xfer)
{
    /* avoid any race with the irq handler */
    unsigned long cpsr = disable_irq_save();
    /* before queuing, update link to last transfer in each transfer */
    struct imx233_i2c_xfer_t *last = xfer;
    while(last->next)
        last = last->next;
    struct imx233_i2c_xfer_t *tmp = xfer;
    while(tmp)
    {
        tmp->last = last;
        tmp = tmp->next;
    }
    /* no transfer pending: start one */
    if(i2c_head == NULL)
    {
        i2c_head = xfer;
        i2c_tail = last;
        /* kick transfer now */
        imx233_i2c_start();
    }
    /* pending transer: queue and let the irq handler process it for us */
    else
    {
        i2c_tail->next = xfer;
        i2c_tail = last;
    }

    restore_irq(cpsr);
}

/**
 * DMA API implementation
 */

/* Used for DMA */
struct i2c_dma_command_t
{
    struct apb_dma_command_t dma;
    /* PIO words */
    uint32_t ctrl0;
    /* copy buffer pointers */
    void *src;
    void *dst;
    /* padded to next multiple of cache line size (32 bytes) */
    uint32_t pad[2];
} __attribute__((packed)) CACHEALIGN_ATTR;

__ENSURE_STRUCT_CACHE_FRIENDLY(struct i2c_dma_command_t)

#define I2C_NR_STAGES   4
#define I2C_BUFFER_SIZE 512
/* Current transfer */
static int i2c_nr_stages;
static struct i2c_dma_command_t i2c_stage[I2C_NR_STAGES];
static uint8_t i2c_buffer[I2C_BUFFER_SIZE] CACHEALIGN_ATTR;
static uint32_t i2c_buffer_end; /* current end */

static void imx233_i2c_reset(void)
{
    /* clear softreset */
    imx233_reset_block(&HW_I2C_CTRL0);
    /* Errata (imx233):
     * When RETAIN_CLOCK is set, the ninth clock pulse (ACK) is not generated. However, the SDA
     * line is read at the proper timing interval. If RETAIN_CLOCK is cleared, the ninth clock pulse is
     * generated.
     * HW_I2C_CTRL1[ACK_MODE] has default value of 0. It should be set to 1 to enable the fix for
     * this issue.
     */
#if IMX233_SUBTARGET >= 3780
    BF_SET(I2C_CTRL1, ACK_MODE);
#endif
    BF_SET(I2C_CTRL0, CLKGATE);
}

void imx233_i2c_init(void)
{
    BF_SET(I2C_CTRL0, SFTRST);
    /* setup pins (must be done when shutdown) */
    imx233_pinctrl_setup_vpin(VPIN_I2C_SCL, "i2c scl", PINCTRL_DRIVE_4mA, true);
    imx233_pinctrl_setup_vpin(VPIN_I2C_SDA, "i2c sda", PINCTRL_DRIVE_4mA, true);
    imx233_i2c_reset();
    i2c_head = i2c_tail = NULL;
}

static void imx233_i2c_begin(void)
{
    /* wakeup */
    BF_CLR(I2C_CTRL0, CLKGATE);
    i2c_nr_stages = 0;
    i2c_buffer_end = 0;
}

static void imx233_i2c_add(bool start, bool transmit,
    void *buffer, unsigned size, bool stop)
{
    if(i2c_nr_stages == I2C_NR_STAGES)
        panicf("i2c: too many stages");
    /* align buffer end on cache boundary */
    uint32_t start_off = CACHEALIGN_UP(i2c_buffer_end);
    uint32_t end_off = start_off + size;
    if(end_off > I2C_BUFFER_SIZE)
        panicf("i2c: transfer is too big");
    i2c_buffer_end = end_off;
    if(transmit)
    {
        /* copy data to buffer */
        memcpy(i2c_buffer + start_off, buffer, size);
    }
    else
    {
        /* record pointers for finalization */
        i2c_stage[i2c_nr_stages].src = i2c_buffer + start_off;
        i2c_stage[i2c_nr_stages].dst = buffer;
    }

    if(i2c_nr_stages > 0)
    {
        i2c_stage[i2c_nr_stages - 1].dma.next = &i2c_stage[i2c_nr_stages].dma;
        i2c_stage[i2c_nr_stages - 1].dma.cmd |= BM_APB_CHx_CMD_CHAIN;
        if(!start)
            i2c_stage[i2c_nr_stages - 1].ctrl0 |= BM_I2C_CTRL0_RETAIN_CLOCK;
    }
    i2c_stage[i2c_nr_stages].dma.buffer = i2c_buffer + start_off;
    i2c_stage[i2c_nr_stages].dma.next = NULL;
    i2c_stage[i2c_nr_stages].dma.cmd = BF_OR(APB_CHx_CMD,
        COMMAND(transmit ? BV_APB_CHx_CMD_COMMAND__READ : BV_APB_CHx_CMD_COMMAND__WRITE),
        WAIT4ENDCMD(1), CMDWORDS(1), XFER_COUNT(size));
    /* assume that any read is final (send nak on last) */
    i2c_stage[i2c_nr_stages].ctrl0 = BF_OR(I2C_CTRL0,
        XFER_COUNT(size), DIRECTION(transmit), SEND_NAK_ON_LAST(!transmit),
        PRE_SEND_START(start), POST_SEND_STOP(stop), MASTER_MODE(1));
    i2c_nr_stages++;
}

static enum imx233_i2c_error_t imx233_i2c_finalize(void)
{
    discard_dcache_range(i2c_buffer, I2C_BUFFER_SIZE);
    for(int i = 0; i < i2c_nr_stages; i++)
    {
        struct i2c_dma_command_t *c = &i2c_stage[i];
        if(BF_RDX(c->dma.cmd, APB_CHx_CMD, COMMAND) == BV_APB_CHx_CMD_COMMAND__WRITE)
            memcpy(c->dst, c->src, BF_RDX(c->dma.cmd, APB_CHx_CMD, XFER_COUNT));
    }
    return I2C_SUCCESS;
}

static void imx233_i2c_kick(void)
{
    if(i2c_nr_stages == 0)
        panicf("i2c: empty kick");
    i2c_stage[i2c_nr_stages - 1].dma.cmd |= BM_APB_CHx_CMD_SEMAPHORE | BM_APB_CHx_CMD_IRQONCMPLT;

    BF_CLR(I2C_CTRL1, SLAVE_IRQ, SLAVE_STOP_IRQ, MASTER_LOSS_IRQ, EARLY_TERM_IRQ,
        OVERSIZE_XFER_TERM_IRQ, NO_SLAVE_ACK_IRQ, DATA_ENGINE_CMPLT_IRQ, BUS_FREE_IRQ);
    imx233_dma_reset_channel(APB_I2C);
    imx233_icoll_enable_interrupt(INT_SRC_I2C_DMA, true);
    imx233_icoll_enable_interrupt(INT_SRC_I2C_ERROR, true);
    imx233_dma_enable_channel_interrupt(APB_I2C, true);
    imx233_dma_start_command(APB_I2C, &i2c_stage[0].dma);
}

static void imx233_i2c_abort(void)
{
    /* FIXME there is a race condition here: if dma irq fires right before we
     * reset the channel, it will most likely trigger an IRQ anyway. It is
     * extremely unlikely but ideally, we should check this in the IRQ handler
     * with an id/counter. */
    imx233_dma_reset_channel(APB_I2C);
    imx233_i2c_reset();
    imx233_i2c_transfer_complete(I2C_TIMEOUT);
}

static enum imx233_i2c_error_t imx233_i2c_end(void)
{
    enum imx233_i2c_error_t ret;
    /* check for various errors */
    if(BF_RD(I2C_CTRL1, MASTER_LOSS_IRQ))
        ret = I2C_MASTER_LOSS;
    else if(BF_RD(I2C_CTRL1, NO_SLAVE_ACK_IRQ))
    {
        /* the core doesn't like this error, this is a workaround to prevent lock up */
#if IMX233_SUBTARGET >= 3780
        BF_SET(I2C_CTRL1, CLR_GOT_A_NAK);
#endif
        imx233_dma_reset_channel(APB_I2C);
        imx233_i2c_reset();
        ret = I2C_NO_SLAVE_ACK;
    }
    else if(BF_RD(I2C_CTRL1, EARLY_TERM_IRQ))
        ret = I2C_SLAVE_NAK;
    else
        ret = imx233_i2c_finalize();
    /* sleep */
    BF_SET(I2C_CTRL0, CLKGATE);
    return ret;
}

static void imx233_i2c_set_speed(bool fast_mode)
{
    /* See I2C specification for standard- and fast-mode timings
     * Clock is derived APBX which we assume to be running at 24 MHz. */
    if(fast_mode)
    {
        /* Fast-mode @ 400 kHz */
        HW_I2C_TIMING0 = 0x000f0007; /* HIGH_COUNT=0.6us, RCV_COUNT=0.2us */
        HW_I2C_TIMING1 = 0x001f000f; /* LOW_COUNT=1.3us, XMIT_COUNT=0.6us */
        HW_I2C_TIMING2 = 0x0015000d; /* BUS_FREE=0.9us LEADIN_COUNT=0.55us */
    }
    else
    {
        /* Standard-mode @ 100 kHz */
        HW_I2C_TIMING0 = 0x00780030; /* HIGH_COUNT=5us, RCV_COUNT=2us */
        HW_I2C_TIMING1 = 0x00800030; /* LOW_COUNT=5.3us, XMIT_COUNT=2us */
        HW_I2C_TIMING2 = 0x00300030; /* BUS_FREE=2us LEADIN_COUNT=2us */
    }
}

static void imx233_i2c_irq(bool err)
{
    if(err)
        panicf("i2c: dma error");
    /* reset dma channel on error */
    if(imx233_dma_is_channel_error_irq(APB_I2C))
        imx233_dma_reset_channel(APB_I2C);
    /* clear irq flags */
    imx233_dma_clear_channel_interrupt(APB_I2C);
    /* handle completion */
    imx233_i2c_transfer_complete(imx233_i2c_end());
}

void INT_I2C_DMA(void)
{
    imx233_i2c_irq(false);
}

void INT_I2C_ERROR(void)
{
    imx233_i2c_irq(true);
}

/** Public API */

void i2c_init(void)
{
}

struct imx233_i2c_sync_xfer_t
{
    struct imx233_i2c_xfer_t xfer;
    struct semaphore sema;
    volatile enum imx233_i2c_error_t status;
};

/* synchronous callback: record status and release semaphore */
static void i2c_sync_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
{
    struct imx233_i2c_sync_xfer_t *sxfer = (void *)xfer;
    sxfer->status = status;
    semaphore_release(&sxfer->sema);
}

static int i2c_sync_transfer(struct imx233_i2c_sync_xfer_t *xfer)
{
    semaphore_init(&xfer->sema, 1, 0);
    /* common init */
    xfer->xfer.next = NULL;
    xfer->xfer.callback = &i2c_sync_callback;
    xfer->xfer.fast_mode = true;
    xfer->xfer.tmo_ms = 1000;
    /* kick */
    imx233_i2c_transfer(&xfer->xfer);
    /* wait */
    semaphore_wait(&xfer->sema, TIMEOUT_BLOCK);
    return (int)xfer->status;
}

int i2c_write(int device, const unsigned char* buf, int count)
{
    struct imx233_i2c_sync_xfer_t xfer;
    xfer.xfer.dev_addr = device;
    xfer.xfer.mode = I2C_WRITE;
    xfer.xfer.count[0] = count;
    xfer.xfer.data[0] = (void *)buf;
    xfer.xfer.count[1] = 0;
    return i2c_sync_transfer(&xfer);
}

int i2c_read(int device, unsigned char* buf, int count)
{
    struct imx233_i2c_sync_xfer_t xfer;
    xfer.xfer.dev_addr = device;
    xfer.xfer.mode = I2C_READ;
    xfer.xfer.count[0] = 0;
    xfer.xfer.count[1] = count;
    xfer.xfer.data[1] = buf;
    return i2c_sync_transfer(&xfer);
}

int i2c_readmem(int device, int address, unsigned char* buf, int count)
{
    uint8_t addr = address; /* assume 1 byte */
    struct imx233_i2c_sync_xfer_t xfer;
    xfer.xfer.dev_addr = device;
    xfer.xfer.mode = I2C_READ;
    xfer.xfer.count[0] = 1;
    xfer.xfer.data[0] = &addr;
    xfer.xfer.count[1] = count;
    xfer.xfer.data[1] = buf;
    return i2c_sync_transfer(&xfer);
}

int i2c_writemem(int device, int address, const unsigned char* buf, int count)
{
    uint8_t addr = address; /* assume 1 byte */
    struct imx233_i2c_sync_xfer_t xfer;
    xfer.xfer.dev_addr = device;
    xfer.xfer.mode = I2C_WRITE;
    xfer.xfer.count[0] = 1;
    xfer.xfer.data[0] = &addr;
    xfer.xfer.count[1] = count;
    xfer.xfer.data[1] = (void *)buf;
    return i2c_sync_transfer(&xfer);
}
