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