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