/**
 * @file wrtd-adcin.c
 *
 * ADCIN: trigger coming from the ADC mezzanine.
 *
 * Copyright (c) 2018-2019 CERN (home.cern)
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include "mockturtle-rt.h"

#define ADCIN_NUM_CHANNELS 5

struct wrtd_adcin_dev {
        uint32_t io_addr;
        /* FIXME: base channel (to create the event id).  */
};

static inline void adcin_writel(const struct wrtd_adcin_dev *dev,
                                uint32_t value, uint32_t reg)
{
        dp_writel(value, dev->io_addr + reg);
}

static inline uint32_t adcin_readl(const struct wrtd_adcin_dev *dev, uint32_t reg)
{
        return dp_readl(dev->io_addr + reg);
}

static inline int adcin_wr_link_up(struct wrtd_adcin_dev *adcin)
{
        return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_LINK;
}

static inline int adcin_wr_time_locked(struct wrtd_adcin_dev *adcin)
{
        return 1;
}

static void adcin_wr_enable_lock(struct wrtd_adcin_dev *dev, int enable)
{
        return;
}

static inline int adcin_wr_time_ready(struct wrtd_adcin_dev *adcin)
{
        return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_VALID;
}

static inline int adcin_wr_sync_timeout(void)
{
        return 0;
}

/**
 * Handles input timestamps from all ADC channels.
 *
 * TODO: adc config: base address
 *                   first channel number (for sid)
 *                   time offset
 */
static void adcin_input(struct wrtd_adcin_dev *adcin)
{
        uint32_t status = adcin_readl(adcin, ALT_TRIGOUT_STATUS);
        uint32_t mask;
        struct wrtd_event ev;
        int i;

        /* Poll the FIFO and read the timestamp */
        if(!(status & ALT_TRIGOUT_TS_PRESENT))
                return;

        mask = adcin_readl(adcin, ALT_TRIGOUT_TS_MASK_SEC + 0);
        ev.ts.seconds = adcin_readl(adcin, ALT_TRIGOUT_TS_MASK_SEC + 4);
        ev.ts.ns = adcin_readl(adcin, ALT_TRIGOUT_TS_CYCLES) * 8;
        ev.ts.frac = 0;

        for (i = 0; i < ADCIN_NUM_CHANNELS; i++) {

                /* The last channel is the ext trigger with a different mask */
                if ( i == ADCIN_NUM_CHANNELS - 1 ) {
                        if (!(mask & (ALT_TRIGOUT_EXT_MASK >> 32)))
                                continue;
                }
                else if (!(mask & ((ALT_TRIGOUT_CH1_MASK >> 32) << i)))
                        continue;

                memset(ev.id, 0, WRTD_ID_LEN);
                ev.id[0] = 'L';
                ev.id[1] = 'C';
                ev.id[2] = '-';
                ev.id[3] = 'I';
                ev.id[4] = '1' + i;
                ev.flags = 0;

                wrtd_log(WRTD_LOG_MSG_EV_GENERATED,
                         WRTD_LOG_GENERATED_DEVICE + (i * 8),
                         NULL, &ev, NULL);

                /* Pass to wrtd.  */
                wrtd_route_in(&ev);
        }
}

static int adcin_init(struct wrtd_adcin_dev *adcin)
{
        pr_debug("adcin initialization complete\n\r");

        return 0;
}
