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); }
+}