/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (c) 2008 by Michael Sevakis
 *
 * 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 "system.h"
#include "cpu.h"
#define DEFINE_MC13783_VECTOR_TABLE
#include "mc13783-target.h"
#include "gpio-target.h"
#include "debug.h"
#include "kernel.h"

/* Extend the basic SPI transfer descriptor with our own fields */
struct mc13783_transfer_desc
{
    struct spi_transfer_desc xfer;
    union
    {
        /* Pick _either_ data or semaphore */
        struct semaphore sema;
        uint32_t data[4];
    };
};

static uint32_t pmic_int_enb[2];                  /* Enabled ints */
static uint32_t pmic_int_sense_enb[2];            /* Enabled sense reading */
static struct mc13783_transfer_desc int_xfers[2]; /* ISR transfer descriptor */
static const struct mc13783_event *current_event; /* Current event in callback */
static bool int_restore;                          /* Prevent SPI callback from
                                                     unmasking GPIO interrupt
                                                     (lockout) */

static const struct mc13783_event * event_from_id(enum mc13783_int_ids id)
{
    for (unsigned int i = 0; i < mc13783_event_vector_tbl_len; i++)
    {
        if (mc13783_event_vector_tbl[i].id == id)
            return &mc13783_event_vector_tbl[i];
    }

    return NULL;
}

/* Called when a transfer is finished and data is ready/written */
static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer)
{
    semaphore_release(&((struct mc13783_transfer_desc *)xfer)->sema);
}

static inline bool wait_for_transfer_complete(struct mc13783_transfer_desc *xfer)
{
    return semaphore_wait(&xfer->sema, TIMEOUT_BLOCK)
            == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0;
}

static inline bool mc13783_transfer(struct spi_transfer_desc *xfer,
                                    uint32_t *txbuf,
                                    uint32_t *rxbuf,
                                    int count,
                                    spi_transfer_cb_fn_type callback)
{
    xfer->node = &mc13783_spi;
    xfer->txbuf = txbuf;
    xfer->rxbuf = rxbuf;
    xfer->count = count;
    xfer->callback = callback;
    xfer->next = NULL;

    return spi_transfer(xfer);
}

static inline void sync_transfer_init(struct mc13783_transfer_desc *xfer)
{
    semaphore_init(&xfer->sema, 1, 0);
}

static inline bool mc13783_sync_transfer(struct mc13783_transfer_desc *xfer,
                                         uint32_t *txbuf,
                                         uint32_t *rxbuf,
                                         int count)
{
    sync_transfer_init(xfer);
    return mc13783_transfer(&xfer->xfer, txbuf, rxbuf, count, mc13783_xfer_complete_cb);
}

/* Efficient interrupt status and acking */
static void mc13783_int_svc_complete_callback(struct spi_transfer_desc *xfer)
{
    struct mc13783_transfer_desc *desc1 = (struct mc13783_transfer_desc *)xfer;
    uint32_t pnd0 = desc1->data[0], pnd1 = desc1->data[1];

    /* Restore PMIC interrupt events */
    if (int_restore)
        gpio_int_enable(MC13783_EVENT_ID);

    /* Call handlers */
    const struct mc13783_event *event = mc13783_event_vector_tbl;
    while (pnd0 | pnd1)
    {
        uint32_t id = event->id;
        uint32_t set = id / 32;
        uint32_t bit = 1 << (id % 32);

        uint32_t pnd = set == 0 ? pnd0 : pnd1;
        if (pnd & bit)
        {
            current_event = event;
            event->callback();
            set == 0 ? (pnd0 &= ~bit) : (pnd1 &= ~bit);
        }

        event++;
    }
}

static void mc13783_int_svc_callback(struct spi_transfer_desc *xfer)
{
    /* Only clear interrupts with handlers */
    struct mc13783_transfer_desc *desc0 = (struct mc13783_transfer_desc *)xfer;
    struct mc13783_transfer_desc *desc1 = &int_xfers[1];

    uint32_t pnd0 = desc0->data[0] & pmic_int_enb[0];
    uint32_t pnd1 = desc0->data[1] & pmic_int_enb[1];

    desc1->data[0] = pnd0;
    desc1->data[1] = pnd1;

    /* Setup the write packets with status(es) to clear */
    desc0->data[0] = (1 << 31) | (MC13783_INTERRUPT_STATUS0 << 25) | pnd0;
    desc0->data[1] = (1 << 31) | (MC13783_INTERRUPT_STATUS1 << 25) | pnd1;

    /* Only read sense if any pending interrupts have them enabled */
    if ((pnd0 & pmic_int_sense_enb[0]) || (pnd1 & pmic_int_sense_enb[1]))
    {
        desc0->data[2] = MC13783_INTERRUPT_SENSE0 << 25;
        desc0->data[3] = MC13783_INTERRUPT_SENSE1 << 25;
        desc1->xfer.rxbuf = desc0->data;
        desc1->xfer.count = 4;
    }
}

/* GPIO interrupt handler for mc13783 */
void INT_MC13783(void)
{
    /* Mask the interrupt (unmasked after final read services it). */
    gpio_int_disable(MC13783_EVENT_ID);
    gpio_int_clear(MC13783_EVENT_ID);

    /* Setup the read packets */
    int_xfers[0].data[0] = MC13783_INTERRUPT_STATUS0 << 25;
    int_xfers[0].data[1] = MC13783_INTERRUPT_STATUS1 << 25;

    unsigned long cpsr = disable_irq_save();

    /* Do these without intervening transfers */
    if (mc13783_transfer(&int_xfers[0].xfer, int_xfers[0].data,
                         int_xfers[0].data, 2, mc13783_int_svc_callback))
    {
        /* Start this provisionally and fill-in actual values during the
           first transfer's callback - set whatever could be known */
        mc13783_transfer(&int_xfers[1].xfer, int_xfers[0].data, NULL, 2,
                         mc13783_int_svc_complete_callback);
    }

    restore_irq(cpsr);
}

void INIT_ATTR mc13783_init(void)
{
    /* Serial interface must have been initialized first! */

    /* Enable the PMIC SPI module */
    spi_enable_node(&mc13783_spi, true);

    /* Mask any PMIC interrupts for now - modules will enable them as
     * required */
    mc13783_write(MC13783_INTERRUPT_MASK0, 0xffffff);
    mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff);

    gpio_int_clear(MC13783_EVENT_ID);
    gpio_enable_event(MC13783_EVENT_ID, true);
}

void mc13783_close(void)
{
    int_restore = false;
    gpio_int_disable(MC13783_EVENT_ID);
    gpio_enable_event(MC13783_EVENT_ID, false);
    spi_enable_node(&mc13783_spi, false);
}

void mc13783_enable_event(enum mc13783_int_ids id, bool enable)
{
    static const unsigned char pmic_intm_regs[2] =
        { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 };

    const struct mc13783_event * const event = event_from_id(id);
    if (event == NULL)
        return;

    unsigned int set = id / 32;
    uint32_t mask = 1 << (id % 32);
    uint32_t bit = enable ? mask : 0;

    /* Mask GPIO while changing bits around */
    int_restore = false;
    gpio_int_disable(MC13783_EVENT_ID);
    mc13783_write_masked(pmic_intm_regs[set], bit ^ mask, mask);
    bitmod32(&pmic_int_sense_enb[set], event->sense ? bit : 0, mask);
    bitmod32(&pmic_int_enb[set], bit, mask);
    int_restore = true;
    gpio_int_enable(MC13783_EVENT_ID);
}

uint32_t mc13783_event_sense(void)
{
    const struct mc13783_event *event = current_event;
    return int_xfers[0].data[2 + event->id / 32] & event->sense;
}

uint32_t mc13783_set(unsigned address, uint32_t bits)
{
    return mc13783_write_masked(address, bits, bits);
}

uint32_t mc13783_clear(unsigned address, uint32_t bits)
{
    return mc13783_write_masked(address, 0, bits);
}

/* Called when the first transfer of mc13783_write_masked is complete */
static void mc13783_write_masked_cb(struct spi_transfer_desc *xfer)
{
    struct mc13783_transfer_desc *desc = (struct mc13783_transfer_desc *)xfer;
    desc->data[1] |= desc->data[0] & desc->data[2]; /* & ~mask */
}

uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask)
{
    if (address >= MC13783_NUM_REGS)
        return MC13783_DATA_ERROR;

    mask &= 0xffffff;

    struct mc13783_transfer_desc xfers[2];
    xfers[0].data[0] = address << 25;
    xfers[0].data[1] = (1 << 31) | (address << 25) | (data & mask);
    xfers[0].data[2] = ~mask;

    unsigned long cpsr = disable_irq_save();

    /* Queue up two transfers in a row */
    bool ok = mc13783_transfer(&xfers[0].xfer,
                               &xfers[0].data[0], &xfers[0].data[0], 1,
                               mc13783_write_masked_cb) &&
              mc13783_sync_transfer(&xfers[1], &xfers[0].data[1], NULL, 1);

    restore_irq(cpsr);

    if (ok && wait_for_transfer_complete(&xfers[1]))
        return xfers[0].data[0];

    return MC13783_DATA_ERROR;
}

uint32_t mc13783_read(unsigned address)
{
    if (address >= MC13783_NUM_REGS)
        return MC13783_DATA_ERROR;

    uint32_t packet = address << 25;

    struct mc13783_transfer_desc xfer;
    if (mc13783_sync_transfer(&xfer, &packet, &packet, 1) &&
        wait_for_transfer_complete(&xfer))
    {
        return packet;
    }

    return MC13783_DATA_ERROR;
}

int mc13783_write(unsigned address, uint32_t data)
{
    if (address >= MC13783_NUM_REGS)
        return -1;

    uint32_t packet = (1 << 31) | (address << 25) | (data & 0xffffff);

    struct mc13783_transfer_desc xfer;
    if (mc13783_sync_transfer(&xfer, &packet, NULL, 1) &&
        wait_for_transfer_complete(&xfer))
    {
        return 1 - xfer.xfer.count;
    }

    return -1;
}

int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer,
                      int count)
{
    struct mc13783_transfer_desc xfer;
    sync_transfer_init(&xfer);

    if (mc13783_read_async(&xfer.xfer, regs, buffer, count,
                           mc13783_xfer_complete_cb) &&
        wait_for_transfer_complete(&xfer))
    {
        return count - xfer.xfer.count;
    }

    return -1;
}

int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer,
                       int count)
{
    struct mc13783_transfer_desc xfer;
    sync_transfer_init(&xfer);

    if (mc13783_write_async(&xfer.xfer, regs, buffer, count,
                            mc13783_xfer_complete_cb) &&
        wait_for_transfer_complete(&xfer))
    {
        return count - xfer.xfer.count;
    }

    return -1;
}

bool mc13783_read_async(struct spi_transfer_desc *xfer,
                        const unsigned char *regs, uint32_t *buffer,
                        int count, spi_transfer_cb_fn_type callback)
{
    for (int i = 0; i < count; i++)
    {
        unsigned reg = regs[i];

        if (reg >= MC13783_NUM_REGS)
            return false;

        buffer[i] = reg << 25;
    }

    return mc13783_transfer(xfer, buffer, buffer, count, callback);
}

bool mc13783_write_async(struct spi_transfer_desc *xfer,
                         const unsigned char *regs, uint32_t *buffer,
                         int count, spi_transfer_cb_fn_type callback)
{
    for (int i = 0; i < count; i++)
    {
        unsigned reg = regs[i];

        if (reg >= MC13783_NUM_REGS)
            return false;

        buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff);
    }

    return mc13783_transfer(xfer, buffer, NULL, count, callback);
}
