blob: 57a05dec29f12437e1157a6fe96d00554fa595ad [file] [log] [blame]
// 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); }
}