diff options
-rw-r--r-- | cmd/sbc_harness/main.c | 2 | ||||
-rw-r--r-- | libhw/rp2040_hwspi.c | 15 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwspi.h | 30 |
3 files changed, 36 insertions, 11 deletions
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c index 6fa76bd..a3351fc 100644 --- a/cmd/sbc_harness/main.c +++ b/cmd/sbc_harness/main.c @@ -169,6 +169,8 @@ COROUTINE init_cr(void *) { rp2040_hwspi_init(&globals.dev_spi, "W5500", RP2040_HWSPI_0, SPI_MODE_0, /* the W5500 supports mode 0 or mode 3 */ 31250000, /* min(w5500, hwspi); w5500=80MHz; hwspi=31.25MHz, see rp2040_hwspi.h for a comment about why this is so low */ + 30, /* W5500 datasheet says min(T_CS = SCSn High Time) = 30ns */ + 0, /* bogus write write data when doing a read */ 16, /* PIN_MISO */ 19, /* PIN_MOSI */ 18, /* PIN_CLK */ diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c index ac46451..8dd49d6 100644 --- a/libhw/rp2040_hwspi.c +++ b/libhw/rp2040_hwspi.c @@ -18,6 +18,8 @@ #define IMPLEMENTATION_FOR_LIBHW_RP2040_HWSPI_H YES #include <libhw/rp2040_hwspi.h> +#include <libhw/generic/alarmclock.h> + #include "config.h" #ifndef CONFIG_RP2040_SPI_DEBUG @@ -39,6 +41,8 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, enum rp2040_hwspi_instance inst_num, enum spi_mode mode, uint baudrate_hz, + uint64_t min_delay_ns, + uint8_t bogus_data, uint pin_miso, uint pin_mosi, uint pin_clk, @@ -100,9 +104,12 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, gpio_set_dir(pin_cs, GPIO_OUT); gpio_put(pin_cs, 1); - /* Return. */ + /* Initialize self. */ self->inst = inst; + self->min_delay_ns = min_delay_ns; + self->bogus_data = bogus_data; self->pin_cs = pin_cs; + self->dead_until_ns = 0; } static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) { @@ -113,6 +120,9 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl assert(iov); assert(iovcnt); + uint64_t now = LO_CALL(bootclock, get_time_ns); + if (now < self->dead_until_ns) + sleep_until_ns(self->dead_until_ns); gpio_put(self->pin_cs, 0); /* TODO: Replace blocking reads+writes with DMA. */ for (int i = 0; i < iovcnt; i++) { @@ -121,9 +131,10 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl else if (iov[i].iov_write_src) spi_write_blocking(inst, iov[i].iov_write_src, iov[i].iov_len); else if (iov[i].iov_read_dst) - spi_read_blocking(inst, 0, iov[i].iov_read_dst, iov[i].iov_len); + spi_read_blocking(inst, self->bogus_data, iov[i].iov_read_dst, iov[i].iov_len); else assert_notreached("duplex_iovec is neither read nor write"); } gpio_put(self->pin_cs, 1); + self->dead_until_ns = LO_CALL(bootclock, get_time_ns) + self->min_delay_ns; } diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h index a1d85d4..fef1dbd 100644 --- a/libhw/rp2040_include/libhw/rp2040_hwspi.h +++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h @@ -20,9 +20,14 @@ enum rp2040_hwspi_instance { struct rp2040_hwspi { BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H) + /* const */ LM_IF(IS_IMPLEMENTATION_FOR(LIBHW_RP2040_HWSPI_H))(spi_inst_t)(void) *inst; + uint64_t min_delay_ns; + uint8_t bogus_data; + uint pin_cs; - uint pin_cs; + /* mutable */ + uint64_t dead_until_ns; END_PRIVATE(LIBHW_RP2040_HWSPI_H) }; LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi) @@ -31,14 +36,17 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi) /** * Initialize an instance of `struct rp2040_hwspi`. * - * @param self : struct rp2040_hwspi : the structure to initialize - * @param name : char * : a name for the SPI port; to include in the bininfo - * @param inst_num : enum rp2040_hwspi_instance : the PL220 instance number; RP2040_HWSPI_{0,1} - * @param mode : enum spi_mode : the SPI mode; SPI_MODE_{0..3} - * @param pin_miso : uint : pin number; 0, 4, 16, or 20 for _HWSPI_0; 8, 12, 24, or 28 for _HWSPI_1 - * @param pin_mosi : uint : pin number; 3, 7, 19, or 23 for _HWSPI_0; 11, 15, or 27 for _HWSPI_1 - * @param pin_clk : uint : pin number; 2, 6, 18, or 22 for _HWSPI_0; 10, 14, or 26 for _HWSPI_1 - * @param pin_cs : uint : pin number; any unused GPIO pin + * @param self : struct rp2040_hwspi : the structure to initialize + * @param name : char * : a name for the SPI port; to include in the bininfo + * @param inst_num : enum rp2040_hwspi_instance : the PL220 instance number; RP2040_HWSPI_{0,1} + * @param mode : enum spi_mode : the SPI mode; SPI_MODE_{0..3} + * @param baudrate_hz : uint : baudrate in Hz + * @param min_delay_ns: uint64_t : minimum time for pin_cs to be high between messages + * @param bogus_data : uint8_t : bogus data to write when .iov_write_src is NULL + * @param pin_miso : uint : pin number; 0, 4, 16, or 20 for _HWSPI_0; 8, 12, 24, or 28 for _HWSPI_1 + * @param pin_mosi : uint : pin number; 3, 7, 19, or 23 for _HWSPI_0; 11, 15, or 27 for _HWSPI_1 + * @param pin_clk : uint : pin number; 2, 6, 18, or 22 for _HWSPI_0; 10, 14, or 26 for _HWSPI_1 + * @param pin_cs : uint : pin number; any unused GPIO pin * * There is no bit-order argument; the RP2040's hardware SPI always * uses MSB-first bit order. @@ -67,6 +75,7 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi) */ #define rp2040_hwspi_init(self, name, \ inst_num, mode, baudrate_hz, \ + min_delay_ns, bogus_data, \ pin_miso, pin_mosi, pin_clk, pin_cs) \ do { \ bi_decl(bi_4pins_with_names(pin_miso, name" SPI MISO", \ @@ -75,12 +84,15 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi) pin_mosi, name" SPI CS")); \ _rp2040_hwspi_init(self, \ inst_num, mode, baudrate_hz, \ + min_delay_ns, bogus_data, \ pin_miso, pin_mosi, pin_clk, pin_cs); \ } while(0) void _rp2040_hwspi_init(struct rp2040_hwspi *self, enum rp2040_hwspi_instance inst_num, enum spi_mode mode, uint baudrate_hz, + uint64_t min_delay_ns, + uint8_t bogus_data, uint pin_miso, uint pin_mosi, uint pin_clk, |