matcha/tock: add hal support spi_host Change-Id: I7404bb098aa245957670fc7e7147f4620d8169b8
diff --git a/config/src/lib.rs b/config/src/lib.rs index 9279cdd..f365b17 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs
@@ -42,5 +42,8 @@ pub const SMC_CTRL_BASE_ADDRESS: u32 = 0x5402_0000; // TOP_MATCHA_SMC_CTRL_BASE_ADDR +pub const SPI_HOST0_BASE_ADDRESS: u32 = 0x4030_0000; // TOP_MATCHA_SPI_HOST0_BASE_ADDR +pub const SPI_HOST0_SPI_EVENT_IRQ: u32 = 132; // kTopMatchaPlicIrqIdSpiHost0SpiEvent + pub const UART0_BASE_ADDRESS: u32 = 0x40000000; // TOP_MATCHA_UART0_BASE_ADDR pub const UART0_BAUDRATE: u32 = 115200;
diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 5161ab2..e5a6143 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs
@@ -11,6 +11,7 @@ pub mod plic_hal; pub mod rv_core_ibex_hal; pub mod smc_ctrl_hal; +pub mod spi_host_hal; pub mod timer_hal; pub mod uart_hal;
diff --git a/hal/src/spi_host_hal.rs b/hal/src/spi_host_hal.rs new file mode 100644 index 0000000..57a05de --- /dev/null +++ b/hal/src/spi_host_hal.rs
@@ -0,0 +1,403 @@ +// Generated register constants for spi_host. +// This file is licensed under either of: +// Apache License, Version 2.0 (LICENSE-APACHE <http://www.apache.org/licenses/LICENSE-2.0>) +// MIT License (LICENSE-MIT <http://opensource.org/licenses/MIT>) + +// Build date: 2023-02-06T23:02:43.552609 + +// Original reference file: ./hw/opentitan-upstream/hw/ip/spi_host/data/spi_host.hjson +use kernel::common::registers::{ + register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly +}; +use core::cell::Cell; +use kernel::common::cells::{TakeCell, OptionalCell}; +use kernel::common::StaticRef; +use kernel::hil::spi::SpiMasterClient; + +// The number of active-low chip select (cs_n) lines to create. +pub const SPI_HOST_PARAM_NUM_C_S: u32 = 1; +// The size of the Tx FIFO (in words) +pub const SPI_HOST_PARAM_TX_DEPTH: u32 = 72; +// The size of the Rx FIFO (in words) +pub const SPI_HOST_PARAM_RX_DEPTH: u32 = 64; +// The size of the Cmd FIFO (one segment descriptor per entry) +pub const SPI_HOST_PARAM_CMD_DEPTH: u32 = 4; +// Number of alerts +pub const SPI_HOST_PARAM_NUM_ALERTS: u32 = 1; +// Register width +pub const SPI_HOST_PARAM_REG_WIDTH: u32 = 32; + +pub const SPI_HOST0_REGISTERS: StaticRef<SpiHostRegisters> = + unsafe { StaticRef::new(matcha_config::SPI_HOST0_BASE_ADDRESS as *const SpiHostRegisters) }; +pub const SPI_HOST0_REGISTERS8: StaticRef<SpiHostRegisters8> = + unsafe { StaticRef::new(matcha_config::SPI_HOST0_BASE_ADDRESS as *const SpiHostRegisters8) }; +pub static mut SPI_HOST0: SpiHw = SpiHw::new(SPI_HOST0_REGISTERS, SPI_HOST0_REGISTERS8 /*, matcha_config::CHIP_PERIPH_FREQ*/); + +register_structs! { + pub SpiHostRegisters { + // Interrupt State Register + (0x0000 => pub(crate) intr_state: ReadWrite<u32, INTR::Register>), + // Interrupt Enable Register + (0x0004 => pub(crate) intr_enable: ReadWrite<u32, INTR::Register>), + // Interrupt Test Register + (0x0008 => pub(crate) intr_test: ReadWrite<u32, INTR::Register>), + // Alert Test Register + (0x000c => pub(crate) alert_test: ReadWrite<u32, ALERT_TEST::Register>), + // Control register + (0x0010 => pub(crate) control: ReadWrite<u32, CONTROL::Register>), + // Status register + (0x0014 => pub(crate) status: ReadWrite<u32, STATUS::Register>), + // Configuration options register. + (0x0018 => pub(crate) configop: [ReadWrite<u32, CONFIGOP::Register>; 1]), + // Chip-Select ID + (0x001c => pub(crate) csid: ReadWrite<u32, CSID::Register>), + // Command Register + (0x0020 => pub(crate) command: ReadWrite<u32, COMMAND::Register>), + // Memory area: SPI Receive Data. + (0x0024 => pub(crate) rxdata: [ReadOnly<u32>; 1]), + // Memory area: SPI Transmit Data. + (0x0028 => pub(crate) txdata: [WriteOnly<u32>; 1]), + // Controls which classes of errors raise an interrupt. + (0x002c => pub(crate) error_enable: ReadWrite<u32, ERROR_ENABLE::Register>), + // Indicates that any errors that have occurred. + (0x0030 => pub(crate) error_status: ReadWrite<u32, ERROR_STATUS::Register>), + // Controls which classes of SPI events raise an interrupt. + (0x0034 => pub(crate) event_enable: ReadWrite<u32, EVENT_ENABLE::Register>), + (0x0038 => @END), + } +} + +register_structs! { + pub SpiHostRegisters8 { + // Interrupt State Register + (0x0000 => pub(crate) intr_state: ReadWrite<u32, INTR::Register>), + // Interrupt Enable Register + (0x0004 => pub(crate) intr_enable: ReadWrite<u32, INTR::Register>), + // Interrupt Test Register + (0x0008 => pub(crate) intr_test: ReadWrite<u32, INTR::Register>), + // Alert Test Register + (0x000c => pub(crate) alert_test: ReadWrite<u32, ALERT_TEST::Register>), + // Control register + (0x0010 => pub(crate) control: ReadWrite<u32, CONTROL::Register>), + // Status register + (0x0014 => pub(crate) status: ReadWrite<u32, STATUS::Register>), + // Configuration options register. + (0x0018 => pub(crate) configop: [ReadWrite<u32, CONFIGOP::Register>; 1]), + // Chip-Select ID + (0x001c => pub(crate) csid: ReadWrite<u32, CSID::Register>), + // Command Register + (0x0020 => pub(crate) command: ReadWrite<u32, COMMAND::Register>), + // Memory area: SPI Receive Data. + (0x0024 => pub(crate) rxdata: [ReadOnly<u32>; 1]), + // Memory area: SPI Transmit Data. + (0x0028 => pub(crate) txdata: [WriteOnly<u8>; 1]), + // Controls which classes of errors raise an interrupt. + (0x002c => pub(crate) error_enable: ReadWrite<u32, ERROR_ENABLE::Register>), + // Indicates that any errors that have occurred. + (0x0030 => pub(crate) error_status: ReadWrite<u32, ERROR_STATUS::Register>), + // Controls which classes of SPI events raise an interrupt. + (0x0034 => pub(crate) event_enable: ReadWrite<u32, EVENT_ENABLE::Register>), + (0x0038 => @END), + } +} + +register_bitfields![u32, + // Common Interrupt Offsets + pub(crate) INTR [ + ERROR OFFSET(0) NUMBITS(1) [], + SPI_EVENT OFFSET(1) NUMBITS(1) [] + ], + pub(crate) ALERT_TEST [ + FATAL_FAULT OFFSET(0) NUMBITS(1) [] + ], + pub(crate) CONTROL [ + RX_WATERMARK OFFSET(0) NUMBITS(8) [], + TX_WATERMARK OFFSET(8) NUMBITS(8) [], + OUTPUT_EN OFFSET(29) NUMBITS(1) [], + SW_RST OFFSET(30) NUMBITS(1) [], + SPIEN OFFSET(31) NUMBITS(1) [] + ], + pub(crate) STATUS [ + TXQD OFFSET(0) NUMBITS(8) [], + RXQD OFFSET(8) NUMBITS(8) [], + CMDQD OFFSET(16) NUMBITS(4) [], + RXWM OFFSET(20) NUMBITS(1) [], + BYTEORDER OFFSET(22) NUMBITS(1) [], + RXSTALL OFFSET(23) NUMBITS(1) [], + RXEMPTY OFFSET(24) NUMBITS(1) [], + RXFULL OFFSET(25) NUMBITS(1) [], + TXWM OFFSET(26) NUMBITS(1) [], + TXSTALL OFFSET(27) NUMBITS(1) [], + TXEMPTY OFFSET(28) NUMBITS(1) [], + TXFULL OFFSET(29) NUMBITS(1) [], + ACTIVE OFFSET(30) NUMBITS(1) [], + READY OFFSET(31) NUMBITS(1) [] + ], + pub(crate) CONFIGOP [ + CLKDIV_0 OFFSET(0) NUMBITS(16) [], + CSNIDLE_0 OFFSET(16) NUMBITS(4) [], + CSNTRAIL_0 OFFSET(20) NUMBITS(4) [], + CSNLEAD_0 OFFSET(24) NUMBITS(4) [], + FULLCYC_0 OFFSET(29) NUMBITS(1) [], + CPHA_0 OFFSET(30) NUMBITS(1) [], + CPOL_0 OFFSET(31) NUMBITS(1) [] + ], + pub(crate) CSID [ + CSID OFFSET(0) NUMBITS(32) [] + ], + pub(crate) COMMAND [ + LEN OFFSET(0) NUMBITS(9) [], + CSAAT OFFSET(9) NUMBITS(1) [], + SPEED OFFSET(10) NUMBITS(2) [], + DIRECTION OFFSET(12) NUMBITS(2) [] + ], + pub(crate) ERROR_ENABLE [ + CMDBUSY OFFSET(0) NUMBITS(1) [], + OVERFLOW OFFSET(1) NUMBITS(1) [], + UNDERFLOW OFFSET(2) NUMBITS(1) [], + CMDINVAL OFFSET(3) NUMBITS(1) [], + CSIDINVAL OFFSET(4) NUMBITS(1) [] + ], + pub(crate) ERROR_STATUS [ + CMDBUSY OFFSET(0) NUMBITS(1) [], + OVERFLOW OFFSET(1) NUMBITS(1) [], + UNDERFLOW OFFSET(2) NUMBITS(1) [], + CMDINVAL OFFSET(3) NUMBITS(1) [], + CSIDINVAL OFFSET(4) NUMBITS(1) [], + ACCESSINVAL OFFSET(5) NUMBITS(1) [] + ], + pub(crate) EVENT_ENABLE [ + RXFULL OFFSET(0) NUMBITS(1) [], + TXEMPTY OFFSET(1) NUMBITS(1) [], + RXWM OFFSET(2) NUMBITS(1) [], + TXWM OFFSET(3) NUMBITS(1) [], + READY OFFSET(4) NUMBITS(1) [], + IDLE OFFSET(5) NUMBITS(1) [] + ] +]; + +// End generated register constants for spi_host + +pub struct SpiHw { + registers: StaticRef<SpiHostRegisters>, + registers8: StaticRef<SpiHostRegisters8>, + client: OptionalCell<&'static dyn SpiMasterClient>, + chip_select: OptionalCell<u32>, + tx_buf: TakeCell<'static, [u8]>, + rx_buf: TakeCell<'static, [u8]>, + transfer_len: Cell<usize>, + busy: Cell<bool>, + hold_low: Cell<bool>, +} + +impl SpiHw { + pub const fn new(base: StaticRef<SpiHostRegisters>, base8: StaticRef<SpiHostRegisters8>) -> SpiHw { + SpiHw { + registers: base, + registers8: base8, + client: OptionalCell::empty(), + chip_select: OptionalCell::empty(), + tx_buf: TakeCell::empty(), + rx_buf: TakeCell::empty(), + transfer_len: Cell::new(0), + busy: Cell::new(false), + hold_low: Cell::new(false), + } + } + + pub fn handle_interrupt(&self) { + // dprintf!("intr_state: {:?}\r\n", self.registers.intr_state.get()); + // dprintf!("intr_enable: {:?}\r\n", self.registers.intr_enable.get()); + // Cache interrupt state, and clear the register. + let intr_state = self.registers.intr_state.extract(); + self.registers.intr_state.set(intr_state.get()); + + if intr_state.is_set(INTR::SPI_EVENT) { + // For now, only event enabled is IDLE. + self.client.map(|client| match self.tx_buf.take() { + None => (), + Some(tx_buf) => { + self.busy.set(false); + client.read_write_done(tx_buf, self.rx_buf.take(), self.transfer_len.take()); + } + }); + } + } + + fn reset(&self) { + self.registers.control.modify(CONTROL::SW_RST::SET); + let mut active: bool; + loop { + active = self.registers.status.is_set(STATUS::ACTIVE); + if !active { + break; + } + } + let mut txqd: bool; + let mut rxqd: bool; + loop { + let reg = self.registers.status.extract(); + txqd = reg.is_set(STATUS::TXQD); + rxqd = reg.is_set(STATUS::RXQD); + if !txqd && !rxqd { + break; + } + } + self.registers.control.modify(CONTROL::SW_RST::CLEAR); + } + + fn enable(&self, enable: bool) { + if enable { + self.registers.control.modify(CONTROL::SPIEN::SET); + } else { + self.registers.control.modify(CONTROL::SPIEN::CLEAR); + } + } + + fn rx_fifo_wait(&self) { + loop { + let rxqd = self.registers.status.read(STATUS::RXQD); + if rxqd != 0 { + break; + } + } + } + + fn rx_fifo_read32(&self) -> u32 { + self.registers.rxdata[0].get() + } + + fn tx_fifo_wait(&self) { + loop { + let txqd = self.registers.status.read(STATUS::TXQD); + if txqd != SPI_HOST_PARAM_TX_DEPTH { + break; + } + } + } + + fn tx_fifo_write8(&self, val: u8) { + self.tx_fifo_wait(); + self.registers8.txdata[0].set(val); + } + + fn tx_fifo_write32(&self, val: u32) { + self.tx_fifo_wait(); + self.registers.txdata[0].set(val); + } + + fn fifo_write(&self, buf: &[u8], len: usize) { + // dprintf!("start writing fifo {:?}\r\n", len); + let single_write_count = len % 4; + // for i in 0..single_write_count { + // self.tx_fifo_write8(buf[i]); + // } + // for i in (single_write_count..len).step_by(4) { + // let val: u32 = u32::from_le_bytes([buf[i], buf[i+1], buf[i+2], buf[i+3]]); + // self.tx_fifo_write32(val); + // } + for i in (0..len-single_write_count).step_by(4) { + let val: u32 = u32::from_le_bytes([buf[i], buf[i+1], buf[i+2], buf[i+3]]); + self.tx_fifo_write32(val); + if len == 5 { + // dprintf!("write32 {} {}\r\n", i, val); + } + } + for i in len-single_write_count..len { + if len == 5 { + // dprintf!("write8 {} {}\r\n", i, buf[i]); + } + self.tx_fifo_write8(buf[i]); + } + // dprintf!("done writing fifo\r\n"); + } + + fn fifo_read(&self, buf: &mut [u8], len: usize) { + for i in 0..(len / 4) { + self.rx_fifo_wait(); + let val = self.rx_fifo_read32(); + buf[i*4] = (val & 0xFF) as u8; + buf[(i*4)+1] = ((val >> 8) & 0xFF) as u8; + buf[(i*4)+2] = ((val >> 16) & 0xFF) as u8; + buf[(i*4)+3] = ((val >> 24) & 0xFF) as u8; + } + if (len % 4) != 0 { + self.rx_fifo_wait(); + self.rx_fifo_read32(); + } + } + + fn write_command_reg(&self, len: usize, bidir: bool) { + self.registers.command.modify( + COMMAND::LEN.val((len - 1) as u32) + + COMMAND::SPEED::CLEAR + + (if bidir { COMMAND::DIRECTION.val(3) } else { COMMAND::DIRECTION.val(2) }) + + (if self.hold_low.get() { COMMAND::CSAAT::SET } else { COMMAND::CSAAT::CLEAR }) + ); + } +} + +impl kernel::hil::spi::SpiMaster for SpiHw { + type ChipSelect = u32; + fn set_client(&self, client: &'static (dyn kernel::hil::spi::SpiMasterClient + 'static)) { + self.client.set(client); + } + fn init(&self) { + let peripheral_clock_freq_hz = 2_500_000; + let spi_clock = 2_500_000; + let divider = peripheral_clock_freq_hz / spi_clock; + self.reset(); + // TODO(atv): Many elided settings from the original init, + // since we leave them as zero. + self.registers.configop[0].modify(CONFIGOP::CLKDIV_0.val(divider)); + self.enable(true); + self.registers.control.modify(CONTROL::OUTPUT_EN::SET); + self.registers.intr_enable.modify(INTR::SPI_EVENT::SET); + self.registers.event_enable.modify(EVENT_ENABLE::IDLE::SET); + } + fn is_busy(&self) -> bool { todo!() } + fn read_write_bytes(&self, + write_buffer: &'static mut [u8], + read_buffer: core::option::Option<&'static mut [u8]>, + len: usize) -> kernel::ReturnCode { + self.chip_select.map(|cs| { + self.registers.csid.set(*cs); + }); + self.tx_buf.replace(write_buffer); + self.tx_buf.map(|write_buffer| { + self.fifo_write(write_buffer, len); + }); + match read_buffer { + Some(read_buffer) => { + self.rx_buf.put(Some(read_buffer)); + self.busy.set(true); + self.write_command_reg(len, true); + self.rx_buf.map(|read_buffer| { + self.fifo_read(read_buffer, len); + }); + }, + None => { + self.rx_buf.put(None); + self.busy.set(true); + self.write_command_reg(len, false); + } + }; + self.transfer_len.set(len); + // dprintf!("SpiHostHal::read_write_bytes ok\r\n"); + kernel::ReturnCode::SUCCESS + } + fn write_byte(&self, _: u8) { todo!() } + fn read_byte(&self) -> u8 { todo!() } + fn read_write_byte(&self, _: u8) -> u8 { todo!() } + fn specify_chip_select(&self, chip_select: <Self as kernel::hil::spi::SpiMaster>::ChipSelect) { + self.chip_select.set(chip_select); + } + fn set_rate(&self, _: u32) -> u32 { todo!() } + fn get_rate(&self) -> u32 { todo!() } + fn set_clock(&self, _: kernel::hil::spi::ClockPolarity) { todo!() } + fn get_clock(&self) -> kernel::hil::spi::ClockPolarity { todo!() } + fn set_phase(&self, _: kernel::hil::spi::ClockPhase) { todo!() } + fn get_phase(&self) -> kernel::hil::spi::ClockPhase { todo!() } + fn hold_low(&self) { self.hold_low.set(true); } + fn release_low(&self) { self.hold_low.set(false); } +}