aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/usrp3/lib
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usrp3/lib')
-rw-r--r--firmware/usrp3/lib/CMakeLists.txt6
-rw-r--r--firmware/usrp3/lib/cron.c92
-rw-r--r--firmware/usrp3/lib/ethernet.c9
-rw-r--r--firmware/usrp3/lib/flash/spi_flash.c50
-rw-r--r--firmware/usrp3/lib/flash/spif_spsn_s25flxx.c238
-rw-r--r--firmware/usrp3/lib/fw_comm_protocol.c105
-rw-r--r--firmware/usrp3/lib/mdelay.c36
-rw-r--r--firmware/usrp3/lib/wb_spi.c206
8 files changed, 701 insertions, 41 deletions
diff --git a/firmware/usrp3/lib/CMakeLists.txt b/firmware/usrp3/lib/CMakeLists.txt
index 621b9b611..9d9ee3c6c 100644
--- a/firmware/usrp3/lib/CMakeLists.txt
+++ b/firmware/usrp3/lib/CMakeLists.txt
@@ -21,12 +21,16 @@ add_library(usrp3fw STATIC
udp_uart.c
wb_uart.c
wb_i2c.c
+ wb_spi.c
printf.c
wb_pkt_iface64.c
u3_net_stack.c
ethernet.c
- mdelay.c
chinch.c
print_addrs.c
link_state_route_proto.c
+ cron.c
+ fw_comm_protocol.c
+ flash/spi_flash.c
+ flash/spif_spsn_s25flxx.c
)
diff --git a/firmware/usrp3/lib/cron.c b/firmware/usrp3/lib/cron.c
new file mode 100644
index 000000000..24b8feb4e
--- /dev/null
+++ b/firmware/usrp3/lib/cron.c
@@ -0,0 +1,92 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#include "cron.h"
+
+//Counter specific
+static cron_counter_fetcher_t cron_fetch_counter;
+static uint32_t cron_counter_freq;
+
+//Cron job specific
+typedef struct {
+ uint32_t tick_interval;
+ uint32_t last_tick_count;
+} cron_job_t;
+
+static cron_job_t cron_job_table[CRON_MAX_JOBS];
+
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq)
+{
+ cron_fetch_counter = fetch_counter;
+ cron_counter_freq = counter_freq;
+
+ for (int i = 0; i < CRON_MAX_JOBS; i++) {
+ cron_job_table[i].tick_interval = 0;
+ }
+}
+
+uint32_t cron_get_ticks()
+{
+ return cron_fetch_counter();
+}
+
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit)
+{
+ return ((stop_ticks - start_ticks) / cron_counter_freq) * ((uint32_t)unit);
+}
+
+void sleep_ticks(uint32_t ticks)
+{
+ if (ticks == 0) return; //Handle the 0 delay case quickly
+
+ const uint32_t ticks_begin = cron_fetch_counter();
+ while(cron_fetch_counter() - ticks_begin < ticks) {
+ /*NOP: Spinloop*/
+ }
+}
+
+void sleep_us(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000000)));
+}
+
+void sleep_ms(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000)));
+}
+
+void cron_job_init(uint32_t job_id, uint32_t interval_ms)
+{
+ cron_job_table[job_id].tick_interval = (interval_ms * (cron_counter_freq/1000));
+ cron_job_table[job_id].last_tick_count = 0;
+}
+
+bool cron_job_run_due(uint32_t job_id)
+{
+ uint32_t new_tick_count = cron_fetch_counter();
+ bool run_job = (new_tick_count - cron_job_table[job_id].last_tick_count) >=
+ cron_job_table[job_id].tick_interval;
+
+ if (run_job) {
+ //If the job is due to run, update the tick count for the next run
+ //The assumption here is that the caller will actually run their job
+ //when the return value is true. If not, the caller just missed this
+ //iteration and will have to option to run the job in the next pass through.
+ cron_job_table[job_id].last_tick_count = new_tick_count;
+ }
+ return run_job;
+}
diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c
index 91efbfe1d..e9c18528d 100644
--- a/firmware/usrp3/lib/ethernet.c
+++ b/firmware/usrp3/lib/ethernet.c
@@ -21,7 +21,7 @@
#endif
#include "../x300/x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <trace.h>
#include "wb_i2c.h"
#include "wb_utils.h"
@@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
int x;
// Delay read of SFPP
if (delay_ms)
- mdelay(delay_ms);
+ sleep_ms(delay_ms);
// Read ID code from SFP
x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
// I2C Error?
@@ -312,10 +312,9 @@ static void xge_mac_init(const uint32_t base)
}
// base is pointer to XGE MAC on Wishbone.
-static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
+static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port)
{
int x;
- uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg;
// Read LASI Ctrl register to capture state.
//y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);
UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence.");
@@ -323,8 +322,10 @@ static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);
x = x | (1 << 15);
write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x);
+ uint32_t loopCount = 0;
while(x&(1<<15)) {
x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port);
+ if( loopCount++ > 200 ) break; // usually succeeds after 22 or 23 polls
}
}
diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c
new file mode 100644
index 000000000..b4257c96f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spi_flash.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <flash/spi_flash.h>
+
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops)
+{
+ flash->device = device;
+ flash->ops = ops;
+ flash->state = IDLE;
+ flash->last_offset = 0;
+ flash->id = ops->read_id(device);
+}
+
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ flash->ops->read(flash->device, offset, buf, num_bytes);
+}
+
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset)
+{
+ if (flash->ops->erase_sector_dispatch(flash->device, offset)) {
+ return flash->ops->erase_sector_commit(flash->device, offset);
+ } else {
+ return false;
+ }
+}
+
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) {
+ return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes);
+ } else {
+ return false;
+ }
+}
diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
new file mode 100644
index 000000000..244115b6f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <cron.h>
+#include <trace.h>
+#include <string.h> //for memset, memcpy
+
+#define S25FLXX_CMD_WIDTH 8
+#define S25FLXX_ADDR_WIDTH 24
+
+/* S25FLxx-specific commands */
+#define S25FLXX_CMD_READID 0x90 /* Read Manufacturer and Device Identification */
+#define S25FLXX_CMD_READSIG 0xAB /* Read Electronic Signature (Will release from Deep PD) */
+#define S25FLXX_CMD_READ 0x03 /* Read Data Bytes */
+#define S25FLXX_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */
+
+#define S25FLXX_CMD_WREN 0x06 /* Write Enable */
+#define S25FLXX_CMD_WRDI 0x04 /* Write Disable */
+
+#define S25FLXX_CMD_PP 0x02 /* Page Program */
+#define S25FLXX_CMD_SE 0xD8 /* Sector Erase */
+#define S25FLXX_CMD_BE 0xC7 /* Bulk Erase */
+#define S25FLXX_CMD_DP 0xB9 /* Deep Power-down */
+
+#define S25FLXX_CMD_RDSR 0x05 /* Read Status Register */
+#define S25FLXX_CMD_WRSR 0x01 /* Write Status Register */
+
+#define S25FLXX_STATUS_WIP 0x01 /* Write in Progress */
+#define S25FLXX_STATUS_E_ERR 0x20 /* Erase Error Occured */
+#define S25FLXX_STATUS_P_ERR 0x40 /* Programming Error Occured */
+
+#define S25FLXX_SECTOR_ERASE_TIME_MS 750 //Spec: 650ms
+#define S25FLXX_PAGE_WRITE_TIME_MS 1 //Spec: 750us
+
+#define S25FLXX_SMALL_SECTORS_PER_LOGICAL 16 //16 4-kB physical sectors per logical sector
+#define S25FLXX_LARGE_SECTOR_BASE 0x20000 //Large physical sectors start at logical sector 2
+
+inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash)
+{
+ uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF;
+ wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */);
+ return status;
+}
+
+inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms)
+{
+ uint32_t start_ticks = cron_get_ticks();
+ do {
+ if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) {
+ return true;
+ }
+ } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms);
+
+ return false; // Timed out
+}
+
+inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled)
+{
+ uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI;
+ wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH);
+}
+
+const spi_flash_ops_t spif_spsn_s25flxx_ops =
+{
+ .read_id = spif_spsn_s25flxx_read_id,
+ .read = spif_spsn_s25flxx_read,
+ .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch,
+ .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit,
+ .erase_sector_busy = spif_spsn_s25flxx_device_busy,
+ .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch,
+ .write_page_commit = spif_spsn_s25flxx_write_page_commit,
+ .write_page_busy = spif_spsn_s25flxx_device_busy
+};
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations()
+{
+ return &spif_spsn_s25flxx_ops;
+}
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash)
+{
+ wb_spi_slave_select(flash->bus);
+ uint32_t command = S25FLXX_CMD_READID << 24;
+ wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32);
+ uint16_t id = 0;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16);
+ wb_spi_slave_deselect(flash->bus);
+ return id;
+}
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 5 byte instruction tranfer:
+ //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0)
+ uint8_t read_cmd[5];
+ read_cmd[4] = S25FLXX_CMD_FAST_READ;
+ *((uint32_t*)(read_cmd + 3)) = (offset << 8);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8);
+
+ //Read up to 4 bytes at a time until done
+ uint8_t data_sw[16], data[16];
+ size_t xact_size = 16;
+ unsigned char *bytes = (unsigned char *) buf;
+ for (size_t i = 0; i < num_bytes; i += 16) {
+ if (xact_size > num_bytes - i) xact_size = num_bytes - i;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8);
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k];
+ }
+ for (size_t j = 0; j < xact_size; j++) {
+ *bytes = data[j];
+ bytes++;
+ }
+ }
+ wb_spi_slave_deselect(flash->bus);
+}
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Sanity check sector size
+ if (offset % flash->sector_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size.");
+ return false;
+ }
+
+ if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //Send sector erase command
+ uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF);
+ wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Poll status until write done
+ uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1;
+ bool status = false;
+ for (uint8_t i = 0; i < phy_sector_count && !status; i++) {
+ status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS);
+ }
+ if (!status) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset);
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return status;
+}
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (num_bytes == 0 || num_bytes > flash->page_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size.");
+ return false;
+ }
+ if (num_bytes > (flash->sector_size * flash->num_sectors)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary.");
+ return false;
+ }
+
+ //Wait until ready and enable write enabled
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 4 byte instruction tranfer:
+ //PP_CMD, ADDR2, ADDR1, ADDR0
+ uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32);
+
+ //Write the page 16 bytes at a time.
+ uint8_t bytes_sw[16];
+ uint8_t* bytes = (uint8_t*) buf;
+ for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) {
+ const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16;
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k];
+ }
+ wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8);
+ bytes += xact_size;
+ }
+ wb_spi_slave_deselect(flash->bus);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ //Wait until write done
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time.");
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return true;
+}
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash)
+{
+ return (_spif_read_status(flash) & S25FLXX_STATUS_WIP);
+}
+
diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c
new file mode 100644
index 000000000..0cc931a76
--- /dev/null
+++ b/firmware/usrp3/lib/fw_comm_protocol.c
@@ -0,0 +1,105 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+
+#include <trace.h>
+#include <string.h> //memcmp
+
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ uint32_t iface_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback)
+{
+ bool send_response = false;
+
+ uint16_t signature = request->id;
+ uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
+ uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
+ if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
+ version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
+ product == product_id) //Verify device
+ {
+ //Request is valid. Copy it into the reply.
+ memcpy(response, request, sizeof(fw_comm_pkt_t));
+
+ //Start assuming no error
+ response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
+
+ //Otherwise, run the command set by the flags
+ switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
+ case FW_COMM_CMD_ECHO: {
+ UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
+ response->data_words = 1;
+ response->data[0] = iface_id;
+ } break;
+
+ case FW_COMM_CMD_POKE32: {
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
+ request->addr,*(request->data));
+ poke_callback(request->addr, *(request->data));
+ } break;
+
+ case FW_COMM_CMD_PEEK32: {
+ *(response->data) = peek_callback(request->addr);
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
+ request->addr,*(response->data));
+ } break;
+
+ case FW_COMM_CMD_BLOCK_POKE32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
+ }
+ } break;
+
+ case FW_COMM_CMD_BLOCK_PEEK32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
+ } break;
+
+ default: {
+ UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
+ response->flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+
+ //Send a reply if ack requested
+ send_response = (request->flags & FW_COMM_FLAGS_ACK);
+ } else { //Size, protocol, product check failed
+ UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
+ send_response = false;
+ }
+ return send_response;
+}
diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c
deleted file mode 100644
index 6d2742206..000000000
--- a/firmware/usrp3/lib/mdelay.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- * Copyright 2009 Ettus Research LLC
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "mdelay.h"
-#include "wb_utils.h"
-#include "printf.h"
-#include <stdint.h>
-//IJB FIXME.
-#include "../x300/x300_defs.h"
-
-void mdelay(int ms){
- for(int i = 0; i < ms; i++){
- static const uint32_t num_ticks = CPU_CLOCK/1000;
- const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
- // printf("DEBUG: Counter is %d\n",ticks_begin);
- while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
- /*NOP*/
- }
- }
-}
diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c
new file mode 100644
index 000000000..04904feea
--- /dev/null
+++ b/firmware/usrp3/lib/wb_spi.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <trace.h>
+
+typedef struct {
+ volatile uint32_t data0;
+ volatile uint32_t data1;
+ volatile uint32_t data2;
+ volatile uint32_t data3;
+ volatile uint32_t ctrl_status;
+ volatile uint32_t clkdiv;
+ volatile uint32_t slavesel;
+} wb_spi_regs_t;
+
+#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base)
+
+// Masks for different parts of CTRL reg
+#define WB_SPI_CTRL_AUTO_SS (1 << 13)
+#define WB_SPI_CTRL_IE (1 << 12)
+#define WB_SPI_CTRL_LSB (1 << 11)
+#define WB_SPI_CTRL_TXNEG (1 << 10)
+#define WB_SPI_CTRL_RXNEG (1 << 9)
+#define WB_SPI_CTRL_GO_BSY (1 << 8)
+#define WB_SPI_CTRL_LENGTH(x) (x & 0x7F)
+
+static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave)
+{
+ uint32_t flags = 0;
+ //If the SPI slave samples on the rising edge then shift
+ //data out on the falling edge.
+ if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG;
+ //If the SPI slave drives on the rising edge then shift
+ //data in on the falling edge.
+ if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG;
+ if (slave->lsb_first) flags |= WB_SPI_CTRL_LSB;
+ return flags;
+}
+
+static inline void _wait_for_xfer(const wb_spi_slave_t* slave)
+{
+ while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) {
+ /*NOP*/
+ }
+}
+
+void wb_spi_init(const wb_spi_slave_t* slave)
+{
+ WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div;
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+
+ //Do a dummy transaction with no slave selected to prime the engine
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave);
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+ _wait_for_xfer(slave);
+}
+
+void _wb_spi_transact_buf(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length,
+ bool auto_slave_sel)
+{
+ if (length == 0) return;
+
+ //Wait for previous transaction to finish
+ _wait_for_xfer(slave);
+
+ //Write SPI data register(s)
+ if (mosi_buf) {
+ uint8_t* mosi_bytes = (uint8_t*) mosi_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ word = (uint32_t) mosi_bytes[0];
+ } else if (bits_left <= 16) {
+ word = (((uint32_t) mosi_bytes[1]) << 0) |
+ (((uint32_t) mosi_bytes[0]) << 8);
+ } else if (bits_left <= 24) {
+ word = (((uint32_t) mosi_bytes[2]) << 0) |
+ (((uint32_t) mosi_bytes[1]) << 8) |
+ (((uint32_t) mosi_bytes[0]) << 16);
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ }
+ bits_left = 0;
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ mosi_bytes += 4;
+ bits_left -= 32;
+ }
+
+ switch (reg_index) {
+ case 0: WB_SPI_REGS(slave->base)->data0 = word; break;
+ case 1: WB_SPI_REGS(slave->base)->data1 = word; break;
+ case 2: WB_SPI_REGS(slave->base)->data2 = word; break;
+ case 3: WB_SPI_REGS(slave->base)->data3 = word; break;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+
+ //Compute flags for slave and write control register
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave);
+ if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS;
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl;
+
+ // Tell it which SPI slave device to access
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+
+ //Go go go!
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+
+ if (rw_mode == WRITE_READ) {
+ //Wait for SPI read operation to complete
+ _wait_for_xfer(slave);
+
+ if (miso_buf) {
+ //Read SPI data registers
+ uint8_t* miso_bytes = (uint8_t*) miso_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ switch (reg_index) {
+ case 0: word = WB_SPI_REGS(slave->base)->data0; break;
+ case 1: word = WB_SPI_REGS(slave->base)->data1; break;
+ case 2: word = WB_SPI_REGS(slave->base)->data2; break;
+ case 3: word = WB_SPI_REGS(slave->base)->data3; break;
+ }
+
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ miso_bytes[0] = word & 0xFF;
+ } else if (bits_left <= 16) {
+ miso_bytes[1] = word & 0xFF;
+ miso_bytes[0] = (word >> 8) & 0xFF;
+ } else if (bits_left <= 24) {
+ miso_bytes[2] = word & 0xFF;
+ miso_bytes[1] = (word >> 8) & 0xFF;
+ miso_bytes[0] = (word >> 16) & 0xFF;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ }
+ bits_left = 0;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ miso_bytes += 4;
+ bits_left -= 32;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+ }
+}
+
+void wb_spi_transact(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true);
+}
+
+void wb_spi_transact_man_ss(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false);
+}
+
+void wb_spi_slave_select(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually select slave
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+}
+
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually deselect slave
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+}