forked from M-Labs/zynq-rs
remove flash support
PITA to get to work and most boards have SD.
This commit is contained in:
parent
a6955edf14
commit
e601ac9c45
|
@ -15,7 +15,7 @@ use libboard_zynq::{
|
||||||
self as zynq,
|
self as zynq,
|
||||||
clocks::source::{ArmPll, ClockSource, IoPll},
|
clocks::source::{ArmPll, ClockSource, IoPll},
|
||||||
clocks::Clocks,
|
clocks::Clocks,
|
||||||
print, println, stdio,
|
println, stdio,
|
||||||
mpcore,
|
mpcore,
|
||||||
gic,
|
gic,
|
||||||
smoltcp::{
|
smoltcp::{
|
||||||
|
@ -140,17 +140,6 @@ pub fn main_core0() {
|
||||||
clocks.cpu_1x()
|
clocks.cpu_1x()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut flash = zynq::flash::Flash::flash(200_000_000).linear_addressing_mode();
|
|
||||||
let flash_ram: &[u8] = unsafe { core::slice::from_raw_parts(flash.ptr(), flash.size()) };
|
|
||||||
for i in 0..=1 {
|
|
||||||
print!("Flash {}:", i);
|
|
||||||
for b in &flash_ram[(i * 16 * 1024 * 1024)..][..128] {
|
|
||||||
print!(" {:02X}", *b);
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
}
|
|
||||||
let _flash = flash.stop();
|
|
||||||
|
|
||||||
let timer = libboard_zynq::timer::GlobalTimer::start();
|
let timer = libboard_zynq::timer::GlobalTimer::start();
|
||||||
|
|
||||||
let mut ddr = zynq::ddr::DdrRam::ddrram();
|
let mut ddr = zynq::ddr::DdrRam::ddrram();
|
||||||
|
@ -158,45 +147,6 @@ pub fn main_core0() {
|
||||||
ddr.memtest();
|
ddr.memtest();
|
||||||
ram::init_alloc_ddr(&mut ddr);
|
ram::init_alloc_ddr(&mut ddr);
|
||||||
|
|
||||||
#[cfg(dev)]
|
|
||||||
for i in 0..=1 {
|
|
||||||
let mut flash_io = flash.manual_mode(i);
|
|
||||||
// println!("rdcr={:02X}", flash_io.rdcr());
|
|
||||||
print!("Flash {} ID:", i);
|
|
||||||
for b in flash_io.rdid() {
|
|
||||||
print!(" {:02X}", b);
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
print!("Flash {} I/O:", i);
|
|
||||||
for o in 0..8 {
|
|
||||||
const CHUNK: u32 = 8;
|
|
||||||
for b in flash_io.read(CHUNK * o, CHUNK as usize) {
|
|
||||||
print!(" {:02X}", b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
|
|
||||||
flash_io.dump("Read cr1", 0x35);
|
|
||||||
flash_io.dump("Read Autoboot", 0x14);
|
|
||||||
flash_io.dump("Read Bank", 0x16);
|
|
||||||
flash_io.dump("DLP Bank", 0x16);
|
|
||||||
flash_io.dump("Read ESig", 0xAB);
|
|
||||||
flash_io.dump("OTP Read", 0x4B);
|
|
||||||
flash_io.dump("DYB Read", 0xE0);
|
|
||||||
flash_io.dump("PPB Read", 0xE2);
|
|
||||||
flash_io.dump("ASP Read", 0x2B);
|
|
||||||
flash_io.dump("Password Read", 0xE7);
|
|
||||||
|
|
||||||
flash_io.write_enabled(|flash_io| {
|
|
||||||
flash_io.erase(0);
|
|
||||||
});
|
|
||||||
flash_io.write_enabled(|flash_io| {
|
|
||||||
flash_io.program(0, [0x23054223; 0x100 >> 2].iter().cloned());
|
|
||||||
});
|
|
||||||
|
|
||||||
flash = flash_io.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
boot::Core1::start(false);
|
boot::Core1::start(false);
|
||||||
|
|
||||||
let core1_req = unsafe { &mut CORE1_REQ.0 };
|
let core1_req = unsafe { &mut CORE1_REQ.0 };
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
pub trait BytesTransferExt: Sized {
|
|
||||||
// Turn u32 into u8
|
|
||||||
fn bytes_transfer(self) -> BytesTransfer<Self>
|
|
||||||
where
|
|
||||||
Self: Iterator<Item = u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Iterator<Item = u32>> BytesTransferExt for I {
|
|
||||||
// Turn u32 into u8
|
|
||||||
fn bytes_transfer(self) -> BytesTransfer<Self> {
|
|
||||||
BytesTransfer {
|
|
||||||
iter: self,
|
|
||||||
shift: 0,
|
|
||||||
word: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BytesTransfer<I: Iterator<Item = u32> + Sized> {
|
|
||||||
iter: I,
|
|
||||||
shift: u8,
|
|
||||||
word: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Iterator<Item = u32> + Sized> Iterator for BytesTransfer<I> {
|
|
||||||
type Item = u8;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<u8> {
|
|
||||||
if self.shift > 0 {
|
|
||||||
self.shift -= 8;
|
|
||||||
Some((self.word >> self.shift) as u8)
|
|
||||||
} else {
|
|
||||||
self.iter.next()
|
|
||||||
.and_then(|word| {
|
|
||||||
self.shift = 32;
|
|
||||||
self.word = word;
|
|
||||||
self.next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,503 +0,0 @@
|
||||||
//! Quad-SPI Flash Controller
|
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use libregister::{RegisterR, RegisterW, RegisterRW};
|
|
||||||
use crate::{print, println};
|
|
||||||
use super::slcr;
|
|
||||||
use super::clocks::source::{IoPll, ClockSource};
|
|
||||||
|
|
||||||
mod regs;
|
|
||||||
mod bytes;
|
|
||||||
pub use bytes::{BytesTransferExt, BytesTransfer};
|
|
||||||
mod spi_flash_register;
|
|
||||||
use spi_flash_register::*;
|
|
||||||
mod transfer;
|
|
||||||
use transfer::Transfer;
|
|
||||||
|
|
||||||
const FLASH_BAUD_RATE: u32 = 50_000_000;
|
|
||||||
/// 16 MB
|
|
||||||
pub const SINGLE_CAPACITY: u32 = 0x1000000;
|
|
||||||
pub const SECTOR_SIZE: u32 = 0x10000;
|
|
||||||
pub const PAGE_SIZE: u32 = 0x100;
|
|
||||||
|
|
||||||
/// Instruction: Read Identification
|
|
||||||
const INST_RDID: u8 = 0x9F;
|
|
||||||
/// Instruction: Read
|
|
||||||
const INST_READ: u8 = 0x03;
|
|
||||||
/// Instruction: Quad I/O Fast Read
|
|
||||||
const INST_4IO_FAST_READ: u8 = 0xEB;
|
|
||||||
/// Instruction: Write Disable
|
|
||||||
const INST_WRDI: u8 = 0x04;
|
|
||||||
/// Instruction: Write Enable
|
|
||||||
const INST_WREN: u8 = 0x06;
|
|
||||||
/// Instruction: Program page
|
|
||||||
const INST_PP: u8 = 0x02;
|
|
||||||
/// Instruction: Erase 4K Block
|
|
||||||
const INST_BE_4K: u8 = 0x20;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum SpiWord {
|
|
||||||
W8(u8),
|
|
||||||
W16(u16),
|
|
||||||
W24(u32),
|
|
||||||
W32(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for SpiWord {
|
|
||||||
fn from(x: u8) -> Self {
|
|
||||||
SpiWord::W8(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for SpiWord {
|
|
||||||
fn from(x: u16) -> Self {
|
|
||||||
SpiWord::W16(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for SpiWord {
|
|
||||||
fn from(x: u32) -> Self {
|
|
||||||
SpiWord::W32(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Memory-mapped mode
|
|
||||||
pub struct LinearAddressing;
|
|
||||||
/// Manual I/O mode
|
|
||||||
pub struct Manual;
|
|
||||||
|
|
||||||
/// Flash Interface Driver
|
|
||||||
///
|
|
||||||
/// For 2x Spansion S25FL128SAGMFIR01
|
|
||||||
pub struct Flash<MODE> {
|
|
||||||
regs: &'static mut regs::RegisterBlock,
|
|
||||||
_mode: PhantomData<MODE>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<MODE> Flash<MODE> {
|
|
||||||
fn transition<TO>(self) -> Flash<TO> {
|
|
||||||
Flash {
|
|
||||||
regs: self.regs,
|
|
||||||
_mode: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable_interrupts(&mut self) {
|
|
||||||
self.regs.intr_dis.write(
|
|
||||||
regs::IntrDis::zeroed()
|
|
||||||
.rx_overflow(true)
|
|
||||||
.tx_fifo_not_full(true)
|
|
||||||
.tx_fifo_full(true)
|
|
||||||
.rx_fifo_not_empty(true)
|
|
||||||
.rx_fifo_full(true)
|
|
||||||
.tx_fifo_underflow(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_rx_fifo(&self) {
|
|
||||||
while self.regs.intr_status.read().rx_fifo_not_empty() {
|
|
||||||
let _ = self.regs.rx_data.read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_interrupt_status(&mut self) {
|
|
||||||
self.regs.intr_status.write(
|
|
||||||
regs::IntrStatus::zeroed()
|
|
||||||
.rx_overflow(true)
|
|
||||||
.tx_fifo_underflow(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_tx_fifo_flush(&mut self) {
|
|
||||||
self.regs.config.modify(|_, w| w.man_start_com(true));
|
|
||||||
while !self.regs.intr_status.read().tx_fifo_not_full() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Flash<()> {
|
|
||||||
pub fn flash(clock: u32) -> Self {
|
|
||||||
Self::enable_clocks(clock);
|
|
||||||
Self::setup_signals();
|
|
||||||
Self::reset();
|
|
||||||
|
|
||||||
let regs = regs::RegisterBlock::qspi();
|
|
||||||
let mut flash = Flash { regs, _mode: PhantomData };
|
|
||||||
flash.configure((FLASH_BAUD_RATE - 1 + clock) / FLASH_BAUD_RATE);
|
|
||||||
flash
|
|
||||||
}
|
|
||||||
|
|
||||||
/// typical: `200_000_000` Hz
|
|
||||||
fn enable_clocks(clock: u32) {
|
|
||||||
let io_pll = IoPll::freq();
|
|
||||||
let divisor = ((clock - 1 + io_pll) / clock)
|
|
||||||
.max(1).min(63) as u8;
|
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
slcr.lqspi_clk_ctrl.write(
|
|
||||||
slcr::LqspiClkCtrl::zeroed()
|
|
||||||
.src_sel(slcr::PllSource::IoPll)
|
|
||||||
.divisor(divisor)
|
|
||||||
.clkact(true)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_signals() {
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
// 1. Configure MIO pin 1 for chip select 0 output.
|
|
||||||
slcr.mio_pin_01.write(
|
|
||||||
slcr::MioPin01::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Configure MIO pins 2 through 5 for I/O.
|
|
||||||
slcr.mio_pin_02.write(
|
|
||||||
slcr::MioPin02::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_03.write(
|
|
||||||
slcr::MioPin03::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_04.write(
|
|
||||||
slcr::MioPin04::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_05.write(
|
|
||||||
slcr::MioPin05::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Configure MIO pin 6 for serial clock 0 output.
|
|
||||||
slcr.mio_pin_06.write(
|
|
||||||
slcr::MioPin06::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Option: Add Second Device Chip Select
|
|
||||||
// 4. Configure MIO pin 0 for chip select 1 output.
|
|
||||||
slcr.mio_pin_00.write(
|
|
||||||
slcr::MioPin00::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Option: Add Second Serial Clock
|
|
||||||
// 5. Configure MIO pin 9 for serial clock 1 output.
|
|
||||||
slcr.mio_pin_09.write(
|
|
||||||
slcr::MioPin09::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Option: Add 4-bit Data
|
|
||||||
// 6. Configure MIO pins 10 through 13 for I/O.
|
|
||||||
slcr.mio_pin_10.write(
|
|
||||||
slcr::MioPin10::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_11.write(
|
|
||||||
slcr::MioPin11::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_12.write(
|
|
||||||
slcr::MioPin12::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_13.write(
|
|
||||||
slcr::MioPin13::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Option: Add Feedback Output Clock
|
|
||||||
// 7. Configure MIO pin 8 for feedback clock.
|
|
||||||
slcr.mio_pin_08.write(
|
|
||||||
slcr::MioPin08::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset() {
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
slcr.lqspi_rst_ctrl.write(
|
|
||||||
slcr::LqspiRstCtrl::zeroed()
|
|
||||||
.ref_rst(true)
|
|
||||||
.cpu1x_rst(true)
|
|
||||||
);
|
|
||||||
slcr.lqspi_rst_ctrl.write(
|
|
||||||
slcr::LqspiRstCtrl::zeroed()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure(&mut self, divider: u32) {
|
|
||||||
// Disable
|
|
||||||
self.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
);
|
|
||||||
self.disable_interrupts();
|
|
||||||
self.regs.lqspi_cfg.write(
|
|
||||||
regs::LqspiCfg::zeroed()
|
|
||||||
);
|
|
||||||
self.clear_rx_fifo();
|
|
||||||
self.clear_interrupt_status();
|
|
||||||
|
|
||||||
// for a baud_rate_div=1 LPBK_DLY_ADJ would be required
|
|
||||||
let mut baud_rate_div = 2u32;
|
|
||||||
while baud_rate_div < 7 && 2u32.pow(1 + baud_rate_div) < divider {
|
|
||||||
baud_rate_div += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.regs.config.write(regs::Config::zeroed()
|
|
||||||
.baud_rate_div(baud_rate_div as u8)
|
|
||||||
.mode_sel(true)
|
|
||||||
.leg_flsh(true)
|
|
||||||
.holdb_dr(true)
|
|
||||||
// 32 bits TX FIFO width
|
|
||||||
.fifo_width(0b11)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initialize RX/TX pipes thresholds
|
|
||||||
unsafe {
|
|
||||||
self.regs.rx_thres.write(1);
|
|
||||||
self.regs.tx_thres.write(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn linear_addressing_mode(self) -> Flash<LinearAddressing> {
|
|
||||||
// Set manual start enable to auto mode.
|
|
||||||
// Assert the chip select.
|
|
||||||
self.regs.config.modify(|_, w| w
|
|
||||||
.man_start_en(false)
|
|
||||||
.pcs(false)
|
|
||||||
.manual_cs(false)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
|
|
||||||
// Quad I/O Fast Read
|
|
||||||
.inst_code(INST_4IO_FAST_READ)
|
|
||||||
.dummy_mask(0x2)
|
|
||||||
.mode_en(false)
|
|
||||||
.mode_bits(0xFF)
|
|
||||||
// 2 devices
|
|
||||||
.two_mem(true)
|
|
||||||
.u_page(false)
|
|
||||||
// Quad SPI mode
|
|
||||||
.lq_mode(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
.spi_en(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn manual_mode(self, chip_index: usize) -> Flash<Manual> {
|
|
||||||
self.regs.config.modify(|_, w| w
|
|
||||||
.man_start_en(true)
|
|
||||||
.manual_cs(true)
|
|
||||||
.endian(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
|
|
||||||
// Quad I/O Fast Read
|
|
||||||
.inst_code(INST_READ)
|
|
||||||
.dummy_mask(0x2)
|
|
||||||
.mode_en(false)
|
|
||||||
.mode_bits(0xFF)
|
|
||||||
// 2 devices
|
|
||||||
.two_mem(true)
|
|
||||||
.u_page(chip_index != 0)
|
|
||||||
// Quad SPI mode
|
|
||||||
.lq_mode(false)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Flash<LinearAddressing> {
|
|
||||||
/// Stop linear addressing mode
|
|
||||||
pub fn stop(self) -> Flash<()> {
|
|
||||||
self.regs.enable.modify(|_, w| w.spi_en(false));
|
|
||||||
// De-assert chip select.
|
|
||||||
self.regs.config.modify(|_, w| w.pcs(true));
|
|
||||||
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr<T>(&mut self) -> *mut T {
|
|
||||||
0xFC00_0000 as *mut _
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
2 * (SINGLE_CAPACITY as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Flash<Manual> {
|
|
||||||
pub fn stop(self) -> Flash<()> {
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_reg<R: SpiFlashRegister>(&mut self) -> R {
|
|
||||||
let args = Some(R::inst_code());
|
|
||||||
let transfer = self.transfer(args.into_iter(), 2)
|
|
||||||
.bytes_transfer();
|
|
||||||
R::new(transfer.skip(1).next().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_reg_until<R, F, A>(&mut self, f: F) -> A
|
|
||||||
where
|
|
||||||
R: SpiFlashRegister,
|
|
||||||
F: Fn(R) -> Option<A>,
|
|
||||||
{
|
|
||||||
let mut result = None;
|
|
||||||
while result.is_none() {
|
|
||||||
let args = Some(R::inst_code());
|
|
||||||
for b in self.transfer(args.into_iter(), 32)
|
|
||||||
.bytes_transfer().skip(1) {
|
|
||||||
result = f(R::new(b));
|
|
||||||
|
|
||||||
if result.is_none() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status Register-1 remains `0x00` immediately after invoking a command.
|
|
||||||
fn wait_while_sr1_zeroed(&mut self) -> SR1 {
|
|
||||||
self.read_reg_until::<SR1, _, SR1>(|sr1|
|
|
||||||
if sr1.is_zeroed() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(sr1)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read Identification
|
|
||||||
pub fn rdid(&mut self) -> core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>> {
|
|
||||||
let args = Some((INST_RDID as u32) << 24);
|
|
||||||
self.transfer(args.into_iter(), 0x44)
|
|
||||||
.bytes_transfer().skip(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read flash data
|
|
||||||
pub fn read(&mut self, offset: u32, len: usize
|
|
||||||
) -> core::iter::Take<core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>>>
|
|
||||||
{
|
|
||||||
let args = Some(((INST_READ as u32) << 24) | (offset as u32));
|
|
||||||
self.transfer(args.into_iter(), len + 6)
|
|
||||||
.bytes_transfer().skip(6).take(len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn erase(&mut self, offset: u32) {
|
|
||||||
let args = Some(((INST_BE_4K as u32) << 24) | (offset as u32));
|
|
||||||
self.transfer(args.into_iter(), 4);
|
|
||||||
|
|
||||||
let sr1 = self.wait_while_sr1_zeroed();
|
|
||||||
|
|
||||||
if sr1.e_err() {
|
|
||||||
error!("E_ERR");
|
|
||||||
} else if sr1.p_err() {
|
|
||||||
error!("P_ERR");
|
|
||||||
} else if sr1.wip() {
|
|
||||||
info!("Erase in progress");
|
|
||||||
while self.read_reg::<SR1>().wip() {
|
|
||||||
print!(".");
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
} else {
|
|
||||||
warn!("erased? sr1={:02X}", sr1.inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn program<I: Iterator<Item=u32>>(&mut self, offset: u32, data: I) {
|
|
||||||
{
|
|
||||||
let len = 4 + 4 * data.size_hint().0;
|
|
||||||
let args = Some(SpiWord::W32(((INST_PP as u32) << 24) | (offset as u32))).into_iter()
|
|
||||||
.chain(data.map(SpiWord::W32));
|
|
||||||
self.transfer(args, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let sr1 = self.wait_while_sr1_zeroed();
|
|
||||||
let sr1 = self.read_reg::<SR1>();
|
|
||||||
|
|
||||||
if sr1.e_err() {
|
|
||||||
error!("E_ERR");
|
|
||||||
} else if sr1.p_err() {
|
|
||||||
error!("P_ERR");
|
|
||||||
} else if sr1.wip() {
|
|
||||||
info!("Program in progress");
|
|
||||||
while self.read_reg::<SR1>().wip() {
|
|
||||||
print!(".");
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
} else {
|
|
||||||
warn!("programmed? sr1={:02X}", sr1.inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_enabled<F: Fn(&mut Self) -> R, R>(&mut self, f: F) -> R {
|
|
||||||
// Write Enable
|
|
||||||
let args = Some(INST_WREN);
|
|
||||||
self.transfer(args.into_iter(), 1);
|
|
||||||
self.regs.gpio.modify(|_, w| w.wp_n(true));
|
|
||||||
let sr1 = self.wait_while_sr1_zeroed();
|
|
||||||
if !sr1.wel() {
|
|
||||||
panic!("Cannot write-enable flash");
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = f(self);
|
|
||||||
|
|
||||||
// Write Disable
|
|
||||||
let args = Some(INST_WRDI);
|
|
||||||
self.transfer(args.into_iter(), 1);
|
|
||||||
self.regs.gpio.modify(|_, w| w.wp_n(false));
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transfer<'s: 't, 't, Args, W>(&'s mut self, args: Args, len: usize) -> Transfer<'t, Args, W>
|
|
||||||
where
|
|
||||||
Args: Iterator<Item = W>,
|
|
||||||
W: Into<SpiWord>,
|
|
||||||
{
|
|
||||||
Transfer::new(self, args, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dump(&mut self, label: &'_ str, inst_code: u8) {
|
|
||||||
print!("{}:", label);
|
|
||||||
|
|
||||||
let args = Some(u32::from(inst_code) << 24);
|
|
||||||
for b in self.transfer(args.into_iter(), 32).bytes_transfer() {
|
|
||||||
print!(" {:02X}", b);
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
use volatile_register::{RO, WO, RW};
|
|
||||||
|
|
||||||
use libregister::{register, register_at, register_bit, register_bits};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct RegisterBlock {
|
|
||||||
pub config: Config,
|
|
||||||
pub intr_status: IntrStatus,
|
|
||||||
pub intr_en: IntrEn,
|
|
||||||
pub intr_dis: IntrDis,
|
|
||||||
pub intr_mask: RO<u32>,
|
|
||||||
pub enable: Enable,
|
|
||||||
pub delay: RW<u32>,
|
|
||||||
pub txd0: WO<u32>,
|
|
||||||
pub rx_data: RO<u32>,
|
|
||||||
pub slave_idle_count: RW<u32>,
|
|
||||||
pub tx_thres: RW<u32>,
|
|
||||||
pub rx_thres: RW<u32>,
|
|
||||||
pub gpio: QspiGpio,
|
|
||||||
pub _unused1: RO<u32>,
|
|
||||||
pub lpbk_dly_adj: RW<u32>,
|
|
||||||
pub _unused2: [RO<u32>; 17],
|
|
||||||
pub txd1: WO<u32>,
|
|
||||||
pub txd2: WO<u32>,
|
|
||||||
pub txd3: WO<u32>,
|
|
||||||
pub _unused3: [RO<u32>; 5],
|
|
||||||
pub lqspi_cfg: LqspiCfg,
|
|
||||||
pub lqspi_sts: RW<u32>,
|
|
||||||
pub _unused4: [RO<u32>; 21],
|
|
||||||
pub mod_id: RW<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const BASE_ADDRESS: u32 = 0xE000D000;
|
|
||||||
|
|
||||||
register_at!(RegisterBlock, 0xE000D000, qspi);
|
|
||||||
|
|
||||||
register!(config, Config, RW, u32);
|
|
||||||
register_bit!(config,
|
|
||||||
/// Enables master mode
|
|
||||||
mode_sel, 0);
|
|
||||||
register_bit!(config,
|
|
||||||
/// Clock polarity low/high
|
|
||||||
clk_pol, 1);
|
|
||||||
register_bit!(config,
|
|
||||||
/// Clock phase
|
|
||||||
clk_ph, 2);
|
|
||||||
register_bits!(config,
|
|
||||||
/// divider = 2 ** (1 + baud_rate_div)
|
|
||||||
baud_rate_div, u8, 3, 5);
|
|
||||||
register_bits!(config,
|
|
||||||
/// Must be set to 0b11
|
|
||||||
fifo_width, u8, 6, 7);
|
|
||||||
register_bit!(config,
|
|
||||||
/// Must be 0
|
|
||||||
ref_clk, 8);
|
|
||||||
register_bit!(config,
|
|
||||||
/// Peripheral Chip Select Line
|
|
||||||
pcs, 10);
|
|
||||||
register_bit!(config,
|
|
||||||
/// false: auto mode, true: manual CS mode
|
|
||||||
manual_cs, 14);
|
|
||||||
register_bit!(config,
|
|
||||||
/// false: auto mode, true: enables manual start enable
|
|
||||||
man_start_en, 15);
|
|
||||||
register_bit!(config,
|
|
||||||
/// false: auto mode, true: enables manual start command
|
|
||||||
man_start_com, 16);
|
|
||||||
register_bit!(config, holdb_dr, 19);
|
|
||||||
register_bit!(config,
|
|
||||||
/// false: little, true: endian
|
|
||||||
endian, 26);
|
|
||||||
register_bit!(config,
|
|
||||||
/// false: legacy SPI mode, true: Flash memory interface mode
|
|
||||||
leg_flsh, 31);
|
|
||||||
|
|
||||||
register!(intr_status, IntrStatus, RW, u32);
|
|
||||||
register_bit!(intr_status, rx_overflow, 0);
|
|
||||||
register_bit!(intr_status,
|
|
||||||
/// < tx_thres
|
|
||||||
tx_fifo_not_full, 2);
|
|
||||||
register_bit!(intr_status, tx_fifo_full, 3);
|
|
||||||
register_bit!(intr_status,
|
|
||||||
/// >= rx_thres
|
|
||||||
rx_fifo_not_empty, 4);
|
|
||||||
register_bit!(intr_status, rx_fifo_full, 5);
|
|
||||||
register_bit!(intr_status, tx_fifo_underflow, 6);
|
|
||||||
|
|
||||||
register!(intr_en, IntrEn, WO, u32);
|
|
||||||
register_bit!(intr_en, rx_overflow, 0);
|
|
||||||
register_bit!(intr_en, tx_fifo_not_full, 2);
|
|
||||||
register_bit!(intr_en, tx_fifo_full, 3);
|
|
||||||
register_bit!(intr_en, rx_fifo_not_empty, 4);
|
|
||||||
register_bit!(intr_en, rx_fifo_full, 5);
|
|
||||||
register_bit!(intr_en, tx_fifo_underflow, 6);
|
|
||||||
|
|
||||||
register!(intr_dis, IntrDis, WO, u32);
|
|
||||||
register_bit!(intr_dis, rx_overflow, 0);
|
|
||||||
register_bit!(intr_dis, tx_fifo_not_full, 2);
|
|
||||||
register_bit!(intr_dis, tx_fifo_full, 3);
|
|
||||||
register_bit!(intr_dis, rx_fifo_not_empty, 4);
|
|
||||||
register_bit!(intr_dis, rx_fifo_full, 5);
|
|
||||||
register_bit!(intr_dis, tx_fifo_underflow, 6);
|
|
||||||
|
|
||||||
register!(enable, Enable, RW, u32);
|
|
||||||
register_bit!(enable, spi_en, 0);
|
|
||||||
|
|
||||||
// named to avoid confusion with normal gpio
|
|
||||||
register!(qspi_gpio, QspiGpio, RW, u32);
|
|
||||||
register_bit!(qspi_gpio,
|
|
||||||
/// Write protect pin (inverted)
|
|
||||||
wp_n, 0);
|
|
||||||
|
|
||||||
register!(lqspi_cfg, LqspiCfg, RW, u32);
|
|
||||||
register_bits!(lqspi_cfg, inst_code, u8, 0, 7);
|
|
||||||
register_bits!(lqspi_cfg, dummy_mask, u8, 8, 10);
|
|
||||||
register_bits!(lqspi_cfg, mode_bits, u8, 16, 23);
|
|
||||||
register_bit!(lqspi_cfg, mode_on, 24);
|
|
||||||
register_bit!(lqspi_cfg, mode_en, 25);
|
|
||||||
register_bit!(lqspi_cfg, u_page, 28);
|
|
||||||
register_bit!(lqspi_cfg, sep_bus, 29);
|
|
||||||
register_bit!(lqspi_cfg, two_mem, 30);
|
|
||||||
register_bit!(lqspi_cfg, lq_mode, 31);
|
|
|
@ -1,62 +0,0 @@
|
||||||
use bit_field::BitField;
|
|
||||||
|
|
||||||
pub trait SpiFlashRegister {
|
|
||||||
fn inst_code() -> u8;
|
|
||||||
fn new(src: u8) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! u8_register {
|
|
||||||
($name: ident, $doc: tt, $inst_code: expr) => {
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[doc=$doc]
|
|
||||||
pub struct $name {
|
|
||||||
pub inner: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpiFlashRegister for $name {
|
|
||||||
fn inst_code() -> u8 {
|
|
||||||
$inst_code
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(src: u8) -> Self {
|
|
||||||
$name {
|
|
||||||
inner: src,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $name {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn is_zeroed(&self) -> bool {
|
|
||||||
self.inner == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
u8_register!(CR, "Configuration Register", 0x35);
|
|
||||||
u8_register!(SR1, "Status Register-1", 0x05);
|
|
||||||
impl SR1 {
|
|
||||||
/// Write In Progress
|
|
||||||
pub fn wip(&self) -> bool {
|
|
||||||
self.inner.get_bit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write Enable Latch
|
|
||||||
pub fn wel(&self) -> bool {
|
|
||||||
self.inner.get_bit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Erase Error Occurred
|
|
||||||
pub fn e_err(&self) -> bool {
|
|
||||||
self.inner.get_bit(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Programming Error Occurred
|
|
||||||
pub fn p_err(&self) -> bool {
|
|
||||||
self.inner.get_bit(6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8_register!(SR2, "Status Register-2", 0x07);
|
|
||||||
u8_register!(BA, "Bank Address Register", 0xB9);
|
|
|
@ -1,125 +0,0 @@
|
||||||
use libregister::{RegisterR, RegisterW, RegisterRW};
|
|
||||||
use super::regs;
|
|
||||||
use super::{SpiWord, Flash, Manual};
|
|
||||||
|
|
||||||
pub struct Transfer<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> {
|
|
||||||
flash: &'a mut Flash<Manual>,
|
|
||||||
args: Args,
|
|
||||||
sent: usize,
|
|
||||||
received: usize,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Transfer<'a, Args, W> {
|
|
||||||
pub fn new(flash: &'a mut Flash<Manual>, args: Args, len: usize) -> Self {
|
|
||||||
flash.regs.config.modify(|_, w| w.pcs(false));
|
|
||||||
flash.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
.spi_en(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut xfer = Transfer {
|
|
||||||
flash,
|
|
||||||
args,
|
|
||||||
sent: 0,
|
|
||||||
received: 0,
|
|
||||||
len,
|
|
||||||
};
|
|
||||||
xfer.fill_tx_fifo();
|
|
||||||
xfer.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
|
||||||
xfer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_tx_fifo(&mut self) {
|
|
||||||
while self.sent < self.len && !self.flash.regs.intr_status.read().tx_fifo_full() {
|
|
||||||
let arg = self.args.next()
|
|
||||||
.map(|n| n.into())
|
|
||||||
.unwrap_or(SpiWord::W32(0));
|
|
||||||
match arg {
|
|
||||||
SpiWord::W32(w) => {
|
|
||||||
// println!("txd0 {:08X}", w);
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd0.write(w);
|
|
||||||
}
|
|
||||||
self.sent += 4;
|
|
||||||
}
|
|
||||||
// Only txd0 can be used without flushing
|
|
||||||
_ => {
|
|
||||||
if !self.flash.regs.intr_status.read().tx_fifo_not_full() {
|
|
||||||
// Flush if necessary
|
|
||||||
self.flash.wait_tx_fifo_flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
match arg {
|
|
||||||
SpiWord::W8(w) => {
|
|
||||||
// println!("txd1 {:02X}", w);
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd1.write(u32::from(w) << 24);
|
|
||||||
}
|
|
||||||
self.sent += 1;
|
|
||||||
}
|
|
||||||
SpiWord::W16(w) => {
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd2.write(u32::from(w) << 16);
|
|
||||||
}
|
|
||||||
self.sent += 2;
|
|
||||||
}
|
|
||||||
SpiWord::W24(w) => {
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd3.write(w << 8);
|
|
||||||
}
|
|
||||||
self.sent += 3;
|
|
||||||
}
|
|
||||||
SpiWord::W32(_) => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.flash.wait_tx_fifo_flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_read(&mut self) -> bool {
|
|
||||||
self.flash.regs.intr_status.read().rx_fifo_not_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&mut self) -> u32 {
|
|
||||||
let rx = self.flash.regs.rx_data.read();
|
|
||||||
self.received += 4;
|
|
||||||
rx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Drop for Transfer<'a, Args, W> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Discard remaining rx_data
|
|
||||||
while self.can_read() {
|
|
||||||
self.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop
|
|
||||||
self.flash.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
.spi_en(false)
|
|
||||||
);
|
|
||||||
self.flash.regs.config.modify(|_, w| w
|
|
||||||
.pcs(true)
|
|
||||||
.man_start_com(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Iterator for Transfer<'a, Args, W> {
|
|
||||||
type Item = u32;
|
|
||||||
|
|
||||||
fn next<'s>(&'s mut self) -> Option<u32> {
|
|
||||||
if self.received >= self.len {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fill_tx_fifo();
|
|
||||||
|
|
||||||
while !self.can_read() {}
|
|
||||||
Some(self.read())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,6 @@ pub mod axi_gp;
|
||||||
pub mod ddr;
|
pub mod ddr;
|
||||||
pub mod mpcore;
|
pub mod mpcore;
|
||||||
pub mod gic;
|
pub mod gic;
|
||||||
pub mod flash;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod sdio;
|
pub mod sdio;
|
||||||
|
|
Loading…
Reference in New Issue