Make switching SPI bus modes automatic.

This commit is contained in:
Martin Ling 2026-05-29 06:30:57 +01:00
parent eb2e6a1c05
commit 595ea6c072
22 changed files with 63 additions and 69 deletions

View file

@ -25,7 +25,6 @@
#include <stdbool.h>
#include "ice40_spi.h"
#include "max283x.h"
/* Driver instance. */
fpga_driver_t fpga = {
@ -57,9 +56,7 @@ void fpga_setup(fpga_driver_t* const drv)
uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r)
{
uint8_t v;
ssp1_set_mode_ice40();
v = ice40_spi_read(drv->bus, r);
ssp1_set_mode_max283x();
drv->regs[r] = v;
return v;
}
@ -67,9 +64,7 @@ uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r)
void fpga_reg_write(fpga_driver_t* const drv, uint8_t r, uint8_t v)
{
drv->regs[r] = v;
ssp1_set_mode_ice40();
ice40_spi_write(drv->bus, r, v);
ssp1_set_mode_max283x();
FPGA_REG_SET_CLEAN(drv, r);
}

View file

@ -26,7 +26,6 @@
#include "fpga.h"
#include "ice40_spi.h"
#include "lz4_blk.h"
#include "max283x.h"
#include "selftest.h"
struct fpga_image_read_ctx {
@ -86,7 +85,6 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index)
// A callback function is used by the FPGA programmer
// to obtain consecutive gateware chunks.
ice40_spi_target_init(&ice40);
ssp1_set_mode_ice40();
struct fpga_image_read_ctx fpga_image_ctx = {
.loader = loader,
.addr = loader->start_addr + bitstream_offset,
@ -96,7 +94,6 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index)
loader->out_buffer,
fpga_image_read_block_cb,
&fpga_image_ctx);
ssp1_set_mode_max283x();
// Update selftest result.
selftest.fpga_image_load = success ? PASSED : FAILED;

View file

@ -71,10 +71,8 @@ bool fpga_spi_selftest(void)
// Test writing a register and reading it back.
uint8_t reg = 6;
uint8_t write_value = 0xA5;
ssp1_set_mode_ice40();
ice40_spi_write(&ice40, reg, write_value);
uint8_t read_value = ice40_spi_read(&ice40, reg);
ssp1_set_mode_max283x();
// Update selftest result.
selftest.fpga_spi = (read_value == write_value) ? PASSED : FAILED;

View file

@ -40,11 +40,6 @@ ice40_spi_driver_t ice40 = {
.bus = &spi_bus_ssp1,
};
void ssp1_set_mode_ice40(void)
{
spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga);
}
void ice40_spi_target_init(ice40_spi_driver_t* const drv)
{
const platform_scu_t* scu = platform_scu();
@ -67,14 +62,14 @@ void ice40_spi_target_init(ice40_spi_driver_t* const drv)
uint8_t ice40_spi_read(ice40_spi_driver_t* const drv, uint8_t r)
{
uint8_t value[3] = {r & 0x7F, 0, 0};
spi_bus_transfer(drv->bus, value, 3);
spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3);
return value[2];
}
void ice40_spi_write(ice40_spi_driver_t* const drv, uint8_t r, uint16_t v)
{
uint8_t value[3] = {(r & 0x7F) | 0x80, v, 0};
spi_bus_transfer(drv->bus, value, 3);
spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3);
}
static void spi_ssp1_wait_for_tx_fifo_not_full(void)
@ -107,6 +102,8 @@ bool ice40_spi_syscfg_program(
size_t (*read_block_cb)(void* ctx),
void* read_ctx)
{
spi_bus_start(drv->bus, &ssp_config_ice40_fpga);
// Drive CRESET_B = 0, SPI_SS = 0, SPI_SCK = 1.
gpio_clear(drv->gpio_creset);
gpio_clear(drv->gpio_select);

View file

@ -48,4 +48,3 @@ bool ice40_spi_syscfg_program(
/* Driver instance. */
extern ssp_config_t ssp_config_ice40_fpga;
extern ice40_spi_driver_t ice40;
void ssp1_set_mode_ice40(void);

View file

@ -109,7 +109,7 @@ static void max2831_write(max2831_driver_t* const drv, uint8_t r, uint16_t v)
{
uint32_t word = (((uint32_t) v & 0x3fff) << 4) | (r & 0xf);
uint16_t values[2] = {word >> 9, word & 0x1ff};
spi_bus_transfer(drv->bus, values, 2);
spi_bus_transfer(drv->bus, drv->config, values, 2);
}
uint16_t max2831_reg_read(max2831_driver_t* const drv, uint8_t r)

View file

@ -50,6 +50,7 @@ typedef enum {
typedef struct _max2831_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
gpio_t gpio_rxhp;

View file

@ -156,14 +156,14 @@ void max2837_setup(max2837_driver_t* const drv)
static uint16_t max2837_read(max2837_driver_t* const drv, uint8_t r)
{
uint16_t value = (1 << 15) | (r << 10);
spi_bus_transfer(drv->bus, &value, 1);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
return value & 0x3ff;
}
static void max2837_write(max2837_driver_t* const drv, uint8_t r, uint16_t v)
{
uint16_t value = (r << 10) | (v & 0x3ff);
spi_bus_transfer(drv->bus, &value, 1);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
}
uint16_t max2837_reg_read(max2837_driver_t* const drv, uint8_t r)

View file

@ -43,6 +43,7 @@ typedef enum {
typedef struct _max2837_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rx_enable;
gpio_t gpio_tx_enable;

View file

@ -164,14 +164,14 @@ void max2839_setup(max2839_driver_t* const drv)
static uint16_t max2839_read(max2839_driver_t* const drv, uint8_t r)
{
uint16_t value = (1 << 15) | (r << 10);
spi_bus_transfer(drv->bus, &value, 1);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
return value & 0x3ff;
}
static void max2839_write(max2839_driver_t* const drv, uint8_t r, uint16_t v)
{
uint16_t value = (r << 10) | (v & 0x3ff);
spi_bus_transfer(drv->bus, &value, 1);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
}
uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r)

View file

@ -46,6 +46,7 @@ typedef enum {
typedef struct _max2839_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
void (*target_init)(struct _max2839_driver_t* const drv);

View file

@ -29,7 +29,6 @@
#include "fixed_point.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#include "spi_bus.h"
#ifdef IS_PRALINE
#include "max2831_target.h"
@ -56,14 +55,10 @@ ssp_config_t ssp_config_max283x = {
max283x_driver_t max283x = {};
void ssp1_set_mode_max283x(void)
{
spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x);
}
#ifdef IS_PRALINE
max2831_driver_t max2831 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2831_target_init,
.set_mode = max2831_target_set_mode,
};
@ -72,6 +67,7 @@ max2831_driver_t max2831 = {
#ifdef IS_NOT_PRALINE
max2837_driver_t max2837 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2837_target_init,
.set_mode = max2837_target_set_mode,
};
@ -80,6 +76,7 @@ max2837_driver_t max2837 = {
#ifdef IS_HACKRF_ONE
max2839_driver_t max2839 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2839_target_init,
.set_mode = max2839_target_set_mode,
};

View file

@ -143,4 +143,3 @@ void max283x_rx_calibration(max283x_driver_t* const drv);
/* Driver instance. */
extern ssp_config_t ssp_config_max283x;
extern max283x_driver_t max283x;
void ssp1_set_mode_max283x(void);

View file

@ -48,14 +48,9 @@ max5864_driver_t max5864 = {
.target_init = max5864_target_init,
};
void ssp1_set_mode_max5864(void)
{
spi_bus_start(max5864.bus, &ssp_config_max5864);
}
static void max5864_write(max5864_driver_t* const drv, uint8_t value)
{
spi_bus_transfer(drv->bus, &value, 1);
spi_bus_transfer(drv->bus, &ssp_config_max5864, &value, 1);
}
static void max5864_init(max5864_driver_t* const drv)

View file

@ -42,4 +42,3 @@ void max5864_xcvr(max5864_driver_t* const drv);
/* Driver instance. */
extern ssp_config_t ssp_config_max5864;
extern max5864_driver_t max5864;
void ssp1_set_mode_max5864(void);

View file

@ -318,8 +318,6 @@ void pins_setup(void)
}
#endif
ssp1_set_mode_max283x();
mixer_bus_setup(&mixer);
#ifdef IS_H1_R9

View file

@ -494,11 +494,9 @@ void rf_path_pin_setup(rf_path_t* const rf_path)
void rf_path_init(rf_path_t* const rf_path)
{
ssp1_set_mode_max5864();
max5864_setup(&max5864);
max5864_shutdown(&max5864);
ssp1_set_mode_max283x();
max283x_setup(&max283x);
max283x_start(&max283x);
@ -536,9 +534,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
} else {
mixer_enable(&mixer);
}
ssp1_set_mode_max5864();
max5864_tx(&max5864);
ssp1_set_mode_max283x();
max283x_tx(&max283x);
break;
@ -553,9 +549,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
} else {
mixer_enable(&mixer);
}
ssp1_set_mode_max5864();
max5864_rx(&max5864);
ssp1_set_mode_max283x();
max283x_rx(&max283x);
break;
@ -564,9 +558,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
case RF_PATH_DIRECTION_RX_CALIBRATION:
rf_path->switchctrl &= ~SWITCHCTRL_TX;
mixer_disable(&mixer);
ssp1_set_mode_max5864();
max5864_xcvr(&max5864);
ssp1_set_mode_max283x();
if (direction == RF_PATH_DIRECTION_TX_CALIBRATION) {
max283x_tx_calibration(&max283x);
} else {
@ -581,9 +573,7 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
/* Set RF path to receive direction when "off" */
rf_path->switchctrl &= ~SWITCHCTRL_TX;
mixer_disable(&mixer);
ssp1_set_mode_max5864();
max5864_standby(&max5864);
ssp1_set_mode_max283x();
max283x_set_mode(&max283x, MAX283x_MODE_STANDBY);
break;
}

View file

@ -202,7 +202,7 @@ 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);
spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
return data[1];
}
@ -211,7 +211,7 @@ static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t
(void) drv;
uint16_t data[] = {0x00 | (r & 0x7f), v};
spi_bus_transfer(drv->bus, data, 2);
spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
}
uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r)

View file

@ -31,17 +31,29 @@ void spi_bus_start(spi_bus_t* const bus, const void* const config)
void spi_bus_stop(spi_bus_t* const bus)
{
bus->stop(bus);
bus->config = NULL;
}
void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count)
void spi_bus_transfer(
spi_bus_t* const bus,
const void* const config,
void* const data,
const size_t count)
{
if (config != bus->config) {
spi_bus_start(bus, config);
}
bus->transfer(bus, data, count);
}
void spi_bus_transfer_gather(
spi_bus_t* const bus,
const void* const config,
const spi_transfer_t* const transfers,
const size_t count)
{
if (config != bus->config) {
spi_bus_start(bus, config);
}
bus->transfer_gather(bus, transfers, count);
}

View file

@ -46,8 +46,13 @@ typedef struct _spi_bus_t {
void spi_bus_start(spi_bus_t* const bus, const void* const config);
void spi_bus_stop(spi_bus_t* const bus);
void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count);
void spi_bus_transfer(
spi_bus_t* const bus,
const void* const config,
void* const data,
const size_t count);
void spi_bus_transfer_gather(
spi_bus_t* const bus,
const void* const config,
const spi_transfer_t* const transfers,
const size_t count);

View file

@ -26,7 +26,6 @@
#include <stdint.h>
#include "clock_gen.h"
#include "max283x.h"
#include "rf_path.h"
#include "transceiver_mode.h"
@ -195,9 +194,6 @@ static void ui_update(void)
rad1o_lcdNl();
rad1o_lcdDisplay();
// Don't ask...
ssp1_set_mode_max283x();
}
static void rad1o_ui_init(void)
@ -211,8 +207,6 @@ static void rad1o_ui_deinit(void)
{
rad1o_lcdDeInit();
enabled = false;
// Don't ask...
ssp1_set_mode_max283x();
}
static void rad1o_ui_set_frequency(uint64_t frequency)

View file

@ -67,6 +67,22 @@ w25q80bv_driver_t spi_flash = {
.target_init = w25q80bv_target_init,
};
static void w25q80bv_transfer(
w25q80bv_driver_t* const drv,
void* const data,
const size_t count)
{
spi_bus_transfer(drv->bus, &ssp_config_w25q80bv, data, count);
}
static void w25q80bv_transfer_gather(
w25q80bv_driver_t* const drv,
const spi_transfer_t* const transfers,
size_t size)
{
spi_bus_transfer_gather(drv->bus, &ssp_config_w25q80bv, transfers, size);
}
/*
* Set up pins for GPIO and SPI control, configure SSP0 peripheral for SPI.
* SSP0_CS is controlled by GPIO in order to handle various transfer lengths.
@ -96,7 +112,7 @@ void w25q80bv_setup(w25q80bv_driver_t* const drv)
uint8_t w25q80bv_get_status(w25q80bv_driver_t* const drv)
{
uint8_t data[] = {W25Q80BV_READ_STATUS1, 0xFF};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
return data[1];
}
@ -104,7 +120,7 @@ uint8_t w25q80bv_get_status(w25q80bv_driver_t* const drv)
uint8_t w25q80bv_get_device_id(w25q80bv_driver_t* const drv)
{
uint8_t data[] = {W25Q80BV_DEVICE_ID, 0xFF, 0xFF, 0xFF, 0xFF};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
return data[4];
}
@ -124,7 +140,7 @@ void w25q80bv_get_unique_id(w25q80bv_driver_t* const drv, w25q80bv_unique_id_t*
0xFF,
0xFF,
0xFF};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
for (size_t i = 0; i < 8; i++) {
unique_id->id_8b[i] = data[5 + i];
@ -141,7 +157,7 @@ void w25q80bv_write_enable(w25q80bv_driver_t* const drv)
w25q80bv_wait_while_busy(drv);
uint8_t data[] = {W25Q80BV_WRITE_ENABLE};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
while (!(w25q80bv_get_status(drv) & W25Q80BV_STATUS_WEL)) {}
}
@ -159,7 +175,7 @@ void w25q80bv_chip_erase(w25q80bv_driver_t* const drv)
w25q80bv_write_enable(drv);
uint8_t data[] = {W25Q80BV_CHIP_ERASE};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
}
/* write up a 256 byte page or partial page */
@ -190,7 +206,7 @@ static void w25q80bv_page_program(
const spi_transfer_t transfers[] = {{header, ARRAY_SIZE(header)}, {data, len}};
spi_bus_transfer_gather(drv->bus, transfers, ARRAY_SIZE(transfers));
w25q80bv_transfer_gather(drv, transfers, ARRAY_SIZE(transfers));
}
/* write an arbitrary number of bytes */
@ -265,7 +281,7 @@ void w25q80bv_read(
const spi_transfer_t transfers[] = {{header, ARRAY_SIZE(header)}, {data, len}};
spi_bus_transfer_gather(drv->bus, transfers, ARRAY_SIZE(transfers));
w25q80bv_transfer_gather(drv, transfers, ARRAY_SIZE(transfers));
}
void w25q80bv_clear_status(w25q80bv_driver_t* const drv)
@ -273,16 +289,16 @@ void w25q80bv_clear_status(w25q80bv_driver_t* const drv)
w25q80bv_wait_while_busy(drv);
w25q80bv_write_enable(drv);
uint8_t data[] = {W25Q80BV_WRITE_STATUS, 0x00, 0x00};
spi_bus_transfer(drv->bus, data, ARRAY_SIZE(data));
w25q80bv_transfer(drv, data, ARRAY_SIZE(data));
}
void w25q80bv_get_full_status(w25q80bv_driver_t* const drv, uint8_t* data)
{
uint8_t cmd[] = {W25Q80BV_READ_STATUS1, 0xFF};
spi_bus_transfer(drv->bus, cmd, ARRAY_SIZE(cmd));
w25q80bv_transfer(drv, cmd, ARRAY_SIZE(cmd));
data[0] = cmd[1];
cmd[0] = W25Q80BV_READ_STATUS2;
cmd[1] = 0xFF;
spi_bus_transfer(drv->bus, cmd, ARRAY_SIZE(cmd));
w25q80bv_transfer(drv, cmd, ARRAY_SIZE(cmd));
data[1] = cmd[1];
}