summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/hw
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/sbc_harness/hw')
-rw-r--r--cmd/sbc_harness/hw/rp2040_hwspi.h20
-rw-r--r--cmd/sbc_harness/hw/spi.h2
-rw-r--r--cmd/sbc_harness/hw/w5500.c151
-rw-r--r--cmd/sbc_harness/hw/w5500.h29
4 files changed, 193 insertions, 9 deletions
diff --git a/cmd/sbc_harness/hw/rp2040_hwspi.h b/cmd/sbc_harness/hw/rp2040_hwspi.h
index 9f4a551..22ef6fa 100644
--- a/cmd/sbc_harness/hw/rp2040_hwspi.h
+++ b/cmd/sbc_harness/hw/rp2040_hwspi.h
@@ -30,18 +30,22 @@ struct rp2040_hwspi {
* There is no bit-order argument; the RP2040's hardware SPI always
* uses MSB-first bit order.
*/
-#define rp2040_hwspi_init(self, name, inst_num, mode, baudrate_hz, pin_miso, pin_mosi, pin_clk, pin_cs) do { \
- bi_decl(bi_4_pins_with_names(pin_miso, name" SPI MISO", \
- pin_mosi, name" SPI MOSI", \
- pin_mosi, name" SPI CLK", \
- pin_mosi, name" SPI CS")); \
- _rp2040_hwspi_init(self, inst_num, mode, baudrate_hz, pin_miso, pin_mosi, pin_c;k, pin_cs); \
+#define rp2040_hwspi_init(self, name, \
+ inst_num, mode, baudrate_hz, \
+ pin_miso, pin_mosi, pin_clk, pin_cs) \
+ do { \
+ bi_decl(bi_4_pins_with_names(pin_miso, name" SPI MISO", \
+ pin_mosi, name" SPI MOSI", \
+ pin_mosi, name" SPI CLK", \
+ pin_mosi, name" SPI CS")); \
+ _rp2040_hwspi_init(self, \
+ inst_num, mode, baudrate_hz, \
+ pin_miso, pin_mosi, pin_c;k, 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;
+ uint baudrate_hz,
uint pin_miso,
uint pin_mosi,
uint pin_clk,
diff --git a/cmd/sbc_harness/hw/spi.h b/cmd/sbc_harness/hw/spi.h
index 4a160ca..c5a5603 100644
--- a/cmd/sbc_harness/hw/spi.h
+++ b/cmd/sbc_harness/hw/spi.h
@@ -31,7 +31,7 @@ struct spi;
* non-multiple-of-8 number of bits.
*/
struct spi_vtable {
- void (*readwritev)(struct spi *, const struct bidi_iovec *iov, int iovcnt)
+ void (*readwritev)(struct spi *, const struct bidi_iovec *iov, int iovcnt);
};
struct spi {
diff --git a/cmd/sbc_harness/hw/w5500.c b/cmd/sbc_harness/hw/w5500.c
new file mode 100644
index 0000000..993c2b5
--- /dev/null
+++ b/cmd/sbc_harness/hw/w5500.c
@@ -0,0 +1,151 @@
+#include "hw/w5500.h"
+
+/* A u8 control byte has 3 parts: block-ID, R/W, and
+ * operating-mode. */
+
+/* Part 1: Block ID. */
+#define CTL_MASK_BLOCK 0b11111'000
+#define _CTL_BLOCK_RES (0b00'000)
+#define _CTL_BLOCK_REG (0b01'000)
+#define _CTL_BLOCK_TX (0b10'000)
+#define _CTL_BLOCK_RX (0b11'000)
+#define CTL_BLOCK_SOCK(n,part) (((n)<<5)|(_CTL_BLOCK_##part))
+#define CTL_BLOCK_COMMON_REG CTL_BLOCK_SOCK(0,RES)
+
+/* Part 2: R/W. */
+#define CTL_MASK_RW 0b1'00
+#define CTL_R 0b0'00
+#define CTL_W 0b1'00
+
+/* Part 3: Operating mode. */
+#define CTL_MASK_OM 0b11
+#define CTL_OM_VDM 0b00
+#define CTL_OM_FDM1 0b01
+#define CTL_OM_FDM2 0b10
+#define CTL_OM_FDM4 0b11
+
+/* The W5500 has 2 channels of communication with the MCU:
+ *
+ * - An SPI-based RPC protocol:
+ * + mode: mode 0 or mode 3
+ * + bit-order: MSB-first
+ * + clock frequency: 33.3MHz - 80MHz
+ * - An interrupt pin that it pulls low when an event happens (to let
+ * the MCU know that it should do an SPI RPC "get" to see what
+ * happened.)
+ *
+ * Even though SPI is a full-duplex protocol, the W5500's RPC protocol
+ * on top of it is only half-duplex. Lame.
+ */
+
+void w5500_spiframe_write(struct spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
+ assert(spidev);
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(data);
+ assert(data_len);
+
+ uint8_t header[3] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
+ };
+ struct bidi_iovec iov[] = {
+ {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)},
+ {.iov_read_dst = NULL, .iov_write_src = data, .iov_len = data_len},
+ };
+ spidev->vtable->readwritev(spidev, iov, 2);
+}
+
+void w5500_spiframe_read(uint16_t addr, uint8_t ctl, void *data, size_t data_len) {
+ assert(spidev);
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(data);
+ assert(data_len);
+
+ uint8_t header[3] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
+ };
+ struct bidi_iovec iov[] = {
+ {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)},
+ {.iov_read_dst = data, .iov_write_src = NULL, .iov_len = data_len},
+ };
+ spidev->vtable->readwritev(spidev, iov, 2);
+}
+
+void _w5500_init(struct w5500 *self, struct spi* spi, uint pin_intr) {
+ self->spidev = spi;
+ gpio_set_irq_enabled_with_callback(pin_intr, GPIO_IRQ_EDGE_FALL, true, cbfn);
+}
+
+struct w5500_block_common_reg {
+ uint8_t mode; /* MR */
+ uint8_t ip_gateway_addr[4]; /* GAR0 ... GAR3 */
+ uint8_t ip_subnet_mask[4]; /* SUBR0 ... SUBR3 */
+ uint8_t eth_addr[6]; /* SHAR0 ... SHAR5 */
+ uint8_t ip_addr[4]; /* SIPR0 ... SIPR3 */
+
+ uint8_t intlevel_0; /* INTLEVEL0 */
+ uint8_t intlevel_1; /* INTLEVEL1 */
+ uint8_t interrupt; /* IR */
+ uint8_t interrupt_mask; /* IMR */
+ uint8_t sock_interrupt; /* SIR */
+ uint8_t sock_interrupt_mask; /* SIMR */
+ uint8_t retry_time_0; /* RTR0 */
+ uint8_t retry_time_1; /* RTR0 */
+ uint8_t retry_count; /* RCR */
+
+ uint8_t ppp_lcp_request_timer; /* PTIMER */
+ uint8_t ppp_lcp_magic_bumber; /* PMAGIC */
+ uint8_t ppp_dst_eth_addr[6]; /* PHAR0 ... PHAR5 */
+ uint8_t ppp_sess_id[2]; /* PSID0 ... PSID1 */
+ uint8_t ppp_max_seg_size[2]; /* PMRU0 ... PMRU1 */
+
+ uint8_t unreachable_ip_addr[4]; /* UIPR0 ... UIPR3 */
+ uint8_t unreachable_port[2]; /* UPORTR0, UPROTR1 */
+
+ uint8_t phy_cfg; /* PHYCFGR */
+
+ uint8_t _reserved[10];
+
+ uint8_t chip_version; /* VERSIONR */
+};
+static_assert(sizeof(struct w5500_block_common_reg) == 0x3A);
+
+struct w5500_block_sock_reg {
+ uint8_t mode; /* Sn_MR */
+ uint8_t command; /* Sn_CR */
+ uint8_t interrupt; /* Sn_IR */
+ uint8_t status; /* Sn_SR */
+ uint8_t src_port[2]; /* Sn_PORT0, Sn_PORT1 */
+ uint8_t dst_eth_addr[6]; /* Sn_DHAR0 ... SnDHAR5 */
+ uint8_t dst_ip_addr[4]; /* Sn_DIPR0 ... Sn_DIP3 */
+ uint8_t dst_port[2]; /* Sn_DPORT0 ... Sn_DPORT1 */
+
+ uint8_t max_seg_size[2]; /* Sn_MSSR0, Sn_MSSR1 */
+ uint8_t _reserved0[1];
+ uint8_t ip_tos; /* Sn_TOS */
+ uint8_t ip_ttl; /* Sn_TTL */
+ uint8_t _reserved1[7];
+
+ uint8_t rx_buf_size; /* Sn_RXBUF_SIZE */
+ uint8_t tx_buf_size; /* Sn_TXBUF_SIZE */
+ uint8_t tx_free_size[2]; /* Sn_TX_FSR0, Sn_TX_FSR1 */
+ uint8_t tx_read_pointer[2]; /* Sn_TX_RD0, Sn_TX_RD1 */
+ uint8_t tx_write_pointer[2]; /* Sn_TX_WR0, Sn_TX_WR1 */
+ uint8_t rx_size[2]; /* Sn_RX_RSR0, Sn_RX_RSR1 */
+ uint8_t rx_read_pointer[2]; /* Sn_RX_RD0, Sn_RX_RD1 */
+ uint8_t rx_write_pointer[2]; /* Sn_RX_WR0, Sn_RX_WR1 */
+
+ uint8_t interrupt_mask; /* Sn_IMR */
+ uint8_t fragment_offset[2]; /* Sn_FRAG0, Sn_FRAG1 */
+ uint8_t keepalive_timer; /* Sn_KPALVTR */
+};
+static_assert(sizeof(struct w5500_block_sock_reg) == 0x30);
+
+
+struct w5500_socket {
+ struct spi *spidev;
+ uint8_t socknum; /* 0-7 */
+};
diff --git a/cmd/sbc_harness/hw/w5500.h b/cmd/sbc_harness/hw/w5500.h
new file mode 100644
index 0000000..0f37797
--- /dev/null
+++ b/cmd/sbc_harness/hw/w5500.h
@@ -0,0 +1,29 @@
+#ifndef _HW_W5500_H_
+#define _HW_W5500_H_
+
+#include "hw/spi.h"
+
+struct w5500 {
+ struct spi *spidev;
+};
+
+/**
+ * Initialize a WIZnet W5500 Ethernet and TCP/IP-offload chip.
+ *
+ * The W5500 has 2 channels of communication with the MCU:
+ *
+ * - An SPI-based RPC protocol:
+ * + mode: mode 0 or mode 3
+ * + bit-order: MSB-first
+ * + clock frequency: 33.3MHz - 80MHz
+ * - An interrupt pin that it pulls low when an event happens (to let
+ * the MCU know that it should do an SPI RPC "get" to see what
+ * happened.)
+ */
+#define w5500_init(self, name, spi, pin_intr) do { \
+ bi_decl(bi_1_pin_with_name(pin_intr, name" interrupt")); \
+ _w5500_init(self, spi, pin_intr); \
+ } while (0)
+void _w5500_init(struct w5500 *self, struct spi* spi, uint pin_intr);
+
+#endif /* _HW_W5500_H_ */