aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/usrp3/lib/chinch.c
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2014-10-07 09:39:25 +0200
committerMartin Braun <martin.braun@ettus.com>2014-10-07 09:39:25 +0200
commit5bd58bc309e959537e3e820abfa39ee629b140a5 (patch)
tree81e3a611134e02d9118f0aa846b7146234849fe8 /firmware/usrp3/lib/chinch.c
parent9f6a11173aef5e661100268bd746963d713adb91 (diff)
downloaduhd-5bd58bc309e959537e3e820abfa39ee629b140a5.tar.gz
uhd-5bd58bc309e959537e3e820abfa39ee629b140a5.tar.bz2
uhd-5bd58bc309e959537e3e820abfa39ee629b140a5.zip
Reorganized firmware/ subdirectory (x300->usrp3, zpu->usrp2)
Diffstat (limited to 'firmware/usrp3/lib/chinch.c')
-rw-r--r--firmware/usrp3/lib/chinch.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/firmware/usrp3/lib/chinch.c b/firmware/usrp3/lib/chinch.c
new file mode 100644
index 000000000..e33378851
--- /dev/null
+++ b/firmware/usrp3/lib/chinch.c
@@ -0,0 +1,313 @@
+//
+// Copyright 2013 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 "chinch.h"
+
+#define PCIE_MSG_REG_BASE 0xFB00
+#define PCIE_MSG_DATA_REG 0 //Write: Data register for outbound requests and responses
+ //Read: Latched data for inbound requests
+#define PCIE_MSG_CTRL_REG 1 //Write: Control register for outbound requests and responses (Initiates xfer)
+ //Read: Latched control word for inbound requests
+#define PCIE_MSG_RESP_REG 2 //Read: Latched response data for outbound requests
+#define PCIE_MSG_STATUS_REG 3 //Read: Status register for inbound and outbound transactions
+
+// Transaction Word Format
+//
+// -Control- --Data--
+// 63 32 31 0
+// | | | |
+// FXXA AAAA DDDD DDDD
+//
+// where:
+// D = Data/Payload
+// A = Address
+// X = Reserved
+// F = Flags: {Read_Response, Write_Request, Read_Request, Half_Word_Cycle}
+
+#define PCIE_CTRL_REG_READ_RESP (1<<31)
+#define PCIE_CTRL_REG_WRITE (1<<30)
+#define PCIE_CTRL_REG_READ (1<<29)
+#define PCIE_CTRL_REG_HALF_WORD (1<<28)
+#define PCIE_CTRL_REG_ADDR_MASK 0x000FFFFF
+
+#define PCIE_STATUS_REG_READ_PENDING (1<<0)
+#define PCIE_STATUS_REG_REQ_PENDING (1<<1)
+#define PCIE_STATUS_REG_RESP_PENDING (1<<2)
+#define PCIE_STATUS_REG_BUSY (1<<4)
+
+#define CHINCH_FPGA_CONFIG_REG 0x58
+#define CHINCH_FLASH_WINDOW_REG0 0xC0
+#define CHINCH_FLASH_WINDOW_REG1 0xE0
+#define CHINCH_FLASH_2AAA_REG 0x400
+#define CHINCH_FLASH_5555_REG 0x408
+#define CHINCH_FLASH_WINDOW_BASE 0x60000
+#define CHINCH_FLASH_WINDOW_SIZE 0x20000
+#define CHINCH_FLASH_WINDOW_CONF 0x91
+
+//-----------------------------------------------------
+// Peek-Poke interface
+//-----------------------------------------------------
+
+bool chinch_poke(
+ const uint32_t addr,
+ const uint32_t data,
+ bool half_word,
+ uint32_t timeout
+)
+{
+ //Build transaction control word
+ uint32_t ctrl_word = 0, i;
+ ctrl_word |= (addr & PCIE_CTRL_REG_ADDR_MASK);
+ if (half_word) ctrl_word |= PCIE_CTRL_REG_HALF_WORD;
+ ctrl_word |= PCIE_CTRL_REG_WRITE;
+
+ //Wait for space in the transaction queue or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > timeout) return false;
+ }
+
+ //Flush transaction control and data registers
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG), data);
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), ctrl_word);
+
+ return true;
+}
+
+bool chinch_peek(
+ const uint32_t addr,
+ uint32_t* data,
+ bool half_word,
+ uint32_t timeout
+)
+{
+ //Build transaction control word
+ uint32_t ctrl_word = 0, i;
+ ctrl_word |= (addr & PCIE_CTRL_REG_ADDR_MASK);
+ if (half_word) ctrl_word |= PCIE_CTRL_REG_HALF_WORD;
+ ctrl_word |= PCIE_CTRL_REG_READ;
+
+ //Wait for space in the transaction queue or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > timeout) return false;
+ }
+
+ //Flush transaction control register
+ if (data) *data = 0;
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), ctrl_word);
+
+ //Wait for read completion or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_READ_PENDING) != 0) {
+ if (++i > timeout) return false;
+ }
+ //Read transaction data register
+ if (data) *data = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_RESP_REG));
+ return true;
+}
+
+//-----------------------------------------------------
+// Flash access
+//-----------------------------------------------------
+
+uint32_t g_cached_win_reg0;
+uint32_t g_cached_win_reg1;
+
+bool chinch_flash_init()
+{
+ chinch_peek32(CHINCH_FLASH_WINDOW_REG0, &g_cached_win_reg0);
+ chinch_peek32(CHINCH_FLASH_WINDOW_REG1, &g_cached_win_reg1);
+
+ bool status = true, passed = true;
+ STATUS_MERGE(chinch_poke32(CHINCH_FLASH_WINDOW_REG0, CHINCH_FLASH_WINDOW_BASE | CHINCH_FLASH_WINDOW_CONF), status);
+
+ //Run a loopback test to ensure that we will not corrupt the flash.
+ STATUS_MERGE(chinch_poke32(0x200, 0xDEADBEEF), status);
+ STATUS_MERGE(chinch_poke16(0x204, 0x5678), status);
+ uint32_t reg_val;
+ STATUS_MERGE(chinch_peek16(0x0, &reg_val), status);
+ STATUS_MERGE(chinch_poke16(0x206, reg_val), status);
+ STATUS_MERGE(chinch_peek32(0x200, &reg_val), status);
+ passed &= (reg_val == 0xDEADBEEF);
+ STATUS_MERGE(chinch_peek32(0x204, &reg_val), status);
+ passed &= (reg_val == 0x7AD05678);
+
+ return status && passed;
+}
+
+void chinch_flash_cleanup()
+{
+ chinch_poke32(CHINCH_FLASH_WINDOW_REG0, g_cached_win_reg0);
+ chinch_poke32(CHINCH_FLASH_WINDOW_REG1, g_cached_win_reg1);
+}
+
+bool chinch_flash_select_sector(uint32_t sector)
+{
+ return chinch_poke32(CHINCH_FLASH_WINDOW_REG1, sector * CHINCH_FLASH_WINDOW_SIZE);
+}
+
+bool chinch_flash_erase_sector()
+{
+ bool status = true;
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x0080), status); //Setup
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0030), status); //Erase
+
+ if (status) {
+ uint32_t read_data;
+ while (true) {
+ status = chinch_peek16(CHINCH_FLASH_WINDOW_BASE, &read_data); //Wait for sector to erase
+ if (((read_data & 0xFFFF) == 0xFFFF) || !status) break;
+ }
+ }
+ return status;
+}
+
+bool chinch_flash_read_buf(uint32_t offset, uint16_t* buf, uint32_t size)
+{
+ bool status = true;
+ uint32_t base_addr = CHINCH_FLASH_WINDOW_BASE | (offset & 0x3FFFF);
+ for (uint32_t i = 0; (i < size) && status; i++) {
+ uint32_t word;
+ STATUS_CHAIN(chinch_peek16(base_addr + (i * 2), &word), status);
+ buf[i] = (uint16_t)word;
+ }
+ return status;
+}
+
+bool chinch_flash_write_buf(uint32_t offset, uint16_t* buf, uint32_t size)
+{
+ if (size > CHINCH_FLASH_MAX_BUF_WRITES || buf == 0) return false;
+ bool status = true;
+
+ //Setup buffered write
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_MERGE(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0025), status); //Setup write
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, size - 1), status); //Num words
+
+ //Write the data
+ uint32_t base_addr = CHINCH_FLASH_WINDOW_BASE | (offset & 0x3FFFF);
+ for (uint32_t i = 0; i < size; i++) {
+ STATUS_CHAIN(chinch_poke16(base_addr + (i * 2), buf[i]), status);
+ }
+
+ //Commit write
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0029), status);
+
+ //Poll for completion
+ //Bit 7 of the data at the final address is the status bit.
+ //It is set to the inverse of bit 7 of the final data to be
+ //written until the final write is completed.
+ uint32_t read_data;
+ do {
+ STATUS_MERGE(chinch_peek16(base_addr + ((size - 1) * 2), &read_data), status);
+ } while (status && (((uint16_t)read_data ^ buf[size - 1]) & (1 << 7)));
+
+ return status;
+}
+
+//-----------------------------------------------------
+// FPGA Configuration
+//-----------------------------------------------------
+void chinch_start_config()
+{
+ chinch_poke32(CHINCH_FPGA_CONFIG_REG, 0x1);
+}
+
+config_status_t chinch_get_config_status()
+{
+ bool status = true;
+ uint32_t read_data;
+ STATUS_MERGE(chinch_peek32(CHINCH_FPGA_CONFIG_REG, &read_data), status);
+ return status ? (config_status_t)read_data : CHINCH_CONFIG_ERROR;
+}
+
+//-----------------------------------------------------
+// Read-back interface for the user initiated
+// PCIe register transactions
+//-----------------------------------------------------
+pcie_register_xact_t g_pcie_reg_xact_info;
+uint32_t g_pcie_res_timeout;
+
+bool _respond_to_pcie_xact_request(uint32_t response, uint32_t timeout)
+{
+ //Wait for space in the transaction queue or timeout
+ uint32_t i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > g_pcie_res_timeout) return false;
+ }
+
+ //First write data and then the control register to ensure coherency
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG), response);
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), PCIE_CTRL_REG_READ_RESP);
+
+ return true;
+}
+
+bool check_pcie_user_regport(pcie_register_xact_t** xact_info_hdl)
+{
+ //Check for pending transaction requests
+ if ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_REQ_PENDING) != 0) {
+ //Attach responder to transaction info
+ g_pcie_reg_xact_info.respond = _respond_to_pcie_xact_request;
+
+ //First read data and then the control register to ensure coherency
+ g_pcie_reg_xact_info.data = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG));
+ uint32_t xact_control = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG));
+
+ g_pcie_reg_xact_info.addr = xact_control & PCIE_CTRL_REG_ADDR_MASK;
+ g_pcie_reg_xact_info.size =
+ (xact_control & PCIE_CTRL_REG_HALF_WORD) == 0 ? PCIE_XACT_32_BIT : PCIE_XACT_16_BIT;
+ if ((xact_control & PCIE_CTRL_REG_READ) != 0)
+ g_pcie_reg_xact_info.type = PCIE_XACT_READ;
+ else if ((xact_control & PCIE_CTRL_REG_WRITE) != 0)
+ g_pcie_reg_xact_info.type = PCIE_XACT_WRITE;
+ else
+ g_pcie_reg_xact_info.type = PCIE_XACT_ERROR;
+
+ *xact_info_hdl = &g_pcie_reg_xact_info;
+ return true;
+ } else {
+ *xact_info_hdl = 0;
+ return false;
+ }
+}
+
+bool forward_pcie_user_xact_to_wb()
+{
+ pcie_register_xact_t* xact_info;
+ if (check_pcie_user_regport(&xact_info)) {
+ if (xact_info->size == PCIE_XACT_32_BIT) {
+ //Only respond to 32-bit transactions because that is all the LVFPGA interface can send
+ if (xact_info->type == PCIE_XACT_WRITE) {
+ wb_poke32(xact_info->addr, xact_info->data);
+ return true;
+ } else if (xact_info->type == PCIE_XACT_READ) {
+ return xact_info->respond(wb_peek32(xact_info->addr), CHINCH_DEFAULT_XACT_TIMEOUT);
+ }
+ }
+ }
+ return false;
+}
+
+
+