/*
 * Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
 * Copyright 2014 Jared Boone <jared@sharebrained.com>
 *
 * This file is part of HackRF.
 *
 * 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, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

/*
 * 'gcc -DTEST -DDEBUG -O2 -o test rffc5071.c' prints out what test
 * program would do if it had a real spi library
 */

/*
 * The actual part on Jawbreaker is the RFFC5072, not the RFFC5071, but the
 * RFFC5071 may be installed instead.  The only difference between the parts is
 * that the RFFC5071 includes a second mixer.
 */

#include <stdint.h>
#include <string.h>
#include "rffc5071.h"
#include "rffc5071_regs.def" // private register def macros
#include "selftest.h"

#include <libopencm3/lpc43xx/scu.h>
#include "hackrf_core.h"

/* Default register values. */
static const uint16_t rffc5071_regs_default[RFFC5071_NUM_REGS] = {
	0xbefa, /* 00 */
	0x4064, /* 01 */
	0x9055, /* 02 */
	0x2d02, /* 03 */
	0xacbf, /* 04 */
	0xacbf, /* 05 */
	0x0028, /* 06 */
	0x0028, /* 07 */
	0xff00, /* 08 */
	0x8220, /* 09 */
	0x0202, /* 0A */
	0x0400, /* 0B */
	0x1a94, /* 0C */
	0xd89d, /* 0D */
	0x8900, /* 0E */
	0x1e84, /* 0F */
	0x89d8, /* 10 */
	0x9d00, /* 11 */
	0x3a20, /* 12, dithering off */
	0x0000, /* 13 */
	0x0000, /* 14 */
	0x0000, /* 15 */
	0x0206, /* 16 */
	0x4900, /* 17 */
	0x0281, /* 18 */
	0xf00f, /* 19 */
	0x0000, /* 1A */
	0x0000, /* 1B */
	0xc840, /* 1C */
	0x0000, /* 1D, readsel = 0b0000 */
	0x0005,
	/* 1E */};

/* Set up all registers according to defaults specified in docs. */
void rffc5071_init(rffc5071_driver_t* const drv)
{
	memcpy(drv->regs, rffc5071_regs_default, sizeof(drv->regs));
	drv->regs_dirty = 0x7fffffff;

	/* Write default register values to chip. */
	rffc5071_regs_commit(drv);

	selftest.mixer_id = rffc5071_reg_read(drv, RFFC5071_READBACK_REG);
	if ((selftest.mixer_id >> 3) != 4416) {
		selftest.report.pass = false;
	}
}

/*
 * Set up pins for GPIO and SPI control, configure SSP peripheral for SPI, and
 * set our own default register configuration.
 */
void rffc5071_setup(rffc5071_driver_t* const drv)
{
	gpio_set(drv->gpio_reset);
	gpio_output(drv->gpio_reset);

#ifdef PRALINE
	/* Configure mixer PLL lock detect pin */
	scu_pinmux(SCU_MIXER_LD, SCU_MIXER_LD_PINCFG);
	gpio_input(drv->gpio_ld);
#endif

	rffc5071_init(drv);

	/* initial setup */
	/* put zeros in freq contol registers */
	set_RFFC5071_P2N(drv, 0);
	set_RFFC5071_P2LODIV(drv, 0);
	set_RFFC5071_P2PRESC(drv, 0);
	set_RFFC5071_P2VCOSEL(drv, 0);
	set_RFFC5071_P2NLSB(drv, 0);

	/* set ENBL and MODE to be configured via 3-wire interface,
	 * not control pins. */
	set_RFFC5071_SIPIN(drv, 1);

	/* GPOs are active at all times */
	set_RFFC5071_GATE(drv, 1);

#ifdef PRALINE
	/* Enable GPO Lock output signal */
	set_RFFC5071_LOCK(drv, 1);
#endif

	rffc5071_regs_commit(drv);
}

static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r)
{
	(void) drv;

	uint16_t data[] = {0x80 | (r & 0x7f), 0xffff};
	spi_bus_transfer(drv->bus, data, 2);
	return data[1];
}

static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t v)
{
	(void) drv;

	uint16_t data[] = {0x00 | (r & 0x7f), v};
	spi_bus_transfer(drv->bus, data, 2);
}

uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r)
{
	/* Readback register is not cached. */
	if (r == RFFC5071_READBACK_REG) {
		return rffc5071_spi_read(drv, r);
	}

	/* Discard uncommited write when reading. This shouldn't
	 * happen, and probably has not been tested. */
	if ((drv->regs_dirty >> r) & 0x1) {
		drv->regs[r] = rffc5071_spi_read(drv, r);
	};
	return drv->regs[r];
}

void rffc5071_reg_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t v)
{
	drv->regs[r] = v;
	rffc5071_spi_write(drv, r, v);
	RFFC5071_REG_SET_CLEAN(drv, r);
}

static inline void rffc5071_reg_commit(rffc5071_driver_t* const drv, uint8_t r)
{
	rffc5071_reg_write(drv, r, drv->regs[r]);
}

void rffc5071_regs_commit(rffc5071_driver_t* const drv)
{
	int r;
	for (r = 0; r < RFFC5071_NUM_REGS; r++) {
		if ((drv->regs_dirty >> r) & 0x1) {
			rffc5071_reg_commit(drv, r);
		}
	}
}

void rffc5071_tx(rffc5071_driver_t* const drv)
{
	set_RFFC5071_ENBL(drv, 0);
	set_RFFC5071_FULLD(drv, 0);
	set_RFFC5071_MODE(drv, 1); /* mixer 2 used for both RX and TX */
	rffc5071_regs_commit(drv);
}

void rffc5071_rx(rffc5071_driver_t* const drv)
{
	set_RFFC5071_ENBL(drv, 0);
	set_RFFC5071_FULLD(drv, 0);
	set_RFFC5071_MODE(drv, 1); /* mixer 2 used for both RX and TX */
	rffc5071_regs_commit(drv);
}

/*
 * This function turns on both mixer (full-duplex) on the RFFC5071, but our
 * current hardware designs do not support full-duplex operation.
 */
void rffc5071_rxtx(rffc5071_driver_t* const drv)
{
	set_RFFC5071_ENBL(drv, 0);
	set_RFFC5071_FULLD(drv, 1); /* mixer 1 and mixer 2 (RXTX) */
	rffc5071_regs_commit(drv);

	rffc5071_enable(drv);
}

void rffc5071_disable(rffc5071_driver_t* const drv)
{
	set_RFFC5071_ENBL(drv, 0);
	rffc5071_regs_commit(drv);
}

void rffc5071_enable(rffc5071_driver_t* const drv)
{
	set_RFFC5071_ENBL(drv, 1);
	rffc5071_regs_commit(drv);
}

#define FREQ_ONE_MHZ (1000ULL * 1000ULL)
#define REF_FREQ     (40 * FREQ_ONE_MHZ)
#define LO_MAX       (5400 * FREQ_ONE_MHZ)

/* configure frequency synthesizer (lo in Hz) */
uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo)
{
	uint64_t fvco;
	uint8_t fbkdivlog;
	uint16_t n;
	uint64_t tune_freq_hz;
	uint16_t p1nmsb;
	uint8_t p1nlsb;

	/* Calculate n_lo (no division) */
	uint8_t n_lo = 0;
	uint64_t x = LO_MAX >> 1;
	while ((x >= lo) && (n_lo < 5)) {
		n_lo++;
		x >>= 1;
	}

	fvco = lo << n_lo;

	/*
	 * Higher charge pump leakage setting is required above 3.2 GHz.
	 */
	if (fvco > (3200 * FREQ_ONE_MHZ)) {
		set_RFFC5071_PLLCPL(drv, 3);
	} else {
		set_RFFC5071_PLLCPL(drv, 2);
	}

	/*
	 * Supposedly fbkdivlog can be set to 1 when VCO is below 3.2 GHz, but
	 * this has resulted in tuning instability on some boards, most evident
	 * in RX sweep mode.
	 */
	fbkdivlog = 2;

	uint64_t tmp_n = (fvco << (24ULL - fbkdivlog)) / REF_FREQ;

	/* Round to nearest step = ref_MHz / 2**s. For s=6, step=625000 Hz */
	/* This also ensures the lowest 22-s fractional bits are set to 0. */
	const uint8_t s = 6;
	const uint8_t d = (24 - fbkdivlog + n_lo) - s;
	tmp_n = ((tmp_n + (1 << (d - 1))) >> d) << d;

	n = tmp_n >> 24ULL;
	p1nmsb = (tmp_n >> 8ULL) & 0xffff;
	p1nlsb = tmp_n & 0xff;

	tune_freq_hz = (tmp_n * REF_FREQ) >> (24 - fbkdivlog + n_lo);

	/* Path 2 */
	set_RFFC5071_P2LODIV(drv, n_lo);
	set_RFFC5071_P2N(drv, n);
	set_RFFC5071_P2PRESC(drv, fbkdivlog);
	set_RFFC5071_P2NMSB(drv, p1nmsb);
	if (s > 14) {
		/* Only set when the step size is small enough. */
		set_RFFC5071_P2NLSB(drv, p1nlsb);
	}

	rffc5071_regs_commit(drv);

	return tune_freq_hz;
}

uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint64_t hz)
{
	uint32_t tune_freq;

	rffc5071_disable(drv);
	tune_freq = rffc5071_config_synth(drv, hz);
	rffc5071_enable(drv);

	return tune_freq;
}

void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t gpo)
{
	/* We set GPO for both paths just in case. */
	set_RFFC5071_P1GPO(drv, gpo);
	set_RFFC5071_P2GPO(drv, gpo);

	rffc5071_regs_commit(drv);
}

#ifdef PRALINE
bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state)
{
	// The RFFC5072 can be configured to output PLL lock status on
	// GPO4. The lock detect signal is produced by a window detector
	// on the VCO tuning voltage. It goes high to show PLL lock when
	// the VCO tuning voltage is within the specified range, typically
	// 0.30V to 1.25V.
	//
	// During the tuning process the lock signal will often go high,
	// only to drop lock briefly before returning to the locked state.
	//
	// Therefore, to reliably detect lock it is necessary to also
	// track the state of the FSM that controls the tuning process.
	//
	// Before re-tuning begins, and after final lock has been
	// established, the FSM can be considered to be in STATE_LOCKED.
	//
	// The very first state change only occurs around 150us _after_ a
	// new frequency has been set and registers updated:
	//
	//   L        123456L   (STATE)
	//   ----___------_---  (LD)
	//
	// This means we need to track the state(s) that occur before
	// STATE_LOCKED to be able to reliably identify lock.
	//
	// At the time of writing 15 different states have been spotted in
	// the wild.
	//
	// The first six states occur at some point during most tuning
	// operations with the others occuring less frequently.
	//
	// Of the first six, two states have been identified as
	// STATE_PRELOCKn which, once entered, indicate that no further
	// changes will occur to the locked state.
	enum state {
		STATE_LOCKED = 0x17, // 0b10111
		STATE_00010 = 0x02,
		STATE_00100 = 0x04,
		STATE_01011 = 0x0b,
		STATE_PRELOCK1 = 0x10, // 0b10000
		STATE_PRELOCK2 = 0x1e, // 0b11110
		STATE_00000 = 0x00,
		STATE_00001 = 0x01, // mixer bypassed
		STATE_00011 = 0x03,
		STATE_00101 = 0x05,
		STATE_00110 = 0x06,
		STATE_00111 = 0x07,
		STATE_01010 = 0x0a,
		STATE_10110 = 0x16,
		STATE_11110 = 0x1e, // ?
		STATE_11111 = 0x1f,
		STATE_NONE = 0xff,
	};

	// Select which fields will be made available in the readback
	// register - we only need to do this the first time.
	if (*prelock_state == STATE_NONE) {
		set_RFFC5071_READSEL(drv, 0b0011);
		rffc5071_regs_commit(drv);
	}

	// read fsm state
	uint16_t rb = rffc5071_reg_read(drv, RFFC5071_READBACK_REG);
	uint8_t rsm_state = (rb >> 11) & 0b11111;

	// get gpo4 lock detect signal
	bool gpo4_ld = gpio_read(drv->gpio_ld);

	// parse state
	switch (rsm_state) {
	case STATE_LOCKED: // 'normal operation'
		if (gpo4_ld &&
		    ((*prelock_state == STATE_PRELOCK1) ||
		     (*prelock_state == STATE_PRELOCK2))) {
			return true;
		}
		break;
	case STATE_00010:
	case STATE_00100:
	case STATE_01011:
		break;
	case STATE_PRELOCK1:
		*prelock_state = rsm_state;
		break;
	case STATE_PRELOCK2:
		*prelock_state = rsm_state;
		break;
		// other states
	case STATE_00000:
	case STATE_00001:
	case STATE_00011:
	case STATE_00101:
	case STATE_00110:
	case STATE_00111:
	case STATE_01010:
	case STATE_10110:
	case STATE_11111:
	case STATE_NONE:
		break;
	default:
		// unknown state
		break;
	}

	return false;
}
#endif
