1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 
11 #include "hardware/gpio.h"
12 #include "hardware/pio.h"
13 #include "hardware/clocks.h"
14 #include "hardware/sync.h"
15 #include "hardware/dma.h"
16 #include "cyw43_bus_pio_spi.pio.h"
17 #include "cyw43.h"
18 #include "cyw43_internal.h"
19 #include "cyw43_spi.h"
20 #include "cyw43_debug_pins.h"
21 #include "pico/cyw43_driver.h"
22 
23 #if CYW43_SPI_PIO
24 
25 #define IRQ_SAMPLE_DELAY_NS 100
26 
27 #if !CYW43_PIN_WL_DYNAMIC && PICO_PIO_USE_GPIO_BASE
28 // The pins should all work in the same gpio base
29 static_assert((CYW43_PIN_WL_DATA_OUT < 32 && CYW43_PIN_WL_DATA_IN < 32 && CYW43_PIN_WL_CLOCK < 32) ||
30     (CYW43_PIN_WL_DATA_OUT >= 16 && CYW43_PIN_WL_DATA_IN >= 16 && CYW43_PIN_WL_CLOCK >= 16), "");
31 #endif
32 
33 #ifdef CYW43_SPI_PROGRAM_NAME
34 #define SPI_PROGRAM_NAME CYW43_SPI_PROGRAM_NAME
35 #else
36 //#define SPI_PROGRAM_NAME spi_gap0_sample1 // for lower cpu speed
37 #define SPI_PROGRAM_NAME spi_gap01_sample0 // for high cpu speed
38 #endif
39 #define SPI_PROGRAM_FUNC __CONCAT(SPI_PROGRAM_NAME, _program)
40 #define SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC __CONCAT(SPI_PROGRAM_NAME, _program_get_default_config)
41 #define SPI_OFFSET_END __CONCAT(SPI_PROGRAM_NAME, _offset_end)
42 #define SPI_OFFSET_LP1_END __CONCAT(SPI_PROGRAM_NAME, _offset_lp1_end)
43 
44 #if !CYW43_PIO_CLOCK_DIV_DYNAMIC
45 #define cyw43_pio_clock_div_int CYW43_PIO_CLOCK_DIV_INT
46 #define cyw43_pio_clock_div_frac8 CYW43_PIO_CLOCK_DIV_FRAC8
47 #else
48 static uint32_t cyw43_pio_clock_div_int = CYW43_PIO_CLOCK_DIV_INT;
49 static uint8_t cyw43_pio_clock_div_frac8 = CYW43_PIO_CLOCK_DIV_FRAC8;
50 
cyw43_set_pio_clkdiv_int_frac8(uint32_t clock_div_int,uint8_t clock_div_frac8)51 void cyw43_set_pio_clkdiv_int_frac8(uint32_t clock_div_int, uint8_t clock_div_frac8) {
52     cyw43_pio_clock_div_int = clock_div_int;
53     cyw43_pio_clock_div_frac8 = clock_div_frac8;
54 }
55 #endif
56 
57 #define PADS_DRIVE_STRENGTH PADS_BANK0_GPIO0_DRIVE_VALUE_12MA
58 
59 #if !CYW43_USE_SPI
60 #error CYW43_USE_SPI should be true
61 #endif
62 
63 #ifndef NDEBUG
64 //#define ENABLE_SPI_DUMPING 1
65 #endif
66 
67 // Set to 1 to enable
68 #if ENABLE_SPI_DUMPING //NDEBUG
69 #if 0
70 #define DUMP_SPI_TRANSACTIONS(A) A
71 #else
72 static bool enable_spi_packet_dumping; // set to true to dump
73 #define DUMP_SPI_TRANSACTIONS(A) if (enable_spi_packet_dumping) {A}
74 #endif
75 
76 static uint32_t counter = 0;
77 #else
78 #define DUMP_SPI_TRANSACTIONS(A)
79 #endif
80 
81 //#define SWAP32(A) ((((A) & 0xff000000U) >> 8) | (((A) & 0xff0000U) << 8) | (((A) & 0xff00U) >> 8) | (((A) & 0xffU) << 8))
__swap16x2(uint32_t a)82 __force_inline static uint32_t __swap16x2(uint32_t a) {
83 #ifndef __riscv
84     pico_default_asm ("rev16 %0, %0" : "+l" (a) : : );
85 #else
86     uint32_t tmp;
87     pico_default_asm (
88         "rev8 %1, %0\n"
89         "rori %0, %1, 16\n"
90         : "+l" (a), "=l" (tmp));
91 #endif
92     return a;
93 }
94 #define SWAP32(a) __swap16x2(a)
95 
96 typedef struct {
97     PIO pio;
98     uint pio_offset;
99     uint pio_sm;
100     int8_t dma_out;
101     int8_t dma_in;
102 } bus_data_t;
103 
104 static bus_data_t bus_data_instance;
105 
cyw43_spi_init(cyw43_int_t * self)106 int cyw43_spi_init(cyw43_int_t *self) {
107     // Only does something if CYW43_LOGIC_DEBUG=1
108     logic_debug_init();
109 
110     assert(!self->bus_data);
111     self->bus_data = &bus_data_instance;
112     bus_data_t *bus_data = (bus_data_t *)self->bus_data;
113     bus_data->pio = NULL;
114     bus_data->dma_in = -1;
115     bus_data->dma_out = -1;
116 
117     const uint min_gpio = MIN(CYW43_PIN_WL_CLOCK, MIN(CYW43_PIN_WL_DATA_IN, CYW43_PIN_WL_DATA_OUT));
118     const uint max_gpio = MAX(CYW43_PIN_WL_CLOCK, MAX(CYW43_PIN_WL_DATA_IN, CYW43_PIN_WL_DATA_OUT));
119     if (!pio_claim_free_sm_and_add_program_for_gpio_range(&SPI_PROGRAM_FUNC, &bus_data->pio, &bus_data->pio_sm, &bus_data->pio_offset, min_gpio, max_gpio - min_gpio + 1, true)) {
120         cyw43_spi_deinit(self);
121         return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
122     }
123     pio_sm_config config = SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC(bus_data->pio_offset);
124 
125     sm_config_set_clkdiv_int_frac8(&config, cyw43_pio_clock_div_int, cyw43_pio_clock_div_frac8);
126     hw_write_masked(&pads_bank0_hw->io[CYW43_PIN_WL_CLOCK],
127                     (uint)PADS_DRIVE_STRENGTH << PADS_BANK0_GPIO0_DRIVE_LSB,
128                     PADS_BANK0_GPIO0_DRIVE_BITS
129     );
130     hw_write_masked(&pads_bank0_hw->io[CYW43_PIN_WL_CLOCK],
131                     (uint)1 << PADS_BANK0_GPIO0_SLEWFAST_LSB,
132                     PADS_BANK0_GPIO0_SLEWFAST_BITS
133     );
134 
135     sm_config_set_out_pins(&config, CYW43_PIN_WL_DATA_OUT, 1);
136     sm_config_set_in_pins(&config, CYW43_PIN_WL_DATA_IN);
137     sm_config_set_set_pins(&config, CYW43_PIN_WL_DATA_OUT, 1);
138     sm_config_set_sideset(&config, 1, false, false);
139     sm_config_set_sideset_pins(&config, CYW43_PIN_WL_CLOCK);
140     sm_config_set_in_shift(&config, false, true, 32);
141     sm_config_set_out_shift(&config, false, true, 32);
142     hw_set_bits(&bus_data->pio->input_sync_bypass, 1u << (CYW43_PIN_WL_DATA_IN - pio_get_gpio_base(bus_data->pio)));
143     pio_sm_set_config(bus_data->pio, bus_data->pio_sm, &config);
144     pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, CYW43_PIN_WL_CLOCK, 1, true);
145     gpio_set_function(CYW43_PIN_WL_DATA_OUT, pio_get_funcsel(bus_data->pio));
146 
147     // Set data pin to pull down and schmitt
148     gpio_set_pulls(CYW43_PIN_WL_DATA_IN, false, true);
149     gpio_set_input_hysteresis_enabled(CYW43_PIN_WL_DATA_IN, true);
150 
151     pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_set(pio_pins, 1));
152 
153     bus_data->dma_out = (int8_t) dma_claim_unused_channel(false);
154     bus_data->dma_in = (int8_t) dma_claim_unused_channel(false);
155     if (bus_data->dma_out < 0 || bus_data->dma_in < 0) {
156         cyw43_spi_deinit(self);
157         return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
158     }
159     return 0;
160 }
161 
cyw43_spi_deinit(cyw43_int_t * self)162 void cyw43_spi_deinit(cyw43_int_t *self) {
163     if (self->bus_data) {
164         bus_data_t *bus_data = (bus_data_t *)self->bus_data;
165         if (bus_data->pio) {
166             pio_remove_program_and_unclaim_sm(&SPI_PROGRAM_FUNC, bus_data->pio, bus_data->pio_sm, bus_data->pio_offset);
167             bus_data->pio = NULL;
168         }
169         if (bus_data->dma_out >= 0) {
170             dma_channel_cleanup(bus_data->dma_out);
171             dma_channel_unclaim(bus_data->dma_out);
172             bus_data->dma_out = -1;
173         }
174         if (bus_data->dma_in >= 0) {
175             dma_channel_cleanup(bus_data->dma_in);
176             dma_channel_unclaim(bus_data->dma_in);
177             bus_data->dma_in = -1;
178         }
179         self->bus_data = NULL;
180     }
181 }
182 
cs_set(bool value)183 static void cs_set(bool value) {
184     gpio_put(CYW43_PIN_WL_CS, value);
185 }
186 
ns_delay(uint32_t ns)187 static __noinline void ns_delay(uint32_t ns) {
188     // cycles = ns * clk_sys_hz / 1,000,000,000
189     uint32_t cycles = ns * (clock_get_hz(clk_sys) >> 16u) / (1000000000u >> 16u);
190     busy_wait_at_least_cycles(cycles);
191 }
192 
start_spi_comms(cyw43_int_t * self)193 static void start_spi_comms(cyw43_int_t *self) {
194     bus_data_t *bus_data = (bus_data_t *)self->bus_data;
195     gpio_set_function(CYW43_PIN_WL_DATA_OUT, pio_get_funcsel(bus_data->pio));
196     gpio_set_function(CYW43_PIN_WL_CLOCK, pio_get_funcsel(bus_data->pio));
197     gpio_pull_down(CYW43_PIN_WL_CLOCK);
198     // Pull CS low
199     cs_set(false);
200 }
201 
202 // we need to atomically de-assert CS and enable IRQ
stop_spi_comms(void)203 static void stop_spi_comms(void) {
204     // from this point a positive edge will cause an IRQ to be pending
205     cs_set(true);
206 
207     // we need to wait a bit in case the irq line is incorrectly high
208     ns_delay(IRQ_SAMPLE_DELAY_NS);
209 }
210 
211 #if ENABLE_SPI_DUMPING
dump_bytes(const uint8_t * bptr,uint32_t len)212 static void dump_bytes(const uint8_t *bptr, uint32_t len) {
213     unsigned int i = 0;
214 
215     for (i = 0; i < len;) {
216         if ((i & 0x0f) == 0) {
217             printf("\n");
218         } else if ((i & 0x07) == 0) {
219             printf(" ");
220         }
221         printf("%02x ", bptr[i++]);
222     }
223     printf("\n");
224 }
225 #endif
226 
cyw43_spi_transfer(cyw43_int_t * self,const uint8_t * tx,size_t tx_length,uint8_t * rx,size_t rx_length)227 int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_length, uint8_t *rx,
228                        size_t rx_length) {
229 
230     if ((tx == NULL) && (rx == NULL)) {
231         return CYW43_FAIL_FAST_CHECK(-CYW43_EINVAL);
232     }
233 
234     bus_data_t *bus_data = (bus_data_t *)self->bus_data;
235     start_spi_comms(self);
236     if (rx != NULL) {
237         if (tx == NULL) {
238             tx = rx;
239             assert(tx_length && tx_length < rx_length);
240         }
241         DUMP_SPI_TRANSACTIONS(
242                 printf("[%lu] bus TX/RX %u bytes rx %u:", counter++, tx_length, rx_length);
243                 dump_bytes(tx, tx_length);
244         )
245         assert(!(tx_length & 3));
246         assert(!(((uintptr_t)tx) & 3));
247         assert(!(((uintptr_t)rx) & 3));
248         assert(!(rx_length & 3));
249 
250         pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
251         pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_END - 1);
252         pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
253         pio_sm_set_pindirs_with_mask64(bus_data->pio, bus_data->pio_sm, 1ull << CYW43_PIN_WL_DATA_OUT, 1ull << CYW43_PIN_WL_DATA_OUT);
254         pio_sm_restart(bus_data->pio, bus_data->pio_sm);
255         pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
256         pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
257         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
258         pio_sm_put(bus_data->pio, bus_data->pio_sm, (rx_length - tx_length) * 8 - 1);
259         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
260         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
261         dma_channel_abort(bus_data->dma_out);
262         dma_channel_abort(bus_data->dma_in);
263 
264         dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
265         channel_config_set_bswap(&out_config, true);
266         channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
267 
268         dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
269 
270         dma_channel_config in_config = dma_channel_get_default_config(bus_data->dma_in);
271         channel_config_set_bswap(&in_config, true);
272         channel_config_set_dreq(&in_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, false));
273         channel_config_set_write_increment(&in_config, true);
274         channel_config_set_read_increment(&in_config, false);
275         dma_channel_configure(bus_data->dma_in, &in_config, rx + tx_length, &bus_data->pio->rxf[bus_data->pio_sm], rx_length / 4 - tx_length / 4, true);
276 
277         pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
278         __compiler_memory_barrier();
279 
280         dma_channel_wait_for_finish_blocking(bus_data->dma_out);
281         dma_channel_wait_for_finish_blocking(bus_data->dma_in);
282 
283         __compiler_memory_barrier();
284         memset(rx, 0, tx_length); // make sure we don't have garbage in what would have been returned data if using real SPI
285     } else if (tx != NULL) {
286         DUMP_SPI_TRANSACTIONS(
287                 printf("[%lu] bus TX only %u bytes:", counter++, tx_length);
288                 dump_bytes(tx, tx_length);
289         )
290         assert(!(((uintptr_t)tx) & 3));
291         assert(!(tx_length & 3));
292         pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
293         pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_LP1_END - 1);
294         pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
295         pio_sm_set_pindirs_with_mask64(bus_data->pio, bus_data->pio_sm, 1ull << CYW43_PIN_WL_DATA_OUT, 1ull << CYW43_PIN_WL_DATA_OUT);
296         pio_sm_restart(bus_data->pio, bus_data->pio_sm);
297         pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
298         pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
299         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
300         pio_sm_put(bus_data->pio, bus_data->pio_sm, 0);
301         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
302         pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
303         dma_channel_abort(bus_data->dma_out);
304 
305         dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
306         channel_config_set_bswap(&out_config, true);
307         channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
308 
309         dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
310 
311         uint32_t fdebug_tx_stall = 1u << (PIO_FDEBUG_TXSTALL_LSB + bus_data->pio_sm);
312         bus_data->pio->fdebug = fdebug_tx_stall;
313         pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
314         while (!(bus_data->pio->fdebug & fdebug_tx_stall)) {
315             tight_loop_contents(); // todo timeout
316         }
317         __compiler_memory_barrier();
318         pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
319         pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, CYW43_PIN_WL_DATA_IN, 1, false);
320     } else if (rx != NULL) { /* currently do one at a time */
321         DUMP_SPI_TRANSACTIONS(
322                 printf("[%lu] bus TX %u bytes:", counter++, rx_length);
323                 dump_bytes(rx, rx_length);
324         )
325         panic_unsupported();
326     }
327     pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_mov(pio_pins, pio_null)); // for next time we turn output on
328 
329     stop_spi_comms();
330     DUMP_SPI_TRANSACTIONS(
331             printf("RXed:");
332             dump_bytes(rx, rx_length);
333             printf("\n");
334     )
335 
336     return 0;
337 }
338 
339 // Initialise our gpios
cyw43_spi_gpio_setup(void)340 void cyw43_spi_gpio_setup(void) {
341     // Setup CYW43_PIN_WL_REG_ON (23)
342     gpio_init(CYW43_PIN_WL_REG_ON);
343     gpio_set_dir(CYW43_PIN_WL_REG_ON, GPIO_OUT);
344     gpio_pull_up(CYW43_PIN_WL_REG_ON);
345 
346     // Setup DO, DI and IRQ (24)
347     gpio_init(CYW43_PIN_WL_DATA_OUT);
348     gpio_set_dir(CYW43_PIN_WL_DATA_OUT, GPIO_OUT);
349     gpio_put(CYW43_PIN_WL_DATA_OUT, false);
350 
351     // Setup CS (25)
352     gpio_init(CYW43_PIN_WL_CS);
353     gpio_set_dir(CYW43_PIN_WL_CS, GPIO_OUT);
354     gpio_put(CYW43_PIN_WL_CS, true);
355 }
356 
357 // Reset wifi chip
cyw43_spi_reset(void)358 void cyw43_spi_reset(void) {
359     gpio_put(CYW43_PIN_WL_REG_ON, false); // off
360     sleep_ms(20);
361     gpio_put(CYW43_PIN_WL_REG_ON, true); // on
362     sleep_ms(250);
363 
364     // Setup IRQ (24) - also used for DO, DI
365     gpio_init(CYW43_PIN_WL_HOST_WAKE);
366     gpio_set_dir(CYW43_PIN_WL_HOST_WAKE, GPIO_IN);
367 }
368 
make_cmd(bool write,bool inc,uint32_t fn,uint32_t addr,uint32_t sz)369 static inline uint32_t make_cmd(bool write, bool inc, uint32_t fn, uint32_t addr, uint32_t sz) {
370     return write << 31 | inc << 30 | fn << 28 | (addr & 0x1ffff) << 11 | sz;
371 }
372 
373 #if CYW43_VERBOSE_DEBUG
func_name(int fn)374 static const char *func_name(int fn) {
375     switch (fn)
376     {
377         case BUS_FUNCTION:
378             return "BUS_FUNCTION";
379         case BACKPLANE_FUNCTION:
380             return "BACKPLANE_FUNCTION";
381         case WLAN_FUNCTION:
382             return "WLAN_FUNCTION";
383         default:
384             return "UNKNOWN";
385     }
386 }
387 #endif
388 
read_reg_u32_swap(cyw43_int_t * self,uint32_t fn,uint32_t reg)389 uint32_t read_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
390     uint32_t buf[2] = {0};
391     assert(fn != BACKPLANE_FUNCTION);
392     buf[0] = SWAP32(make_cmd(false, true, fn, reg, 4));
393     int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)buf, 8);
394     if (ret != 0) {
395         return ret;
396     }
397     return SWAP32(buf[1]);
398 }
399 
_cyw43_read_reg(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint size)400 static inline uint32_t _cyw43_read_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint size) {
401     // Padding plus max read size of 32 bits + another 4?
402     static_assert(CYW43_BACKPLANE_READ_PAD_LEN_BYTES % 4 == 0, "");
403     int index = (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4) + 1 + 1;
404     uint32_t buf32[index];
405     uint8_t *buf = (uint8_t *)buf32;
406     const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? CYW43_BACKPLANE_READ_PAD_LEN_BYTES : 0; // Add response delay
407     buf32[0] = make_cmd(false, true, fn, reg, size);
408 
409     if (fn == BACKPLANE_FUNCTION) {
410         logic_debug_set(pin_BACKPLANE_READ, 1);
411     }
412     int ret = cyw43_spi_transfer(self, NULL, 4, buf, 8 + padding);
413     if (fn == BACKPLANE_FUNCTION) {
414         logic_debug_set(pin_BACKPLANE_READ, 0);
415     }
416 
417     if (ret != 0) {
418         return ret;
419     }
420     uint32_t result = buf32[padding > 0 ? index - 1 : 1];
421     CYW43_VDEBUG("cyw43_read_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, result);
422     return result;
423 }
424 
cyw43_read_reg_u32(cyw43_int_t * self,uint32_t fn,uint32_t reg)425 uint32_t cyw43_read_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
426     return _cyw43_read_reg(self, fn, reg, 4);
427 }
428 
cyw43_read_reg_u16(cyw43_int_t * self,uint32_t fn,uint32_t reg)429 int cyw43_read_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
430     return _cyw43_read_reg(self, fn, reg, 2);
431 }
432 
cyw43_read_reg_u8(cyw43_int_t * self,uint32_t fn,uint32_t reg)433 int cyw43_read_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
434     return _cyw43_read_reg(self, fn, reg, 1);
435 }
436 
437 // This is only used to switch the word order on boot
write_reg_u32_swap(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint32_t val)438 int write_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
439     uint32_t buf[2];
440     // Boots up in little endian so command needs swapping too
441     buf[0] = SWAP32(make_cmd(true, true, fn, reg, 4));
442     buf[1] = SWAP32(val);
443     int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
444     CYW43_VDEBUG("write_reg_u32_swap %s 0x%lx=0x%lx\n", func_name(fn), reg, val);
445     return ret;
446 }
447 
_cyw43_write_reg(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint32_t val,uint size)448 static inline int _cyw43_write_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val, uint size) {
449     uint32_t buf[2];
450     buf[0] = make_cmd(true, true, fn, reg, size);
451     buf[1] = val;
452     if (fn == BACKPLANE_FUNCTION) {
453         // In case of f1 overflow
454         self->last_size = 8;
455         self->last_header[0] = buf[0];
456         self->last_header[1] = buf[1];
457         self->last_backplane_window = self->cur_backplane_window;
458     }
459 
460     if (fn == BACKPLANE_FUNCTION) {
461         logic_debug_set(pin_BACKPLANE_WRITE, 1);
462     }
463 
464     int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
465 
466     if (fn == BACKPLANE_FUNCTION) {
467         logic_debug_set(pin_BACKPLANE_WRITE, 0);
468     }
469 
470     CYW43_VDEBUG("cyw43_write_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, val);
471     return ret;
472 }
473 
cyw43_write_reg_u32(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint32_t val)474 int cyw43_write_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
475     return _cyw43_write_reg(self, fn, reg, val, 4);
476 }
477 
cyw43_write_reg_u16(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint16_t val)478 int cyw43_write_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint16_t val) {
479     return _cyw43_write_reg(self, fn, reg, val, 2);
480 }
481 
cyw43_write_reg_u8(cyw43_int_t * self,uint32_t fn,uint32_t reg,uint32_t val)482 int cyw43_write_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
483     return _cyw43_write_reg(self, fn, reg, val, 1);
484 }
485 
486 #if CYW43_BUS_MAX_BLOCK_SIZE > 0x7f8
487 #error Block size is wrong for SPI
488 #endif
489 
cyw43_read_bytes(cyw43_int_t * self,uint32_t fn,uint32_t addr,size_t len,uint8_t * buf)490 int cyw43_read_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, uint8_t *buf) {
491     assert(fn != BACKPLANE_FUNCTION || (len <= CYW43_BUS_MAX_BLOCK_SIZE));
492     const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? CYW43_BACKPLANE_READ_PAD_LEN_BYTES : 0; // Add response delay
493     size_t aligned_len = (len + 3) & ~3;
494     assert(aligned_len > 0 && aligned_len <= 0x7f8);
495     assert(buf == self->spid_buf || buf < self->spid_buf || buf >= (self->spid_buf + sizeof(self->spid_buf)));
496     self->spi_header[padding > 0 ? 0 : (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(false, true, fn, addr, len);
497     if (fn == WLAN_FUNCTION) {
498         logic_debug_set(pin_WIFI_RX, 1);
499     }
500     int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)&self->spi_header[padding > 0 ? 0 : (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4 + padding);
501     if (fn == WLAN_FUNCTION) {
502         logic_debug_set(pin_WIFI_RX, 0);
503     }
504     if (ret != 0) {
505         CYW43_PRINTF("cyw43_read_bytes error %d", ret);
506         return ret;
507     }
508     if (buf != self->spid_buf) { // avoid a copy in the usual case just to add the header
509         memcpy(buf, self->spid_buf, len);
510     }
511     return 0;
512 }
513 
514 // See whd_bus_spi_transfer_bytes
515 // Note, uses spid_buf if src isn't using it already
516 // Apart from firmware download this appears to only be used for wlan functions?
cyw43_write_bytes(cyw43_int_t * self,uint32_t fn,uint32_t addr,size_t len,const uint8_t * src)517 int cyw43_write_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, const uint8_t *src) {
518     assert(fn != BACKPLANE_FUNCTION || (len <= CYW43_BUS_MAX_BLOCK_SIZE));
519     const size_t aligned_len = (len + 3) & ~3u;
520     assert(aligned_len > 0 && aligned_len <= 0x7f8);
521     if (fn == WLAN_FUNCTION) {
522         // Wait for FIFO to be ready to accept data
523         int f2_ready_attempts = 1000;
524         while (f2_ready_attempts-- > 0) {
525             uint32_t bus_status = cyw43_read_reg_u32(self, BUS_FUNCTION, SPI_STATUS_REGISTER);
526             if (bus_status & STATUS_F2_RX_READY) {
527                 logic_debug_set(pin_F2_RX_READY_WAIT, 0);
528                 break;
529             } else {
530                 logic_debug_set(pin_F2_RX_READY_WAIT, 1);
531             }
532         }
533         if (f2_ready_attempts <= 0) {
534             CYW43_PRINTF("F2 not ready\n");
535             return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
536         }
537     }
538     if (src == self->spid_buf) { // avoid a copy in the usual case just to add the header
539         self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(true, true, fn, addr, len);
540         logic_debug_set(pin_WIFI_TX, 1);
541         int res = cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4, NULL, 0);
542         logic_debug_set(pin_WIFI_TX, 0);
543         return res;
544     } else {
545         // todo: would be nice to get rid of this. Only used for firmware download?
546         assert(src < self->spid_buf || src >= (self->spid_buf + sizeof(self->spid_buf)));
547         self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(true, true, fn, addr, len);
548         memcpy(self->spid_buf, src, len);
549         return cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4, NULL, 0);
550     }
551 }
552 #endif
553 
554 #if CYW43_PIN_WL_DYNAMIC
555 
556 // storage for cyw43 pins
557 static uint cyw43_pin_array[CYW43_PIN_INDEX_WL_COUNT] = {
558     CYW43_DEFAULT_PIN_WL_REG_ON,
559     CYW43_DEFAULT_PIN_WL_DATA_OUT,
560     CYW43_DEFAULT_PIN_WL_DATA_IN,
561     CYW43_DEFAULT_PIN_WL_HOST_WAKE,
562     CYW43_DEFAULT_PIN_WL_CLOCK,
563     CYW43_DEFAULT_PIN_WL_CS
564 };
565 
566 // Check the cyw43 gpio pin array is valid
cyw43_pins_valid(uint pins[CYW43_PIN_INDEX_WL_COUNT])567 static bool cyw43_pins_valid(uint pins[CYW43_PIN_INDEX_WL_COUNT]) {
568     // check the gpios are valid
569     for(int i = 0; i < CYW43_PIN_INDEX_WL_COUNT; i++) {
570         if (pins[i] >= NUM_BANK0_GPIOS) {
571             return false;
572         }
573     }
574 #if PICO_PIO_USE_GPIO_BASE
575     // These pins should use the same gpio base
576     return (pins[CYW43_PIN_INDEX_WL_DATA_OUT] < 32 && pins[CYW43_PIN_INDEX_WL_DATA_IN] < 32 && pins[CYW43_PIN_INDEX_WL_CLOCK] < 32) ||
577         (pins[CYW43_PIN_INDEX_WL_DATA_OUT] >= 16 && pins[CYW43_PIN_INDEX_WL_DATA_IN] >= 16 && pins[CYW43_PIN_INDEX_WL_CLOCK] >= 16);
578 #else
579     return true;
580 #endif
581 }
582 
583 // Set the gpio pin array
cyw43_set_pins_wl(uint pins[CYW43_PIN_INDEX_WL_COUNT])584 int cyw43_set_pins_wl(uint pins[CYW43_PIN_INDEX_WL_COUNT]) {
585     assert(!bus_data_instance.pio);
586     if (bus_data_instance.pio) {
587         return PICO_ERROR_RESOURCE_IN_USE;
588     }
589     assert(cyw43_pins_valid(pins));
590     if (!cyw43_pins_valid(pins)) {
591         return PICO_ERROR_INVALID_ARG;
592     }
593     memcpy(cyw43_pin_array, pins, sizeof(cyw43_pin_array));
594     return PICO_OK;
595 }
596 
597 // Get a gpio pin
cyw43_get_pin_wl(cyw43_pin_index_t pin_id)598 uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id) {
599     assert(pin_id < CYW43_PIN_INDEX_WL_COUNT);
600     assert(cyw43_pin_array[pin_id] < NUM_BANK0_GPIOS);
601     return cyw43_pin_array[pin_id];
602 }
603 #endif // CYW43_PIN_WL_DYNAMIC
604