diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-07-10 19:45:26 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-07-10 19:45:26 +0200 |
| commit | e560415fde967483573d42f628e52501768584e0 (patch) | |
| tree | 864f2141d7351b0f1940ee2015d1c7d5c3d37ad1 /src | |
:rainbow:
Diffstat (limited to 'src')
| -rw-r--r-- | src/fmt.rs | 228 | ||||
| -rw-r--r-- | src/lib.rs | 879 |
2 files changed, 1107 insertions, 0 deletions
diff --git a/src/fmt.rs b/src/fmt.rs new file mode 100644 index 000000000..f8bb0a035 --- /dev/null +++ b/src/fmt.rs | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[cfg(feature = "defmt-timestamp-uptime")] | ||
| 199 | defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } | ||
| 200 | |||
| 201 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 202 | pub struct NoneError; | ||
| 203 | |||
| 204 | pub trait Try { | ||
| 205 | type Ok; | ||
| 206 | type Error; | ||
| 207 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<T> Try for Option<T> { | ||
| 211 | type Ok = T; | ||
| 212 | type Error = NoneError; | ||
| 213 | |||
| 214 | #[inline] | ||
| 215 | fn into_result(self) -> Result<T, NoneError> { | ||
| 216 | self.ok_or(NoneError) | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | impl<T, E> Try for Result<T, E> { | ||
| 221 | type Ok = T; | ||
| 222 | type Error = E; | ||
| 223 | |||
| 224 | #[inline] | ||
| 225 | fn into_result(self) -> Self { | ||
| 226 | self | ||
| 227 | } | ||
| 228 | } | ||
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..5caf19267 --- /dev/null +++ b/src/lib.rs | |||
| @@ -0,0 +1,879 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait, concat_bytes)] | ||
| 4 | |||
| 5 | // This mod MUST go first, so that the others see its macros. | ||
| 6 | pub(crate) mod fmt; | ||
| 7 | |||
| 8 | use core::slice; | ||
| 9 | |||
| 10 | use embassy::time::{block_for, Duration, Timer}; | ||
| 11 | use embassy::util::yield_now; | ||
| 12 | use embassy_rp::gpio::{Flex, Output, Pin}; | ||
| 13 | |||
| 14 | fn swap16(x: u32) -> u32 { | ||
| 15 | (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 | ||
| 16 | } | ||
| 17 | |||
| 18 | fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { | ||
| 19 | (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) | ||
| 20 | } | ||
| 21 | |||
| 22 | const FUNC_BUS: u32 = 0; | ||
| 23 | const FUNC_BACKPLANE: u32 = 1; | ||
| 24 | const FUNC_WLAN: u32 = 2; | ||
| 25 | const FUNC_BT: u32 = 3; | ||
| 26 | |||
| 27 | const REG_BUS_CTRL: u32 = 0x0; | ||
| 28 | const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status | ||
| 29 | const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask | ||
| 30 | const REG_BUS_STATUS: u32 = 0x8; | ||
| 31 | const REG_BUS_FEEDBEAD: u32 = 0x14; | ||
| 32 | const REG_BUS_TEST: u32 = 0x18; | ||
| 33 | const REG_BUS_RESP_DELAY: u32 = 0x1c; | ||
| 34 | |||
| 35 | // SPI_STATUS_REGISTER bits | ||
| 36 | const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; | ||
| 37 | const STATUS_UNDERFLOW: u32 = 0x00000002; | ||
| 38 | const STATUS_OVERFLOW: u32 = 0x00000004; | ||
| 39 | const STATUS_F2_INTR: u32 = 0x00000008; | ||
| 40 | const STATUS_F3_INTR: u32 = 0x00000010; | ||
| 41 | const STATUS_F2_RX_READY: u32 = 0x00000020; | ||
| 42 | const STATUS_F3_RX_READY: u32 = 0x00000040; | ||
| 43 | const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; | ||
| 44 | const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; | ||
| 45 | const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; | ||
| 46 | const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; | ||
| 47 | const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; | ||
| 48 | const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; | ||
| 49 | const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; | ||
| 50 | |||
| 51 | const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; | ||
| 52 | const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; | ||
| 53 | const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; | ||
| 54 | const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; | ||
| 55 | const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; | ||
| 56 | const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; | ||
| 57 | const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; | ||
| 58 | const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; | ||
| 59 | const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; | ||
| 60 | const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; | ||
| 61 | const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; | ||
| 62 | const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; | ||
| 63 | const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; | ||
| 64 | const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; | ||
| 65 | const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; | ||
| 66 | |||
| 67 | const BACKPLANE_WINDOW_SIZE: usize = 0x8000; | ||
| 68 | const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; | ||
| 69 | const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; | ||
| 70 | const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; | ||
| 71 | |||
| 72 | const AI_IOCTRL_OFFSET: u32 = 0x408; | ||
| 73 | const AI_IOCTRL_BIT_FGC: u8 = 0x0002; | ||
| 74 | const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; | ||
| 75 | const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; | ||
| 76 | |||
| 77 | const AI_RESETCTRL_OFFSET: u32 = 0x800; | ||
| 78 | const AI_RESETCTRL_BIT_RESET: u8 = 1; | ||
| 79 | |||
| 80 | const AI_RESETSTATUS_OFFSET: u32 = 0x804; | ||
| 81 | |||
| 82 | const TEST_PATTERN: u32 = 0x12345678; | ||
| 83 | const FEEDBEAD: u32 = 0xFEEDBEAD; | ||
| 84 | |||
| 85 | // SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits | ||
| 86 | const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" | ||
| 87 | const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; | ||
| 88 | const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; | ||
| 89 | const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 | ||
| 90 | const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 | ||
| 91 | const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; | ||
| 92 | const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; | ||
| 93 | const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests | ||
| 94 | const IRQ_MISC_INTR0: u16 = 0x0100; | ||
| 95 | const IRQ_MISC_INTR1: u16 = 0x0200; | ||
| 96 | const IRQ_MISC_INTR2: u16 = 0x0400; | ||
| 97 | const IRQ_MISC_INTR3: u16 = 0x0800; | ||
| 98 | const IRQ_MISC_INTR4: u16 = 0x1000; | ||
| 99 | const IRQ_F1_INTR: u16 = 0x2000; | ||
| 100 | const IRQ_F2_INTR: u16 = 0x4000; | ||
| 101 | const IRQ_F3_INTR: u16 = 0x8000; | ||
| 102 | |||
| 103 | #[derive(Clone, Copy, PartialEq, Eq)] | ||
| 104 | enum Core { | ||
| 105 | WLAN = 0, | ||
| 106 | SOCSRAM = 1, | ||
| 107 | SDIOD = 2, | ||
| 108 | } | ||
| 109 | |||
| 110 | impl Core { | ||
| 111 | fn base_addr(&self) -> u32 { | ||
| 112 | match self { | ||
| 113 | Self::WLAN => CHIP.arm_core_base_address, | ||
| 114 | Self::SOCSRAM => CHIP.socsram_wrapper_base_address, | ||
| 115 | Self::SDIOD => CHIP.sdiod_core_base_address, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | struct Chip { | ||
| 121 | arm_core_base_address: u32, | ||
| 122 | socsram_base_address: u32, | ||
| 123 | socsram_wrapper_base_address: u32, | ||
| 124 | sdiod_core_base_address: u32, | ||
| 125 | pmu_base_address: u32, | ||
| 126 | chip_ram_size: u32, | ||
| 127 | atcm_ram_base_address: u32, | ||
| 128 | socram_srmem_size: u32, | ||
| 129 | chanspec_band_mask: u32, | ||
| 130 | chanspec_band_2g: u32, | ||
| 131 | chanspec_band_5g: u32, | ||
| 132 | chanspec_band_shift: u32, | ||
| 133 | chanspec_bw_10: u32, | ||
| 134 | chanspec_bw_20: u32, | ||
| 135 | chanspec_bw_40: u32, | ||
| 136 | chanspec_bw_mask: u32, | ||
| 137 | chanspec_bw_shift: u32, | ||
| 138 | chanspec_ctl_sb_lower: u32, | ||
| 139 | chanspec_ctl_sb_upper: u32, | ||
| 140 | chanspec_ctl_sb_none: u32, | ||
| 141 | chanspec_ctl_sb_mask: u32, | ||
| 142 | } | ||
| 143 | |||
| 144 | const WRAPPER_REGISTER_OFFSET: u32 = 0x100000; | ||
| 145 | |||
| 146 | // Data for CYW43439 | ||
| 147 | const CHIP: Chip = Chip { | ||
| 148 | arm_core_base_address: 0x18003000 + WRAPPER_REGISTER_OFFSET, | ||
| 149 | socsram_base_address: 0x18004000, | ||
| 150 | socsram_wrapper_base_address: 0x18004000 + WRAPPER_REGISTER_OFFSET, | ||
| 151 | sdiod_core_base_address: 0x18002000, | ||
| 152 | pmu_base_address: 0x18000000, | ||
| 153 | chip_ram_size: 512 * 1024, | ||
| 154 | atcm_ram_base_address: 0, | ||
| 155 | socram_srmem_size: 64 * 1024, | ||
| 156 | chanspec_band_mask: 0xc000, | ||
| 157 | chanspec_band_2g: 0x0000, | ||
| 158 | chanspec_band_5g: 0xc000, | ||
| 159 | chanspec_band_shift: 14, | ||
| 160 | chanspec_bw_10: 0x0800, | ||
| 161 | chanspec_bw_20: 0x1000, | ||
| 162 | chanspec_bw_40: 0x1800, | ||
| 163 | chanspec_bw_mask: 0x3800, | ||
| 164 | chanspec_bw_shift: 11, | ||
| 165 | chanspec_ctl_sb_lower: 0x0000, | ||
| 166 | chanspec_ctl_sb_upper: 0x0100, | ||
| 167 | chanspec_ctl_sb_none: 0x0000, | ||
| 168 | chanspec_ctl_sb_mask: 0x0700, | ||
| 169 | }; | ||
| 170 | |||
| 171 | #[derive(Clone, Copy)] | ||
| 172 | #[repr(C)] | ||
| 173 | struct SdpcmHeader { | ||
| 174 | len: u16, | ||
| 175 | len_inv: u16, | ||
| 176 | /// Rx/Tx sequence number | ||
| 177 | sequence: u8, | ||
| 178 | /// 4 MSB Channel number, 4 LSB arbitrary flag | ||
| 179 | channel_and_flags: u8, | ||
| 180 | /// Length of next data frame, reserved for Tx | ||
| 181 | next_length: u8, | ||
| 182 | /// Data offset | ||
| 183 | header_length: u8, | ||
| 184 | /// Flow control bits, reserved for Tx | ||
| 185 | wireless_flow_control: u8, | ||
| 186 | /// Maximum Sequence number allowed by firmware for Tx | ||
| 187 | bus_data_credit: u8, | ||
| 188 | /// Reserved | ||
| 189 | reserved: [u8; 2], | ||
| 190 | } | ||
| 191 | |||
| 192 | #[derive(Clone, Copy)] | ||
| 193 | #[repr(C)] | ||
| 194 | struct CdcHeader { | ||
| 195 | cmd: u32, | ||
| 196 | out_len: u16, | ||
| 197 | in_len: u16, | ||
| 198 | flags: u16, | ||
| 199 | id: u16, | ||
| 200 | status: u32, | ||
| 201 | } | ||
| 202 | |||
| 203 | #[derive(Clone, Copy)] | ||
| 204 | #[repr(C)] | ||
| 205 | struct BdcHeader { | ||
| 206 | flags: u8, | ||
| 207 | /// 802.1d Priority (low 3 bits) | ||
| 208 | priority: u8, | ||
| 209 | flags2: u8, | ||
| 210 | /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. | ||
| 211 | data_offset: u8, | ||
| 212 | } | ||
| 213 | |||
| 214 | macro_rules! impl_bytes { | ||
| 215 | ($t:ident) => { | ||
| 216 | impl $t { | ||
| 217 | const SIZE: usize = core::mem::size_of::<Self>(); | ||
| 218 | |||
| 219 | pub fn to_bytes(&self) -> [u8; Self::SIZE] { | ||
| 220 | unsafe { core::mem::transmute(*self) } | ||
| 221 | } | ||
| 222 | |||
| 223 | pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { | ||
| 224 | unsafe { core::mem::transmute(*bytes) } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | }; | ||
| 228 | } | ||
| 229 | impl_bytes!(SdpcmHeader); | ||
| 230 | impl_bytes!(CdcHeader); | ||
| 231 | impl_bytes!(BdcHeader); | ||
| 232 | |||
| 233 | pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { | ||
| 234 | pwr: Output<'a, PWR>, | ||
| 235 | |||
| 236 | /// SPI chip-select. | ||
| 237 | cs: Output<'a, CS>, | ||
| 238 | |||
| 239 | /// SPI clock | ||
| 240 | clk: Output<'a, CLK>, | ||
| 241 | |||
| 242 | /// 4 signals, all in one!! | ||
| 243 | /// - SPI MISO | ||
| 244 | /// - SPI MOSI | ||
| 245 | /// - IRQ | ||
| 246 | /// - strap to set to gSPI mode on boot. | ||
| 247 | dio: Flex<'a, DIO>, | ||
| 248 | |||
| 249 | backplane_window: u32, | ||
| 250 | } | ||
| 251 | |||
| 252 | impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { | ||
| 253 | pub fn new(pwr: Output<'a, PWR>, cs: Output<'a, CS>, clk: Output<'a, CLK>, dio: Flex<'a, DIO>) -> Self { | ||
| 254 | Self { | ||
| 255 | pwr, | ||
| 256 | cs, | ||
| 257 | clk, | ||
| 258 | dio, | ||
| 259 | backplane_window: 0xAAAA_AAAA, | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | pub async fn init(&mut self) { | ||
| 264 | // Set strap to select gSPI mode. | ||
| 265 | self.dio.set_as_output(); | ||
| 266 | self.dio.set_low(); | ||
| 267 | |||
| 268 | // Reset | ||
| 269 | self.pwr.set_low(); | ||
| 270 | Timer::after(Duration::from_millis(20)).await; | ||
| 271 | self.pwr.set_high(); | ||
| 272 | Timer::after(Duration::from_millis(250)).await; | ||
| 273 | |||
| 274 | info!("waiting for ping..."); | ||
| 275 | while self.read32_swapped(REG_BUS_FEEDBEAD) != FEEDBEAD {} | ||
| 276 | info!("ping ok"); | ||
| 277 | |||
| 278 | self.write32_swapped(0x18, TEST_PATTERN); | ||
| 279 | let val = self.read32_swapped(REG_BUS_TEST); | ||
| 280 | assert_eq!(val, TEST_PATTERN); | ||
| 281 | |||
| 282 | // 32bit, big endian. | ||
| 283 | self.write32_swapped(REG_BUS_CTRL, 0x00010033); | ||
| 284 | |||
| 285 | let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD); | ||
| 286 | assert_eq!(val, FEEDBEAD); | ||
| 287 | let val = self.read32(FUNC_BUS, REG_BUS_TEST); | ||
| 288 | assert_eq!(val, TEST_PATTERN); | ||
| 289 | |||
| 290 | // No response delay in any of the funcs. | ||
| 291 | // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do... | ||
| 292 | //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0); | ||
| 293 | |||
| 294 | // Init ALP (no idea what that stands for) clock | ||
| 295 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x08); | ||
| 296 | info!("waiting for clock..."); | ||
| 297 | while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x40 == 0 {} | ||
| 298 | info!("clock ok"); | ||
| 299 | |||
| 300 | let chip_id = self.bp_read16(0x1800_0000); | ||
| 301 | info!("chip ID: {}", chip_id); | ||
| 302 | |||
| 303 | // Upload firmware. | ||
| 304 | self.core_disable(Core::WLAN); | ||
| 305 | self.core_reset(Core::SOCSRAM); | ||
| 306 | self.bp_write32(CHIP.socsram_base_address + 0x10, 3); | ||
| 307 | self.bp_write32(CHIP.socsram_base_address + 0x44, 0); | ||
| 308 | |||
| 309 | // I'm flashing the firmwares independently at hardcoded addresses, instead of baking them | ||
| 310 | // into the program with `include_bytes!` or similar, so that flashing the program stays fast. | ||
| 311 | // | ||
| 312 | // Flash them like this, also don't forget to update the lengths below if you change them!. | ||
| 313 | // | ||
| 314 | // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 315 | // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 | ||
| 316 | let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 317 | let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 318 | |||
| 319 | let ram_addr = CHIP.atcm_ram_base_address; | ||
| 320 | |||
| 321 | info!("loading fw"); | ||
| 322 | self.bp_write(ram_addr, fw); | ||
| 323 | |||
| 324 | info!("verifying fw"); | ||
| 325 | let mut buf = [0; 1024]; | ||
| 326 | for (i, chunk) in fw.chunks(1024).enumerate() { | ||
| 327 | let buf = &mut buf[..chunk.len()]; | ||
| 328 | self.bp_read(ram_addr + i as u32 * 1024, buf); | ||
| 329 | assert_eq!(chunk, buf); | ||
| 330 | } | ||
| 331 | |||
| 332 | info!("loading nvram"); | ||
| 333 | // Round up to 4 bytes. | ||
| 334 | let nvram_len = (NVRAM.len() + 3) / 4 * 4; | ||
| 335 | self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM); | ||
| 336 | |||
| 337 | let nvram_len_words = nvram_len as u32 / 4; | ||
| 338 | let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; | ||
| 339 | self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic); | ||
| 340 | |||
| 341 | // Start core! | ||
| 342 | info!("starting up core..."); | ||
| 343 | self.core_reset(Core::WLAN); | ||
| 344 | assert!(self.core_is_up(Core::WLAN)); | ||
| 345 | |||
| 346 | while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x80 == 0 {} | ||
| 347 | |||
| 348 | // "Set up the interrupt mask and enable interrupts" | ||
| 349 | self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0); | ||
| 350 | |||
| 351 | // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." | ||
| 352 | // Sounds scary... | ||
| 353 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32); | ||
| 354 | |||
| 355 | // wait for wifi startup | ||
| 356 | info!("waiting for wifi init..."); | ||
| 357 | while self.read32(FUNC_BUS, REG_BUS_STATUS) & STATUS_F2_RX_READY == 0 {} | ||
| 358 | |||
| 359 | // Some random configs related to sleep. | ||
| 360 | // I think they're not needed if we don't want sleep...??? | ||
| 361 | /* | ||
| 362 | let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL); | ||
| 363 | val |= 0x02; // WAKE_TILL_HT_AVAIL | ||
| 364 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val); | ||
| 365 | self.write8(FUNC_BUS, 0xF0, 0x08); // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 | ||
| 366 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02); // SBSDIO_FORCE_HT | ||
| 367 | |||
| 368 | let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR); | ||
| 369 | val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON | ||
| 370 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val); | ||
| 371 | |||
| 372 | // clear pulls | ||
| 373 | self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0); | ||
| 374 | let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); | ||
| 375 | */ | ||
| 376 | |||
| 377 | let mut buf = [0; 8 + 12 + 1024]; | ||
| 378 | buf[0..8].copy_from_slice(b"clmload\x00"); | ||
| 379 | buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00"); | ||
| 380 | buf[20..].copy_from_slice(&clm[..1024]); | ||
| 381 | self.send_ioctl(2, 263, 0, &buf); | ||
| 382 | |||
| 383 | info!("init done "); | ||
| 384 | |||
| 385 | let mut old_irq = 0; | ||
| 386 | let mut buf = [0; 2048]; | ||
| 387 | loop { | ||
| 388 | let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); | ||
| 389 | if irq != old_irq { | ||
| 390 | info!("irq: {:04x}", irq); | ||
| 391 | } | ||
| 392 | old_irq = irq; | ||
| 393 | |||
| 394 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 395 | let mut status = 0xFFFF_FFFF; | ||
| 396 | while status == 0xFFFF_FFFF { | ||
| 397 | status = self.read32(FUNC_BUS, REG_BUS_STATUS); | ||
| 398 | } | ||
| 399 | |||
| 400 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 401 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 402 | info!("got len {}", len); | ||
| 403 | |||
| 404 | let cmd = cmd_word(false, true, FUNC_WLAN, 0, len); | ||
| 405 | |||
| 406 | self.cs.set_low(); | ||
| 407 | self.spi_write(&cmd.to_le_bytes()); | ||
| 408 | self.spi_read(&mut buf[..len as usize]); | ||
| 409 | // pad to 32bit | ||
| 410 | let mut junk = [0; 4]; | ||
| 411 | if len % 4 != 0 { | ||
| 412 | self.spi_read(&mut junk[..(4 - len as usize % 4)]); | ||
| 413 | } | ||
| 414 | self.cs.set_high(); | ||
| 415 | |||
| 416 | info!("rxd packet {:02x}", &buf[..len as usize]); | ||
| 417 | |||
| 418 | self.rx(&buf[..len as usize]); | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | yield_now().await; | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | fn rx(&mut self, packet: &[u8]) { | ||
| 427 | if packet.len() < SdpcmHeader::SIZE { | ||
| 428 | warn!("packet too short, len={}", packet.len()); | ||
| 429 | return; | ||
| 430 | } | ||
| 431 | |||
| 432 | let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); | ||
| 433 | |||
| 434 | if sdpcm_header.len != !sdpcm_header.len_inv { | ||
| 435 | warn!("len inv mismatch"); | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | if sdpcm_header.len as usize != packet.len() { | ||
| 439 | // TODO: is this guaranteed?? | ||
| 440 | warn!("len from header doesn't match len from spi"); | ||
| 441 | return; | ||
| 442 | } | ||
| 443 | |||
| 444 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 445 | |||
| 446 | match channel { | ||
| 447 | 0 => { | ||
| 448 | if packet.len() < SdpcmHeader::SIZE + CdcHeader::SIZE { | ||
| 449 | warn!("control packet too short, len={}", packet.len()); | ||
| 450 | return; | ||
| 451 | } | ||
| 452 | |||
| 453 | let cdc_header = | ||
| 454 | CdcHeader::from_bytes(packet[SdpcmHeader::SIZE..][..CdcHeader::SIZE].try_into().unwrap()); | ||
| 455 | |||
| 456 | // TODO check cdc_header.id matches | ||
| 457 | // TODO check status | ||
| 458 | } | ||
| 459 | _ => {} | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { | ||
| 464 | let mut buf = [0; 2048]; | ||
| 465 | |||
| 466 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 467 | |||
| 468 | let sdpcm_header = SdpcmHeader { | ||
| 469 | len: total_len as u16, | ||
| 470 | len_inv: !total_len as u16, | ||
| 471 | sequence: 0x02, // todo | ||
| 472 | channel_and_flags: 0, // control channle | ||
| 473 | next_length: 0, | ||
| 474 | header_length: SdpcmHeader::SIZE as _, | ||
| 475 | wireless_flow_control: 0, | ||
| 476 | bus_data_credit: 0, | ||
| 477 | reserved: [0, 0], | ||
| 478 | }; | ||
| 479 | |||
| 480 | let cdc_header = CdcHeader { | ||
| 481 | cmd: cmd, | ||
| 482 | out_len: data.len() as _, | ||
| 483 | in_len: 0, | ||
| 484 | flags: kind as u16 | (iface as u16) << 12, | ||
| 485 | id: 1, // todo | ||
| 486 | status: 0, | ||
| 487 | }; | ||
| 488 | |||
| 489 | buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 490 | buf[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 491 | buf[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 492 | |||
| 493 | info!("txing {:02x}", &buf[..total_len]); | ||
| 494 | |||
| 495 | let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); | ||
| 496 | self.cs.set_low(); | ||
| 497 | self.spi_write(&cmd.to_le_bytes()); | ||
| 498 | self.spi_write(&buf[..total_len]); | ||
| 499 | self.cs.set_high(); | ||
| 500 | } | ||
| 501 | |||
| 502 | fn core_disable(&mut self, core: Core) { | ||
| 503 | let base = core.base_addr(); | ||
| 504 | |||
| 505 | // Dummy read? | ||
| 506 | let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); | ||
| 507 | |||
| 508 | // Check it isn't already reset | ||
| 509 | let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); | ||
| 510 | if r & AI_RESETCTRL_BIT_RESET != 0 { | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | |||
| 514 | self.bp_write8(base + AI_IOCTRL_OFFSET, 0); | ||
| 515 | let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); | ||
| 516 | |||
| 517 | block_for(Duration::from_millis(1)); | ||
| 518 | |||
| 519 | self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET); | ||
| 520 | let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); | ||
| 521 | } | ||
| 522 | |||
| 523 | fn core_reset(&mut self, core: Core) { | ||
| 524 | self.core_disable(core); | ||
| 525 | |||
| 526 | let base = core.base_addr(); | ||
| 527 | self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN); | ||
| 528 | let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); | ||
| 529 | |||
| 530 | self.bp_write8(base + AI_RESETCTRL_OFFSET, 0); | ||
| 531 | |||
| 532 | block_for(Duration::from_millis(1)); | ||
| 533 | |||
| 534 | self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN); | ||
| 535 | let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); | ||
| 536 | |||
| 537 | block_for(Duration::from_millis(1)); | ||
| 538 | } | ||
| 539 | |||
| 540 | fn core_is_up(&mut self, core: Core) -> bool { | ||
| 541 | let base = core.base_addr(); | ||
| 542 | |||
| 543 | let io = self.bp_read8(base + AI_IOCTRL_OFFSET); | ||
| 544 | if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { | ||
| 545 | debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); | ||
| 546 | return false; | ||
| 547 | } | ||
| 548 | |||
| 549 | let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); | ||
| 550 | if r & (AI_RESETCTRL_BIT_RESET) != 0 { | ||
| 551 | debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); | ||
| 552 | return false; | ||
| 553 | } | ||
| 554 | |||
| 555 | true | ||
| 556 | } | ||
| 557 | |||
| 558 | fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { | ||
| 559 | // It seems the HW force-aligns the addr | ||
| 560 | // to 2 if data.len() >= 2 | ||
| 561 | // to 4 if data.len() >= 4 | ||
| 562 | // To simplify, enforce 4-align for now. | ||
| 563 | assert!(addr % 4 == 0); | ||
| 564 | |||
| 565 | while !data.is_empty() { | ||
| 566 | // Ensure transfer doesn't cross a window boundary. | ||
| 567 | let window_offs = addr & BACKPLANE_ADDRESS_MASK; | ||
| 568 | let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; | ||
| 569 | |||
| 570 | let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); | ||
| 571 | |||
| 572 | self.backplane_set_window(addr); | ||
| 573 | |||
| 574 | let cmd = cmd_word(false, true, FUNC_BACKPLANE, window_offs, len as u32); | ||
| 575 | self.cs.set_low(); | ||
| 576 | self.spi_write(&cmd.to_le_bytes()); | ||
| 577 | |||
| 578 | // 4-byte response delay. | ||
| 579 | let mut junk = [0; 4]; | ||
| 580 | self.spi_read(&mut junk); | ||
| 581 | |||
| 582 | // Read data | ||
| 583 | self.spi_read(&mut data[..len]); | ||
| 584 | |||
| 585 | // pad to 32bit | ||
| 586 | if len % 4 != 0 { | ||
| 587 | self.spi_read(&mut junk[..(4 - len % 4)]); | ||
| 588 | } | ||
| 589 | self.cs.set_high(); | ||
| 590 | |||
| 591 | // Advance ptr. | ||
| 592 | addr += len as u32; | ||
| 593 | data = &mut data[len..]; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { | ||
| 598 | // It seems the HW force-aligns the addr | ||
| 599 | // to 2 if data.len() >= 2 | ||
| 600 | // to 4 if data.len() >= 4 | ||
| 601 | // To simplify, enforce 4-align for now. | ||
| 602 | assert!(addr % 4 == 0); | ||
| 603 | |||
| 604 | while !data.is_empty() { | ||
| 605 | // Ensure transfer doesn't cross a window boundary. | ||
| 606 | let window_offs = addr & BACKPLANE_ADDRESS_MASK; | ||
| 607 | let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; | ||
| 608 | |||
| 609 | let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); | ||
| 610 | |||
| 611 | self.backplane_set_window(addr); | ||
| 612 | |||
| 613 | let cmd = cmd_word(true, true, FUNC_BACKPLANE, window_offs, len as u32); | ||
| 614 | self.cs.set_low(); | ||
| 615 | self.spi_write(&cmd.to_le_bytes()); | ||
| 616 | self.spi_write(&data[..len]); | ||
| 617 | // pad to 32bit | ||
| 618 | if len % 4 != 0 { | ||
| 619 | let zeros = [0; 4]; | ||
| 620 | self.spi_write(&zeros[..(4 - len % 4)]); | ||
| 621 | } | ||
| 622 | self.cs.set_high(); | ||
| 623 | |||
| 624 | // Advance ptr. | ||
| 625 | addr += len as u32; | ||
| 626 | data = &data[len..]; | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | fn bp_read8(&mut self, addr: u32) -> u8 { | ||
| 631 | self.backplane_readn(addr, 1) as u8 | ||
| 632 | } | ||
| 633 | |||
| 634 | fn bp_write8(&mut self, addr: u32, val: u8) { | ||
| 635 | self.backplane_writen(addr, val as u32, 1) | ||
| 636 | } | ||
| 637 | |||
| 638 | fn bp_read16(&mut self, addr: u32) -> u16 { | ||
| 639 | self.backplane_readn(addr, 2) as u16 | ||
| 640 | } | ||
| 641 | |||
| 642 | fn bp_write16(&mut self, addr: u32, val: u16) { | ||
| 643 | self.backplane_writen(addr, val as u32, 2) | ||
| 644 | } | ||
| 645 | |||
| 646 | fn bp_read32(&mut self, addr: u32) -> u32 { | ||
| 647 | self.backplane_readn(addr, 4) | ||
| 648 | } | ||
| 649 | |||
| 650 | fn bp_write32(&mut self, addr: u32, val: u32) { | ||
| 651 | self.backplane_writen(addr, val, 4) | ||
| 652 | } | ||
| 653 | |||
| 654 | fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { | ||
| 655 | self.backplane_set_window(addr); | ||
| 656 | |||
| 657 | let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; | ||
| 658 | if len == 4 { | ||
| 659 | bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG | ||
| 660 | } | ||
| 661 | self.readn(FUNC_BACKPLANE, bus_addr, len) | ||
| 662 | } | ||
| 663 | |||
| 664 | fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { | ||
| 665 | self.backplane_set_window(addr); | ||
| 666 | |||
| 667 | let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; | ||
| 668 | if len == 4 { | ||
| 669 | bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG | ||
| 670 | } | ||
| 671 | self.writen(FUNC_BACKPLANE, bus_addr, val, len) | ||
| 672 | } | ||
| 673 | |||
| 674 | fn backplane_set_window(&mut self, addr: u32) { | ||
| 675 | let new_window = addr & !BACKPLANE_ADDRESS_MASK; | ||
| 676 | |||
| 677 | if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { | ||
| 678 | self.write8( | ||
| 679 | FUNC_BACKPLANE, | ||
| 680 | REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, | ||
| 681 | (new_window >> 24) as u8, | ||
| 682 | ); | ||
| 683 | } | ||
| 684 | if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { | ||
| 685 | self.write8( | ||
| 686 | FUNC_BACKPLANE, | ||
| 687 | REG_BACKPLANE_BACKPLANE_ADDRESS_MID, | ||
| 688 | (new_window >> 16) as u8, | ||
| 689 | ); | ||
| 690 | } | ||
| 691 | if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { | ||
| 692 | self.write8( | ||
| 693 | FUNC_BACKPLANE, | ||
| 694 | REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, | ||
| 695 | (new_window >> 8) as u8, | ||
| 696 | ); | ||
| 697 | } | ||
| 698 | self.backplane_window = new_window; | ||
| 699 | } | ||
| 700 | |||
| 701 | fn read8(&mut self, func: u32, addr: u32) -> u8 { | ||
| 702 | self.readn(func, addr, 1) as u8 | ||
| 703 | } | ||
| 704 | |||
| 705 | fn write8(&mut self, func: u32, addr: u32, val: u8) { | ||
| 706 | self.writen(func, addr, val as u32, 1) | ||
| 707 | } | ||
| 708 | |||
| 709 | fn read16(&mut self, func: u32, addr: u32) -> u16 { | ||
| 710 | self.readn(func, addr, 2) as u16 | ||
| 711 | } | ||
| 712 | |||
| 713 | fn write16(&mut self, func: u32, addr: u32, val: u16) { | ||
| 714 | self.writen(func, addr, val as u32, 2) | ||
| 715 | } | ||
| 716 | |||
| 717 | fn read32(&mut self, func: u32, addr: u32) -> u32 { | ||
| 718 | self.readn(func, addr, 4) | ||
| 719 | } | ||
| 720 | |||
| 721 | fn write32(&mut self, func: u32, addr: u32, val: u32) { | ||
| 722 | self.writen(func, addr, val, 4) | ||
| 723 | } | ||
| 724 | |||
| 725 | fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { | ||
| 726 | let cmd = cmd_word(false, true, func, addr, len); | ||
| 727 | let mut buf = [0; 4]; | ||
| 728 | |||
| 729 | self.cs.set_low(); | ||
| 730 | self.spi_write(&cmd.to_le_bytes()); | ||
| 731 | if func == FUNC_BACKPLANE { | ||
| 732 | // 4-byte response delay. | ||
| 733 | self.spi_read(&mut buf); | ||
| 734 | } | ||
| 735 | self.spi_read(&mut buf); | ||
| 736 | self.cs.set_high(); | ||
| 737 | |||
| 738 | u32::from_le_bytes(buf) | ||
| 739 | } | ||
| 740 | |||
| 741 | fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { | ||
| 742 | let cmd = cmd_word(true, true, func, addr, len); | ||
| 743 | |||
| 744 | self.cs.set_low(); | ||
| 745 | self.spi_write(&cmd.to_le_bytes()); | ||
| 746 | self.spi_write(&val.to_le_bytes()); | ||
| 747 | self.cs.set_high(); | ||
| 748 | } | ||
| 749 | |||
| 750 | fn read32_swapped(&mut self, addr: u32) -> u32 { | ||
| 751 | let cmd = cmd_word(false, true, FUNC_BUS, addr, 4); | ||
| 752 | let mut buf = [0; 4]; | ||
| 753 | |||
| 754 | self.cs.set_low(); | ||
| 755 | self.spi_write(&swap16(cmd).to_le_bytes()); | ||
| 756 | self.spi_read(&mut buf); | ||
| 757 | self.cs.set_high(); | ||
| 758 | |||
| 759 | swap16(u32::from_le_bytes(buf)) | ||
| 760 | } | ||
| 761 | |||
| 762 | fn write32_swapped(&mut self, addr: u32, val: u32) { | ||
| 763 | let cmd = cmd_word(true, true, FUNC_BUS, addr, 4); | ||
| 764 | |||
| 765 | self.cs.set_low(); | ||
| 766 | self.spi_write(&swap16(cmd).to_le_bytes()); | ||
| 767 | self.spi_write(&swap16(val).to_le_bytes()); | ||
| 768 | self.cs.set_high(); | ||
| 769 | } | ||
| 770 | |||
| 771 | fn spi_read(&mut self, words: &mut [u8]) { | ||
| 772 | self.dio.set_as_input(); | ||
| 773 | for word in words { | ||
| 774 | let mut w = 0; | ||
| 775 | for _ in 0..8 { | ||
| 776 | w = w << 1; | ||
| 777 | |||
| 778 | // rising edge, sample data | ||
| 779 | if self.dio.is_high() { | ||
| 780 | w |= 0x01; | ||
| 781 | } | ||
| 782 | self.clk.set_high(); | ||
| 783 | delay(); | ||
| 784 | |||
| 785 | // falling edge | ||
| 786 | self.clk.set_low(); | ||
| 787 | delay(); | ||
| 788 | } | ||
| 789 | *word = w | ||
| 790 | } | ||
| 791 | self.clk.set_low(); | ||
| 792 | delay(); | ||
| 793 | } | ||
| 794 | |||
| 795 | fn spi_write(&mut self, words: &[u8]) { | ||
| 796 | self.dio.set_as_output(); | ||
| 797 | for word in words { | ||
| 798 | let mut word = *word; | ||
| 799 | for _ in 0..8 { | ||
| 800 | // falling edge, setup data | ||
| 801 | self.clk.set_low(); | ||
| 802 | if word & 0x80 == 0 { | ||
| 803 | self.dio.set_low(); | ||
| 804 | } else { | ||
| 805 | self.dio.set_high(); | ||
| 806 | } | ||
| 807 | delay(); | ||
| 808 | |||
| 809 | // rising edge | ||
| 810 | self.clk.set_high(); | ||
| 811 | delay(); | ||
| 812 | |||
| 813 | word = word << 1; | ||
| 814 | } | ||
| 815 | } | ||
| 816 | self.clk.set_low(); | ||
| 817 | delay(); | ||
| 818 | self.dio.set_as_input(); | ||
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 822 | fn delay() { | ||
| 823 | //cortex_m::asm::delay(5); | ||
| 824 | } | ||
| 825 | |||
| 826 | macro_rules! nvram { | ||
| 827 | ($($s:literal,)*) => { | ||
| 828 | concat_bytes!($($s, b"\x00",)* b"\x00\x00") | ||
| 829 | }; | ||
| 830 | } | ||
| 831 | |||
| 832 | static NVRAM: &'static [u8] = &*nvram!( | ||
| 833 | b"NVRAMRev=$Rev$", | ||
| 834 | b"manfid=0x2d0", | ||
| 835 | b"prodid=0x0727", | ||
| 836 | b"vendid=0x14e4", | ||
| 837 | b"devid=0x43e2", | ||
| 838 | b"boardtype=0x0887", | ||
| 839 | b"boardrev=0x1100", | ||
| 840 | b"boardnum=22", | ||
| 841 | b"macaddr=00:A0:50:86:aa:b6", | ||
| 842 | b"sromrev=11", | ||
| 843 | b"boardflags=0x00404001", | ||
| 844 | b"boardflags3=0x04000000", | ||
| 845 | b"xtalfreq=26000", | ||
| 846 | b"nocrc=1", | ||
| 847 | b"ag0=255", | ||
| 848 | b"aa2g=1", | ||
| 849 | b"ccode=ALL", | ||
| 850 | b"pa0itssit=0x20", | ||
| 851 | b"extpagain2g=0", | ||
| 852 | b"pa2ga0=-168,7161,-820", | ||
| 853 | b"AvVmid_c0=0x0,0xc8", | ||
| 854 | b"cckpwroffset0=5", | ||
| 855 | b"maxp2ga0=84", | ||
| 856 | b"txpwrbckof=6", | ||
| 857 | b"cckbw202gpo=0", | ||
| 858 | b"legofdmbw202gpo=0x66111111", | ||
| 859 | b"mcsbw202gpo=0x77711111", | ||
| 860 | b"propbw202gpo=0xdd", | ||
| 861 | b"ofdmdigfilttype=18", | ||
| 862 | b"ofdmdigfilttypebe=18", | ||
| 863 | b"papdmode=1", | ||
| 864 | b"papdvalidtest=1", | ||
| 865 | b"pacalidx2g=45", | ||
| 866 | b"papdepsoffset=-30", | ||
| 867 | b"papdendidx=58", | ||
| 868 | b"ltecxmux=0", | ||
| 869 | b"ltecxpadnum=0x0102", | ||
| 870 | b"ltecxfnsel=0x44", | ||
| 871 | b"ltecxgcigpio=0x01", | ||
| 872 | b"il0macaddr=00:90:4c:c5:12:38", | ||
| 873 | b"wl0id=0x431b", | ||
| 874 | b"deadman_to=0xffffffff", | ||
| 875 | b"muxenab=0x1", | ||
| 876 | b"spurconfig=0x3", | ||
| 877 | b"glitch_based_crsmin=1", | ||
| 878 | b"btc_mode=1", | ||
| 879 | ); | ||
