diff --git a/meson.build b/meson.build
index 76f52db8..93dcd883 100644
--- a/meson.build
+++ b/meson.build
@@ -457,7 +457,9 @@ mod17_def += openrtx_def + stm32f405_def
##
## Connect Systems CS70000
##
-cs7000_src = ['platform/drivers/stubs/nvmem_stub.c',
+cs7000_src = ['platform/drivers/NVM/nvmem_CS7000.c',
+ 'platform/drivers/NVM/W25Qx.c',
+ 'platform/drivers/NVM/eeep.c',
'platform/drivers/stubs/cps_io_stub.c',
'platform/drivers/stubs/radio_stub.c',
'platform/drivers/keyboard/keyboard_CS7000.c',
diff --git a/platform/drivers/NVM/nvmem_CS7000.c b/platform/drivers/NVM/nvmem_CS7000.c
new file mode 100644
index 00000000..b7bd13ed
--- /dev/null
+++ b/platform/drivers/NVM/nvmem_CS7000.c
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Copyright (C) 2024 by Silvano Seva IU2KWO *
+ * *
+ * 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 3 of the License, 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; if not, see *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "W25Qx.h"
+#include "eeep.h"
+
+static const struct W25QxCfg cfg =
+{
+ .spi = (const struct spiDevice *) &flash_spi,
+ .cs = { FLASH_CS }
+};
+
+W25Qx_DEVICE_DEFINE(eflash, cfg, 0x1000000) // 16 MB, 128 Mbit
+EEEP_DEVICE_DEFINE(eeep)
+
+const struct nvmPartition memPartitions[] =
+{
+ {
+ .offset = 0x0000, // First partition, calibration and other OEM data
+ .size = 32768 // 32kB
+ },
+ {
+ .offset = 0x8000, // Second partition EEEP storage
+ .size = 16384 // 16kB
+ },
+ {
+ .offset = 0xC000, // Third partition, available memory
+ .size = 0xFF4000
+ }
+};
+
+static const struct nvmDescriptor extMem[] =
+{
+ {
+ .name = "External flash",
+ .dev = &eflash,
+ .partNum = sizeof(memPartitions)/sizeof(struct nvmPartition),
+ .partitions = memPartitions
+ },
+ {
+ .name = "Virtual EEPROM",
+ .dev = &eeep,
+ .partNum = 0,
+ .partitions = NULL
+ }
+};
+
+static uint16_t settingsCrc;
+static uint16_t vfoCrc;
+
+void nvm_init()
+{
+ spiBitbang_init(&flash_spi);
+ W25Qx_init(&eflash);
+ eeep_init(&eeep, 0, 1);
+}
+
+void nvm_terminate()
+{
+ eeep_terminate(&eeep);
+ W25Qx_terminate(&eflash);
+ spiBitbang_terminate(&flash_spi);
+}
+
+const struct nvmDescriptor *nvm_getDesc(const size_t index)
+{
+ if(index > 2)
+ return NULL;
+
+ return &extMem[index];
+}
+
+void nvm_readCalibData(void *buf)
+{
+ (void) buf;
+}
+
+void nvm_readHwInfo(hwInfo_t *info)
+{
+ (void) info;
+}
+
+int nvm_readVfoChannelData(channel_t *channel)
+{
+ memset(channel, 0x00, sizeof(channel_t));
+ int ret = nvm_read(1, -1, 0x0001, channel, sizeof(channel_t));
+ if(ret < 0)
+ return -1;
+
+ vfoCrc = crc_ccitt(channel, sizeof(channel_t));
+
+ return 0;
+}
+
+int nvm_readSettings(settings_t *settings)
+{
+ memset(settings, 0x00, sizeof(settings_t));
+ int ret = nvm_read(1, -1, 0x0002, settings, sizeof(settings_t));
+ if(ret < 0)
+ return -1;
+
+ settingsCrc = crc_ccitt(settings, sizeof(settings_t));
+
+ return 0;
+}
+
+int nvm_writeSettings(const settings_t *settings)
+{
+ (void) settings;
+
+ return -1;
+}
+
+int nvm_writeSettingsAndVfo(const settings_t *settings, const channel_t *vfo)
+{
+ uint16_t crc = crc_ccitt(vfo, sizeof(channel_t));
+ if(crc != vfoCrc)
+ nvm_write(1, -1, 0x0001, vfo, sizeof(channel_t));
+
+ crc = crc_ccitt(settings, sizeof(settings_t));
+ if(crc != settingsCrc)
+ nvm_write(1, -1, 0x0002, settings, sizeof(settings_t));
+
+ return 0;
+}
diff --git a/platform/targets/CS7000/hwconfig.c b/platform/targets/CS7000/hwconfig.c
index 6653b41b..e5cdf2aa 100644
--- a/platform/targets/CS7000/hwconfig.c
+++ b/platform/targets/CS7000/hwconfig.c
@@ -21,6 +21,15 @@
#include
#include
+static const struct spiConfig spiFlashCfg =
+{
+ .clk = { FLASH_CLK },
+ .mosi = { FLASH_SDI },
+ .miso = { FLASH_SDO },
+ .clkPeriod = SCK_PERIOD_FROM_FREQ(1000000),
+ .flags = 0
+};
+
/**
* SPI bitbang function for SN74HC595 gpio extender.
*
@@ -61,5 +70,6 @@ static const struct gpioPin shiftRegStrobe = { GPIOEXT_STR };
static pthread_mutex_t adc1Mutex;
SPI_CUSTOM_DEVICE_DEFINE(spiSr, spiSr_func, NULL, NULL)
+SPI_BITBANG_DEVICE_DEFINE(flash_spi, spiFlashCfg, NULL)
GPIO_SHIFTREG_DEVICE_DEFINE(extGpio, (const struct spiDevice *) &spiSr, shiftRegStrobe, 24)
ADC_STM32_DEVICE_DEFINE(adc1, ADC1, &adc1Mutex, 3300000)
diff --git a/platform/targets/CS7000/hwconfig.h b/platform/targets/CS7000/hwconfig.h
index 6cbba353..4a9d623c 100644
--- a/platform/targets/CS7000/hwconfig.h
+++ b/platform/targets/CS7000/hwconfig.h
@@ -36,6 +36,7 @@ enum AdcChannels
extern const struct Adc adc1;
extern const struct spiCustomDevice spiSr;
+extern const struct spiCustomDevice flash_spi;
extern const struct gpioDev extGpio;
/* Screen dimensions */
diff --git a/platform/targets/CS7000/pinmap.h b/platform/targets/CS7000/pinmap.h
index 595d3179..ba44587c 100644
--- a/platform/targets/CS7000/pinmap.h
+++ b/platform/targets/CS7000/pinmap.h
@@ -89,7 +89,7 @@
#define BEEP_OUT GPIOA,5 // System "beep"
/* External flash */
-#define FLASH_CS GPIOE,2
+#define FLASH_CS &GpioE,2
#define FLASH_CLK GPIOE,3
#define FLASH_SDO GPIOE,4
#define FLASH_SDI GPIOE,5
diff --git a/platform/targets/CS7000/platform.c b/platform/targets/CS7000/platform.c
index 47268b1d..294d5fd4 100644
--- a/platform/targets/CS7000/platform.c
+++ b/platform/targets/CS7000/platform.c
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include
#include
@@ -51,6 +52,7 @@ void platform_init()
spi_init((const struct spiDevice *) &spiSr);
gpioShiftReg_init(&extGpio);
adcStm32_init(&adc1);
+ nvm_init();
#ifndef RUNNING_TESTSUITE
gpioDev_set(MAIN_PWR_SW);