HackRF-Treasure-Chest/Software/portapack-mayhem/hackrf/firmware/common/rffc5071.c
2022-09-22 09:26:57 -07:00

288 lines
7.2 KiB
C

/*
* Copyright 2012 Michael Ossmann
* 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 "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 */
0x4800, /* 0B */
0x1a94, /* 0C */
0xd89d, /* 0D */
0x8900, /* 0E */
0x1e84, /* 0F */
0x89d8, /* 10 */
0x9d00, /* 11 */
0x2a20, /* 12 */
0x0000, /* 13 */
0x0000, /* 14 */
0x0000, /* 15 */
0x0000, /* 16 */
0x4900, /* 17 */
0x0281, /* 18 */
0xf00f, /* 19 */
0x0000, /* 1A */
0x0000, /* 1B */
0xc840, /* 1C */
0x1000, /* 1D */
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);
}
/*
* 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);
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_P2N(drv, 0);
set_RFFC5071_P2LODIV(drv, 0);
set_RFFC5071_P2PRESC(drv, 0);
set_RFFC5071_P2VCOSEL(drv, 0);
set_RFFC5071_P2N(drv, 0);
set_RFFC5071_P2LODIV(drv, 0);
set_RFFC5071_P2PRESC(drv, 0);
set_RFFC5071_P2VCOSEL(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);
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 LO_MAX 5400
#define REF_FREQ 40
#define FREQ_ONE_MHZ (1000*1000)
/* configure frequency synthesizer in integer mode (lo in MHz) */
uint64_t rffc5071_config_synth_int(rffc5071_driver_t* const drv, uint16_t lo) {
uint8_t lodiv;
uint16_t fvco;
uint8_t fbkdiv;
uint16_t n;
uint64_t tune_freq_hz;
uint16_t p1nmsb;
uint8_t p1nlsb;
/* Calculate n_lo */
uint8_t n_lo = 0;
uint16_t x = LO_MAX / lo;
while ((x > 1) && (n_lo < 5)) {
n_lo++;
x >>= 1;
}
lodiv = 1 << n_lo;
fvco = lodiv * lo;
/* higher divider and charge pump current required above
* 3.2GHz. Programming guide says these values (fbkdiv, n,
* maybe pump?) can be changed back after enable in order to
* improve phase noise, since the VCO will already be stable
* and will be unaffected. */
if (fvco > 3200) {
fbkdiv = 4;
set_RFFC5071_PLLCPL(drv, 3);
} else {
fbkdiv = 2;
set_RFFC5071_PLLCPL(drv, 2);
}
uint64_t tmp_n = ((uint64_t)fvco << 29ULL) / (fbkdiv*REF_FREQ) ;
n = tmp_n >> 29ULL;
p1nmsb = (tmp_n >> 13ULL) & 0xffff;
p1nlsb = (tmp_n >> 5ULL) & 0xff;
tune_freq_hz = (REF_FREQ * (tmp_n >> 5ULL) * fbkdiv * FREQ_ONE_MHZ)
/ (lodiv * (1 << 24ULL));
/* Path 2 */
set_RFFC5071_P2LODIV(drv, n_lo);
set_RFFC5071_P2N(drv, n);
set_RFFC5071_P2PRESC(drv, fbkdiv >> 1);
set_RFFC5071_P2NMSB(drv, p1nmsb);
set_RFFC5071_P2NLSB(drv, p1nlsb);
rffc5071_regs_commit(drv);
return tune_freq_hz;
}
/* !!!!!!!!!!! hz is currently ignored !!!!!!!!!!! */
uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint16_t mhz) {
uint32_t tune_freq;
rffc5071_disable(drv);
tune_freq = rffc5071_config_synth_int(drv, mhz);
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);
}