capsules_extra/cyw4343/bus/
spi.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive 2025.
4
5use super::common;
6use super::State as BusState;
7use super::{CYW4343xBus, CYW4343xBusClient, Function, RegLen, Type};
8use crate::cyw4343::constants;
9use crate::cyw4343::macros::{backplane_window_bits, reset_and_restore_bufs};
10use core::cell::Cell;
11use kernel::hil::spi::{SpiMasterClient, SpiMasterDevice};
12use kernel::hil::time::ConvertTicks;
13use kernel::hil::{gpio, time};
14use kernel::utilities::leasable_buffer::SubSliceMut;
15use kernel::utilities::registers::register_bitfields;
16use kernel::{utilities::cells::OptionalCell, ErrorCode};
17use task::GspiTask;
18
19/// Max packet size is max payload size + 1 command/status word
20pub const MAX_PACKET_SIZE: usize = MAX_PAYLOAD_SIZE + CMD_SIZE;
21
22/// Maximum payload size
23///
24/// For SPI, 2048 is the maximum due to the length encoding
25/// in the command as 11 bits (0 decoded as 2048)
26const MAX_PAYLOAD_SIZE: usize = 2048;
27
28/// Word size used for address/registers
29pub const WORD_SIZE: usize = 4;
30/// Command size is 1 word
31const CMD_SIZE: usize = WORD_SIZE;
32/// Status size is 1 word
33const STATUS_SIZE: usize = WORD_SIZE;
34
35// Bus command encoding
36register_bitfields![u32,
37    CYW43SPI_CMD [
38        COMMAND OFFSET(31) NUMBITS(1) [],
39        ACCESS OFFSET(30) NUMBITS(1) [
40            IncAddr = 1,
41        ],
42        FUNCTION_NUM OFFSET(28) NUMBITS(2) [],
43        ADDRESS OFFSET(11) NUMBITS(17) [],
44        LENGTH OFFSET(0) NUMBITS(11) [],
45    ]
46];
47
48/// 32-bit LE bus command
49const fn cmd32(command: Type, function: Function, address: u32, length: u32) -> u32 {
50    CYW43SPI_CMD::COMMAND.val(command as u32).value
51        | CYW43SPI_CMD::ACCESS::IncAddr.value
52        | CYW43SPI_CMD::FUNCTION_NUM.val(function as u32).value
53        | CYW43SPI_CMD::ADDRESS.val(address).value
54        | CYW43SPI_CMD::LENGTH.val(length).value
55}
56
57/// 16-bit LE bus command
58const fn cmd16(command: Type, function: Function, address: u32, length: u32) -> u32 {
59    cmd32(command, function, address, length).rotate_left(16)
60}
61
62#[derive(Clone, Copy, Debug, Default)]
63enum State {
64    NotInit,
65    Init(u8),
66    Irq,
67    Write,
68    Read,
69    #[default]
70    Idle,
71}
72
73struct Backplane {
74    /// The backplane window address for the current backplane rx/tx operation
75    curr_window: Cell<u32>,
76    /// Backplane window that is in process to be set
77    pending_window: OptionalCell<(u32, u8)>,
78    /// The maximum packet size for F1 is small (64 bytes), so we need
79    /// to store the current offset of the buffer we want to transmit
80    transfer_offset: Cell<usize>,
81    /// This is true if there is at least one more chunk of the current buffer to be written
82    pending: Cell<bool>,
83}
84
85impl Backplane {
86    fn new() -> Self {
87        Self {
88            curr_window: Cell::new(0xAAAA_AAAA),
89            pending_window: OptionalCell::empty(),
90            transfer_offset: Cell::new(0),
91            pending: Cell::new(false),
92        }
93    }
94}
95
96/// gSPI interface implementation for the CYW43xx Bus
97///
98/// The protocol is explained in chapter 4.2 of the datasheet
99pub struct CYW4343xSpiBus<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> {
100    gspi: &'a S,
101    alarm: &'a A,
102    client: OptionalCell<&'a dyn CYW4343xBusClient>,
103    /// Backplane (F1) state
104    backplane: Backplane,
105    /// Inner bus state (initialising, idle or transfering bytes)
106    inner_state: Cell<State>,
107    /// Bus state from trait
108    state: Cell<BusState>,
109    /// If there was a register read operation this field stores the jump function and
110    /// the starting index for the register
111    read: OptionalCell<(fn(u32, &mut u8), u8)>,
112    /// Command buffer for reads/status buffer for writes
113    extra: OptionalCell<SubSliceMut<'static, u8>>,
114    /// Data buffer for read/writes
115    data: OptionalCell<SubSliceMut<'static, u8>>,
116    /// WLAN buffer
117    wlan: OptionalCell<SubSliceMut<'static, u8>>,
118    /// Firmware
119    fw: &'static [u8],
120    /// WLAN read packet length
121    len: OptionalCell<usize>,
122    /// NVRAM fw blob reference, backplane (F1) address and magic number
123    nvram: (&'static [u8], u32, u32),
124    /// An interrupt has fired while busy
125    irq_fired: Cell<bool>,
126}
127
128impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> CYW4343xSpiBus<'a, S, A> {
129    pub fn new(
130        gspi: &'a S,
131        alarm: &'a A,
132        extra: &'static mut [u8; CMD_SIZE],
133        buffer: &'static mut [u8; MAX_PACKET_SIZE],
134        fw: &'static [u8],
135        nvram: &'static [u8],
136    ) -> Self {
137        let nvram_len = nvram.len().div_ceil(4) * 4;
138        let nvram_words = nvram_len / 4;
139        let nvram_magic = (!nvram_words << 16) | nvram_words;
140        Self {
141            gspi,
142            alarm,
143            backplane: Backplane::new(),
144            extra: OptionalCell::new(SubSliceMut::new(extra)),
145            data: OptionalCell::new(SubSliceMut::new(buffer)),
146            client: OptionalCell::empty(),
147            inner_state: Cell::new(State::NotInit),
148            state: Cell::new(BusState::Idle),
149            read: OptionalCell::empty(),
150            wlan: OptionalCell::empty(),
151            nvram: (
152                nvram,
153                constants::NVRAM_END - (nvram_len as u32),
154                nvram_magic as u32,
155            ),
156            fw,
157            irq_fired: Cell::new(false),
158            len: OptionalCell::empty(),
159        }
160    }
161}
162
163impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> CYW4343xSpiBus<'a, S, A> {
164    /// Execute an initialisation task (read/write bytes on the bus or set a delay)
165    fn do_task(&self, task: GspiTask) -> Result<(), ErrorCode> {
166        match task {
167            GspiTask::ReadBackplane(cmd, addr, jmp) => {
168                self.update_bp_window_or_else(backplane_window_bits!(addr), || {
169                    self.read.insert(jmp.map(|jmp| (jmp, WORD_SIZE as _)));
170                    self.read(Function::Backplane, cmd)
171                })
172            }
173            GspiTask::WriteBackplane(cmd, addr, val) => {
174                self.update_bp_window_or_else(backplane_window_bits!(addr), || self.write(cmd, val))
175            }
176            GspiTask::Read(fun, cmd, jmp) => {
177                let pos = if matches!(fun, Function::Backplane) {
178                    // for backplane transfers there is an additional read
179                    // so the actual register value is the second word from the buffer
180                    4
181                } else {
182                    0
183                };
184
185                self.read.insert(jmp.map(|jmp| (jmp, pos)));
186                self.read(fun, cmd)
187            }
188            GspiTask::Write(cmd, val) => self.write(cmd, val),
189            GspiTask::Fw => self.write_bp(constants::RAM_BASE_ADDR, self.fw),
190            GspiTask::Nvram => self.write_bp(self.nvram.1, self.nvram.0),
191            GspiTask::NvramMagic => self
192                .update_bp_window_or_else(backplane_window_bits!(constants::NVRAM_END), || {
193                    self.write(NVRAM_MAGIC_CMD, self.nvram.2)
194                }),
195            GspiTask::WaitMs(ms) => {
196                self.alarm
197                    .set_alarm(self.alarm.now(), self.alarm.ticks_from_ms(ms));
198                Ok(())
199            }
200        }
201    }
202}
203
204impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> CYW4343xSpiBus<'a, S, A> {
205    /// Write bytes to a backplane memory region from a base address and base buffer.
206    /// As this is likely to be done in chunks of MAX_SPI_BP_CHUNK_SIZE bytes,
207    /// an inner offset will be kept to be added to the base_address.
208    fn write_bp(&self, base_address: u32, base_buffer: &[u8]) -> Result<(), ErrorCode> {
209        let offset = self.backplane.transfer_offset.get();
210        let address = base_address + offset as u32;
211
212        // Either update the backplane window or write a chunk of the buffer
213        self.update_bp_window_or_else(backplane_window_bits!(address), || {
214            let Some((mut extra, mut buf)) = Option::zip(self.extra.take(), self.data.take())
215            else {
216                return Err(ErrorCode::NOMEM);
217            };
218
219            let window_offset = address & constants::BACKPLANE_ADDRESS_MASK;
220            let window_remaining = (constants::BACKPLANE_WINDOW_SIZE - window_offset) as usize;
221
222            // chunk size is the min of:
223            // - the remaining length of packet
224            // - 64 bytes
225            // - the remaining length of the current window (so it doesnt wrap to the start of
226            // window)
227            let len = (base_buffer.len() - offset)
228                .min(constants::MAX_SPI_BP_CHUNK_SIZE)
229                .min(window_remaining);
230
231            let cmd = cmd32(Type::Write, Function::Backplane, window_offset, len as u32);
232
233            // We want to write cmd + len bytes?
234            buf.slice(0..CMD_SIZE + len.div_ceil(4) * 4);
235            extra.slice(0..STATUS_SIZE);
236
237            buf.as_mut_slice()[0..CMD_SIZE].copy_from_slice(&cmd.to_le_bytes());
238            buf.as_mut_slice()[CMD_SIZE..][..len].copy_from_slice(&base_buffer[offset..][..len]);
239
240            self.gspi
241                .read_write_bytes(buf, Some(extra))
242                .map_err(|(err, mut data, extra)| {
243                    let mut extra = extra.unwrap();
244                    reset_and_restore_bufs!(self, extra, data);
245                    err
246                })?;
247
248            // This is not the last chunk to be written
249            // NOTE: Using `last` instead of `pending` could be more readable
250            // let last = len + offset >= base_buffer.len()
251            let pending = len + offset < base_buffer.len();
252
253            self.backplane.pending.set(pending);
254            self.backplane
255                .transfer_offset
256                .set(if pending { offset + len } else { 0 });
257            Ok(())
258        })
259    }
260
261    /// Read a word
262    fn read(&self, fun: Function, cmd: u32) -> Result<(), ErrorCode> {
263        let Some((mut extra, mut data)) = Option::zip(self.extra.take(), self.data.take()) else {
264            return Err(ErrorCode::NOMEM);
265        };
266
267        extra.slice(..CMD_SIZE);
268        let mut size = STATUS_SIZE + WORD_SIZE;
269        if let Function::Backplane = fun {
270            // For backplane functions we read an extra word
271            size += WORD_SIZE;
272        }
273        data.slice(0..size);
274        extra.as_mut_slice()[0..CMD_SIZE].copy_from_slice(&cmd.to_le_bytes());
275
276        self.gspi
277            .read_write_bytes(extra, Some(data))
278            .map_err(|(err, mut extra, data)| {
279                let mut data = data.unwrap();
280                reset_and_restore_bufs!(self, extra, data);
281                err
282            })
283    }
284
285    /// Write a word
286    fn write(&self, cmd: u32, val: u32) -> Result<(), ErrorCode> {
287        let Some((mut extra, mut buffer)) = Option::zip(self.extra.take(), self.data.take()) else {
288            return Err(ErrorCode::NOMEM);
289        };
290
291        buffer.slice(..CMD_SIZE + WORD_SIZE);
292        extra.slice(..STATUS_SIZE);
293
294        buffer.as_mut_slice()[..CMD_SIZE].copy_from_slice(&cmd.to_le_bytes());
295        buffer.as_mut_slice()[CMD_SIZE..][..WORD_SIZE].copy_from_slice(&val.to_le_bytes());
296
297        self.gspi
298            .read_write_bytes(buffer, Some(extra))
299            .map_err(|(err, mut data, extra)| {
300                let mut extra = extra.unwrap();
301                reset_and_restore_bufs!(self, extra, data);
302                err
303            })
304    }
305
306    /// Update the backplane window
307    fn update_bp_window_or_else(
308        &self,
309        window: u32,
310        f: impl FnOnce() -> Result<(), ErrorCode>,
311    ) -> Result<(), ErrorCode> {
312        // FIXME: I think this is very hard to read
313        let update_byte = |idx: u8| {
314            let extract = |val: u32| -> u8 { (val >> ((idx as u32) * 8)) as u8 };
315            let extr = extract(window);
316            let curr = self.backplane.curr_window.get();
317            let extr_curr = extract(curr);
318            (extr != extr_curr).then(|| {
319                (
320                    cmd32(
321                        Type::Write,
322                        Function::Backplane,
323                        constants::REG_BACKPLANE_BACKPLANE_ADDRESS_LOW + idx as u32,
324                        RegLen::Byte as _,
325                    ),
326                    extr,
327                )
328            })
329        };
330
331        // If we were updating a byte from the window, start from that - 1
332        let byte = self.backplane.pending_window.take().map(|(_, b)| b - 1);
333        // If not, start from the highest byte
334        let byte = byte.unwrap_or(2) as _;
335
336        for b in (0..=byte).rev() {
337            if let Some((cmd, val)) = update_byte(b) {
338                self.backplane.pending_window.set((window, b));
339                return self.write(cmd, val as u32);
340            }
341        }
342
343        self.backplane.curr_window.set(window);
344        f()
345    }
346}
347
348impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> CYW4343xBus<'a>
349    for CYW4343xSpiBus<'a, S, A>
350{
351    fn set_client(&self, client: &'a dyn CYW4343xBusClient) {
352        self.client.set(client);
353    }
354
355    fn state(&self) -> Result<BusState, kernel::ErrorCode> {
356        if let State::NotInit = self.inner_state.get() {
357            return Err(ErrorCode::BUSY);
358        }
359        Ok(self.state.get())
360    }
361
362    fn init(&self) -> Result<(), ErrorCode> {
363        let State::NotInit = self.inner_state.get() else {
364            return Err(ErrorCode::ALREADY);
365        };
366
367        const CMD: u32 = cmd16(
368            Type::Read,
369            Function::Bus,
370            constants::REG_BUS_TEST_RO,
371            RegLen::Word as _,
372        );
373
374        self.read(Function::Bus, CMD)?;
375        self.inner_state.set(State::Init(0));
376        self.read.set((common::eq::<0xBEADFEED, 1, 0>, 0));
377        Ok(())
378    }
379
380    fn write_bytes(
381        &self,
382        mut buffer: SubSliceMut<'static, u8>,
383    ) -> Result<(), (kernel::ErrorCode, SubSliceMut<'static, u8>)> {
384        let Some((mut data, mut extra)) = Option::zip(self.data.take(), self.extra.take()) else {
385            return Err((ErrorCode::NOMEM, buffer));
386        };
387
388        let mut total_len = (buffer.len() + 3) & !3;
389        if total_len > MAX_PAYLOAD_SIZE {
390            return Err((ErrorCode::NOMEM, buffer));
391        } else if total_len == MAX_PACKET_SIZE {
392            total_len = 0;
393        }
394
395        // Construct command
396        let cmd = cmd32(Type::Write, Function::Wlan, 0x0, total_len as _);
397
398        let slice = data.as_mut_slice();
399        slice[..CMD_SIZE].copy_from_slice(&cmd.to_le_bytes());
400        slice[CMD_SIZE..][..buffer.len()].copy_from_slice(buffer.as_mut_slice());
401
402        extra.slice(..STATUS_SIZE);
403        data.slice(..CMD_SIZE + total_len);
404
405        if let Err((err, mut data, extra)) = self.gspi.read_write_bytes(data, Some(extra)) {
406            let mut extra = extra.unwrap();
407            reset_and_restore_bufs!(self, data, extra);
408            Err((err, buffer))
409        } else {
410            self.inner_state.set(State::Write);
411            self.wlan.set(buffer);
412            Ok(())
413        }
414    }
415
416    fn read_bytes(
417        &self,
418        mut buffer: SubSliceMut<'static, u8>,
419        len: usize,
420    ) -> Result<(), (kernel::ErrorCode, SubSliceMut<'static, u8>)> {
421        let Some((mut extra, mut data)) = Option::zip(self.extra.take(), self.data.take()) else {
422            return Err((ErrorCode::NOMEM, buffer));
423        };
424
425        let total_len = (len + 3) & !3;
426        if total_len > MAX_PACKET_SIZE {
427            return Err((ErrorCode::NOMEM, buffer));
428        }
429
430        let cmd = cmd32(
431            Type::Read,
432            Function::Wlan,
433            0,
434            if total_len == MAX_PAYLOAD_SIZE {
435                0
436            } else {
437                total_len as _
438            },
439        );
440
441        extra.slice(..CMD_SIZE);
442        extra[..CMD_SIZE].copy_from_slice(&cmd.to_le_bytes());
443        data.slice(0..total_len + STATUS_SIZE);
444
445        if let Err((err, mut extra, data)) = self.gspi.read_write_bytes(extra, Some(data)) {
446            let mut data = data.unwrap();
447            reset_and_restore_bufs!(self, data, extra);
448            Err((err, buffer))
449        } else {
450            buffer.slice(0..len);
451            self.wlan.set(buffer);
452
453            self.len.set(len);
454            self.inner_state.set(State::Read);
455            Ok(())
456        }
457    }
458}
459
460/// Command for reading the interrupt register
461pub(super) const IRQ_CAUSE_CMD: u32 = cmd32(
462    Type::Read,
463    Function::Bus,
464    constants::REG_BUS_INTERRUPT,
465    RegLen::HalfWord as _,
466);
467
468/// Command for writing the NVRAM magic number
469pub(super) const NVRAM_MAGIC_CMD: u32 = cmd32(
470    Type::Write,
471    Function::Backplane,
472    constants::NVRAM_END & constants::BACKPLANE_ADDRESS_MASK | constants::BACKPLANE_WINDOW_SIZE,
473    RegLen::Word as _,
474);
475
476impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> SpiMasterClient
477    for CYW4343xSpiBus<'a, S, A>
478{
479    fn read_write_done(
480        &self,
481        write_buffer: SubSliceMut<'static, u8>,
482        read_buffer: Option<SubSliceMut<'static, u8>>,
483        rval: Result<usize, ErrorCode>,
484    ) {
485        let rval = rval.map(|_| ());
486        let read_buffer = read_buffer.unwrap();
487
488        let (packet_available, packet_len) = {
489            let end = &read_buffer[read_buffer.len() - STATUS_SIZE..];
490            let status = u32::from_le_bytes([end[0], end[1], end[2], end[3]]);
491
492            // The status word indicates whether a F2 (WLAN) packet is available
493            // + contains the length of the available packet
494            (
495                status & constants::STATUS_F2_PKT_AVAILABLE != 0,
496                (status & constants::STATUS_F2_PKT_LEN_MASK) >> constants::STATUS_F2_PKT_LEN_SHIFT,
497            )
498        };
499
500        let irq_fired = self.irq_fired.take() && !packet_available;
501
502        self.state.set(if packet_available {
503            BusState::Available(packet_len as _)
504        } else if irq_fired {
505            BusState::Incoming
506        } else {
507            BusState::Idle
508        });
509
510        // Separate command/status and data buffer
511        let (mut extra, mut data) = if write_buffer.len() == CMD_SIZE {
512            (write_buffer, read_buffer)
513        } else {
514            (read_buffer, write_buffer)
515        };
516
517        match self.inner_state.take() {
518            State::Init(mut idx) => {
519                if let Err(err) = rval {
520                    reset_and_restore_bufs!(self, extra, data);
521                    self.client.map(|client| client.init_done(Err(err)));
522                    self.inner_state.set(State::NotInit);
523                    return;
524                }
525
526                if let Some((window, byte)) = self.backplane.pending_window.get() {
527                    if byte == 0 {
528                        self.backplane.curr_window.set(window);
529                        self.backplane.pending_window.clear();
530                    }
531                } else if !self.backplane.pending.take() {
532                    if let Some((jmp, pos)) = self.read.take() {
533                        let val = &data[pos as usize..];
534                        let val = u32::from_le_bytes([val[0], val[1], val[2], val[3]]);
535                        jmp(val, &mut idx)
536                    } else {
537                        idx += 1
538                    }
539                }
540
541                reset_and_restore_bufs!(self, extra, data);
542                if (idx as usize) < init::OPS.len() {
543                    self.do_task(init::OPS[idx as usize]).map_or_else(
544                        |err| {
545                            self.inner_state.set(State::NotInit);
546                            self.client.map(|client| client.init_done(Err(err)));
547                        },
548                        |()| self.inner_state.set(State::Init(idx)),
549                    )
550                } else {
551                    self.client.map(|client| {
552                        client.init_done(Ok(()));
553                    });
554                }
555            }
556            State::Write => {
557                let Some(wlan) = self.wlan.take() else {
558                    return;
559                };
560
561                reset_and_restore_bufs!(self, extra, data);
562                self.client.map(|client| {
563                    if irq_fired && self.read(Function::Bus, IRQ_CAUSE_CMD).is_ok() {
564                        self.inner_state.set(State::Irq);
565                    }
566                    client.write_bytes_done(wlan, rval);
567                });
568            }
569            State::Read => {
570                let Some(mut wlan) = self.wlan.take() else {
571                    return;
572                };
573                let len = wlan.len();
574                wlan.as_mut_slice()
575                    .copy_from_slice(&data.as_mut_slice()[..len]);
576
577                reset_and_restore_bufs!(self, extra, data);
578                self.client.map(|client| {
579                    if irq_fired && self.read(Function::Bus, IRQ_CAUSE_CMD).is_ok() {
580                        self.inner_state.set(State::Irq);
581                    }
582                    client.read_bytes_done(wlan, rval);
583                });
584            }
585            State::Irq => {
586                let irq = u16::from_le_bytes([data[0], data[1]]);
587                let pending = irq & constants::IRQ_F2_PACKET_AVAILABLE as u16 != 0;
588
589                reset_and_restore_bufs!(self, extra, data);
590                self.client.map(|client| {
591                    client.packet_available(if pending || packet_available {
592                        packet_len as _
593                    } else {
594                        0
595                    })
596                });
597            }
598            _ => unreachable!(),
599        }
600    }
601}
602
603impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> time::AlarmClient
604    for CYW4343xSpiBus<'a, S, A>
605{
606    fn alarm(&self) {
607        let State::Init(mut idx) = self.inner_state.get() else {
608            return;
609        };
610
611        idx += 1;
612
613        self.do_task(init::OPS[idx as usize]).map_or_else(
614            |err| {
615                self.client.map(|client| client.init_done(Err(err)));
616                self.inner_state.set(State::NotInit);
617            },
618            |()| self.inner_state.set(State::Init(idx)),
619        )
620    }
621}
622
623// Implementation of the `gpio` Client trait.
624// The WiFi chip should signal on the interrupt line when a WLAN (F2) packet is ready to be read.
625impl<'a, S: SpiMasterDevice<'a>, A: kernel::hil::time::Alarm<'a>> gpio::Client
626    for CYW4343xSpiBus<'a, S, A>
627{
628    fn fired(&self) {
629        if let State::Init(_) | State::NotInit = self.inner_state.get() {
630            return;
631        }
632
633        if let State::Idle = self.inner_state.get() {
634            if self.read(Function::Bus, IRQ_CAUSE_CMD).is_ok() {
635                self.inner_state.set(State::Irq);
636                self.state.set(BusState::Incoming);
637            }
638        } else {
639            self.irq_fired.set(true)
640        }
641    }
642}
643
644mod init {
645    use crate::cyw4343::{bus, constants};
646    use bus::common;
647    use bus::spi::{bus_init, wakeup};
648    use bus::RegLen::Word;
649
650    macro_rules! copy_from_arr {
651        (task => $curr:expr, $to:expr, $from:expr) => {
652            let mut __idx = 0;
653            while __idx < $from.len() {
654                $to[$curr + __idx] = GspiTask::from($from[__idx]);
655                __idx += 1;
656            }
657            $curr += __idx;
658        };
659
660        ($curr:expr, $to:expr, $from:expr) => {
661            let mut __idx = 0;
662            while __idx < $from.len() {
663                $to[$curr + __idx] = $from[__idx];
664                __idx += 1;
665            }
666            $curr += __idx;
667        };
668    }
669    use super::task::GspiTask;
670
671    pub(crate) static OPS: [GspiTask; 55] = const {
672        let mut curr = 0;
673        let mut bytes = [GspiTask::WaitMs(0);
674            bus_init::OPS.len()
675                + WLAN_DISABLE.len()
676                + SOCRAM_DISABLE.len()
677                + SOCRAM_RESET.len()
678                + 5
679                + WLAN_RESET.len()
680                + wakeup::OPS.len()];
681        // 1. Bus init
682        copy_from_arr!(curr, bytes, bus_init::OPS); // 0..13
683
684        // 2. Wlan core disable
685        const WLAN_DISABLE: [common::BackplaneTask; 7] =
686            common::core_disable::ops::<{ constants::WLAN_ARM_CORE_BASE_ADDR }>();
687        copy_from_arr!(task => curr, bytes, WLAN_DISABLE); // 13..20
688
689        // 3. SoC RAM core disable
690        const SOCRAM_DISABLE: [common::BackplaneTask; 7] =
691            common::core_disable::ops::<{ constants::SOCRAM_CORE_BASE_ADDR }>();
692        copy_from_arr!(task => curr, bytes, SOCRAM_DISABLE); // 20..27
693
694        // 4. SoC RAM core reset
695        const SOCRAM_RESET: [common::BackplaneTask; 7] =
696            common::core_reset::ops::<{ constants::SOCRAM_CORE_BASE_ADDR }>();
697        copy_from_arr!(task => curr, bytes, SOCRAM_RESET); // 27..34
698
699        // 5. Disable remap
700        bytes[curr] = GspiTask::write_bp(0x18004000 + 0x10, Word, 3); // 34
701        bytes[curr + 1] = GspiTask::write_bp(0x18004000 + 0x44, Word, 0); // 35
702
703        // 6. Load firmware, load nvram, load nvram magic
704        bytes[curr + 2] = GspiTask::Fw; // 36
705        bytes[curr + 3] = GspiTask::Nvram; // 37
706        bytes[curr + 4] = GspiTask::NvramMagic; // 38
707        curr += 5;
708
709        // 7. WLAN core reset
710        const WLAN_RESET: [common::BackplaneTask; 7] =
711            common::core_reset::ops::<{ constants::WLAN_ARM_CORE_BASE_ADDR }>();
712        copy_from_arr!(task => curr, bytes, WLAN_RESET); // 39..46
713
714        // 8. Wakeup sequence
715        copy_from_arr!(curr, bytes, wakeup::OPS); // 46..55
716        let _ = curr;
717
718        bytes
719    };
720}
721
722mod bus_init {
723    use super::task::GspiTask;
724    use crate::cyw4343::{bus, constants};
725    use bus::common::{eq, mask};
726    use bus::Function::{Backplane as Bp, Bus};
727    use bus::RegLen::{Byte, Word};
728
729    pub(super) static OPS: [GspiTask; 12] = [
730        // First test
731        GspiTask::read16(
732            Bus,
733            constants::REG_BUS_TEST_RO,
734            Word,
735            Some(eq::<0xBEADFEED, 1, 0>),
736        ),
737        GspiTask::write16(Bus, constants::REG_BUS_TEST_RW, Word, 0x12345678),
738        GspiTask::read16(
739            Bus,
740            constants::REG_BUS_TEST_RW,
741            Word,
742            Some(eq::<0x56781234, 1, 0>),
743        ),
744        // Configure bus
745        GspiTask::write16(Bus, constants::REG_BUS_CTRL, Word, constants::CONFIG_DATA),
746        // Second test
747        GspiTask::read32(
748            Bus,
749            constants::REG_BUS_TEST_RO,
750            Word,
751            Some(eq::<0xFEEDBEAD, 1, 0>),
752        ),
753        // Interrupts
754        GspiTask::write32(
755            Bus,
756            constants::REG_BUS_INTERRUPT,
757            Byte,
758            constants::INTR_STATUS_RESET,
759        ),
760        GspiTask::write32(
761            Bus,
762            constants::REG_BUS_INTERRUPT_ENABLE,
763            Byte,
764            constants::INTR_ENABLE_RESET,
765        ),
766        GspiTask::write32(
767            Bp,
768            constants::REG_BACKPLANE_CHIP_CLOCK_CSR,
769            Byte,
770            constants::BACKPLANE_ALP_AVAIL_REQ as u32,
771        ),
772        GspiTask::write32(Bp, constants::REG_BACKPLANE_FUNCTION2_WATERMARK, Byte, 0x10),
773        GspiTask::read32(Bp, constants::REG_BACKPLANE_FUNCTION2_WATERMARK, Byte, None),
774        GspiTask::read32(
775            Bp,
776            constants::REG_BACKPLANE_CHIP_CLOCK_CSR,
777            Byte,
778            Some(mask::<{ constants::BACKPLANE_ALP_AVAIL }, 0, 1>),
779        ),
780        GspiTask::write32(Bp, constants::REG_BACKPLANE_CHIP_CLOCK_CSR, Byte, 0x0),
781    ];
782}
783
784mod wakeup {
785    use super::task::GspiTask;
786    use crate::cyw4343::{bus, constants};
787    use bus::common::mask;
788    use bus::Function::{Backplane as Bp, Bus};
789    use bus::RegLen::{Byte, HalfWord, Word};
790
791    pub(super) static OPS: [GspiTask; 10] = [
792        GspiTask::WaitMs(30),
793        GspiTask::read32(
794            Bp,
795            constants::REG_BACKPLANE_CHIP_CLOCK_CSR,
796            Byte,
797            Some(mask::<0x80, 0, 1>),
798        ),
799        GspiTask::write_bp(
800            constants::SDIOD_CORE_BASE_ADDRESS + constants::SDIO_INT_HOST_MASK,
801            Word,
802            constants::I_HMB_SW_MASK,
803        ),
804        GspiTask::write32(
805            Bus,
806            constants::REG_BUS_INTERRUPT_ENABLE,
807            HalfWord,
808            constants::IRQ_F2_PACKET_AVAILABLE,
809        ),
810        GspiTask::write32(
811            Bp,
812            constants::REG_BACKPLANE_FUNCTION2_WATERMARK,
813            Byte,
814            constants::SPI_F2_WATERMARK,
815        ),
816        GspiTask::read32(
817            Bus,
818            constants::REG_BUS_STATUS,
819            Word,
820            Some(mask::<{ constants::STATUS_F2_RX_READY }, 0, 1>),
821        ),
822        GspiTask::write32(Bp, constants::REG_BACKPLANE_PULL_UP, Byte, 0x0),
823        GspiTask::read32(Bp, constants::REG_BACKPLANE_PULL_UP, Byte, None),
824        GspiTask::write32(Bp, constants::REG_BACKPLANE_CHIP_CLOCK_CSR, Byte, 0x10),
825        GspiTask::read32(
826            Bp,
827            constants::REG_BACKPLANE_CHIP_CLOCK_CSR,
828            Byte,
829            Some(mask::<0x80, 0, 1>),
830        ),
831    ];
832}
833
834mod task {
835    use super::{cmd16, cmd32};
836    use crate::cyw4343::{bus, constants};
837    use bus::{common, Function, RegLen, Type};
838
839    type Cmd = u32;
840    type Addr = u32;
841
842    /// GSPI task
843    #[derive(Clone, Copy, Debug)]
844    pub(super) enum GspiTask {
845        Read(Function, Cmd, Option<fn(u32, &mut u8) -> ()>),
846        Write(Cmd, u32),
847        ReadBackplane(Cmd, Addr, Option<fn(u32, &mut u8) -> ()>),
848        WriteBackplane(Cmd, Addr, u32),
849        WaitMs(u32),
850        Fw,
851        Nvram,
852        NvramMagic,
853    }
854
855    impl GspiTask {
856        pub(super) const fn from(value: common::BackplaneTask) -> Self {
857            match value {
858                common::BackplaneTask::Read(len, addr, jmp) => Self::read_bp(addr, len, jmp),
859                common::BackplaneTask::Write(len, addr, val) => Self::write_bp(addr, len, val),
860                common::BackplaneTask::WaitMs(ms) => Self::WaitMs(ms),
861            }
862        }
863
864        pub(super) const fn read16(
865            fun: Function,
866            addr: u32,
867            len: RegLen,
868            jmp: Option<fn(u32, &mut u8) -> ()>,
869        ) -> Self {
870            Self::Read(fun, cmd16(Type::Read, fun, addr, len as _), jmp)
871        }
872
873        pub(super) const fn write16(fun: Function, addr: u32, len: RegLen, mut val: u32) -> Self {
874            if let RegLen::Word = len {
875                val = val.rotate_left(16);
876            }
877            Self::Write(cmd16(Type::Write, fun, addr, len as _), val)
878        }
879
880        pub(super) const fn read32(
881            fun: Function,
882            addr: u32,
883            len: RegLen,
884            jmp: Option<fn(u32, &mut u8)>,
885        ) -> Self {
886            Self::Read(fun, cmd32(Type::Read, fun, addr, len as _), jmp)
887        }
888
889        pub(super) const fn write32(fun: Function, addr: u32, len: RegLen, val: u32) -> Self {
890            Self::Write(cmd32(Type::Write, fun, addr, len as _), val)
891        }
892
893        pub(super) const fn read_bp(addr: u32, len: RegLen, jmp: Option<fn(u32, &mut u8)>) -> Self {
894            let mut cmd_addr = addr & constants::BACKPLANE_ADDRESS_MASK;
895            if let RegLen::Word = len {
896                cmd_addr |= constants::BACKPLANE_WINDOW_SIZE
897            }
898
899            Self::ReadBackplane(
900                cmd32(Type::Read, Function::Backplane, cmd_addr, len as _),
901                addr,
902                jmp,
903            )
904        }
905
906        pub(super) const fn write_bp(addr: u32, len: RegLen, val: u32) -> Self {
907            assert!(addr.is_multiple_of(4));
908
909            let mut cmd_addr = addr & constants::BACKPLANE_ADDRESS_MASK;
910            if let RegLen::Word = len {
911                cmd_addr |= constants::BACKPLANE_WINDOW_SIZE
912            }
913
914            Self::WriteBackplane(
915                cmd32(Type::Write, Function::Backplane, cmd_addr, len as _),
916                addr,
917                val,
918            )
919        }
920    }
921}