diff options
author | Martin Braun <martin.braun@ettus.com> | 2014-10-07 09:39:25 +0200 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2014-10-07 09:39:25 +0200 |
commit | 5bd58bc309e959537e3e820abfa39ee629b140a5 (patch) | |
tree | 81e3a611134e02d9118f0aa846b7146234849fe8 /firmware/usrp3/lib/chinch.c | |
parent | 9f6a11173aef5e661100268bd746963d713adb91 (diff) | |
download | uhd-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.c | 313 |
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, ®_val), status); + STATUS_MERGE(chinch_poke16(0x206, reg_val), status); + STATUS_MERGE(chinch_peek32(0x200, ®_val), status); + passed &= (reg_val == 0xDEADBEEF); + STATUS_MERGE(chinch_peek32(0x204, ®_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; +} + + + |