From: Danylo Vodopianov <dvo-...@napatech.com> - Implement nthw_spi_v3 module with initialization, transfer, and utility functions. - Add nthw_spim and nthw_spis modules for SPI primary and secondary interfaces. - Include SPI v3 header files and update meson build configuration. - Implement AVR probe function to retrieve and log AVR information.
Signed-off-by: Danylo Vodopianov <dvo-...@napatech.com> --- drivers/net/ntnic/meson.build | 3 + .../net/ntnic/nthw/core/include/nthw_fpga.h | 2 + .../net/ntnic/nthw/core/include/nthw_spi_v3.h | 107 +++++ .../net/ntnic/nthw/core/include/nthw_spim.h | 58 +++ .../net/ntnic/nthw/core/include/nthw_spis.h | 63 +++ .../nt400dxx/reset/nthw_fpga_rst_nt400dxx.c | 2 + drivers/net/ntnic/nthw/core/nthw_fpga.c | 444 ++++++++++++++++++ drivers/net/ntnic/nthw/core/nthw_spi_v3.c | 358 ++++++++++++++ drivers/net/ntnic/nthw/core/nthw_spim.c | 113 +++++ drivers/net/ntnic/nthw/core/nthw_spis.c | 121 +++++ 10 files changed, 1271 insertions(+) create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_spi_v3.h create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_spim.h create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_spis.h create mode 100644 drivers/net/ntnic/nthw/core/nthw_spi_v3.c create mode 100644 drivers/net/ntnic/nthw/core/nthw_spim.c create mode 100644 drivers/net/ntnic/nthw/core/nthw_spis.c diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build index 9885d4efbf..7e326a3e1d 100644 --- a/drivers/net/ntnic/meson.build +++ b/drivers/net/ntnic/meson.build @@ -69,6 +69,9 @@ sources = files( 'nthw/core/nthw_rmc.c', 'nthw/core/nthw_sdc.c', 'nthw/core/nthw_si5340.c', + 'nthw/core/nthw_spim.c', + 'nthw/core/nthw_spis.c', + 'nthw/core/nthw_spi_v3.c', 'nthw/stat/nthw_stat.c', 'nthw/flow_api/flow_api.c', 'nthw/flow_api/flow_group.c', diff --git a/drivers/net/ntnic/nthw/core/include/nthw_fpga.h b/drivers/net/ntnic/nthw/core/include/nthw_fpga.h index 8b1d548a25..418aea8277 100644 --- a/drivers/net/ntnic/nthw/core/include/nthw_fpga.h +++ b/drivers/net/ntnic/nthw/core/include/nthw_fpga.h @@ -18,6 +18,8 @@ int nthw_fpga_shutdown(struct fpga_info_s *p_fpga_info); int nthw_fpga_get_param_info(struct fpga_info_s *p_fpga_info, nthw_fpga_t *p_fpga); +int nthw_fpga_avr_probe(nthw_fpga_t *p_fpga, const int n_instance_no); + int nthw_fpga_iic_scan(nthw_fpga_t *p_fpga, const int n_instance_no_begin, const int n_instance_no_end); diff --git a/drivers/net/ntnic/nthw/core/include/nthw_spi_v3.h b/drivers/net/ntnic/nthw/core/include/nthw_spi_v3.h new file mode 100644 index 0000000000..66b1f7f45d --- /dev/null +++ b/drivers/net/ntnic/nthw/core/include/nthw_spi_v3.h @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef __NT4GA_SPI_V3__ +#define __NT4GA_SPI_V3__ + +#include "nthw_spis.h" +#include "nthw_spim.h" + +/* Must include v1.x series. The first v1.0a only had 248 bytes of storage. v2.0x have 255 */ +#define MAX_AVR_CONTAINER_SIZE (248) + +enum avr_opcodes { + __AVR_OP_NOP = 0, /* v2 NOP command */ + /* version handlers */ + AVR_OP_VERSION = 1, + AVR_OP_SPI_VERSION = 2, /* v2.0+ command Get protocol version */ + AVR_OP_SYSINFO = 3, + /* Ping handlers */ + AVR_OP_PING = 4, + AVR_OP_PING_DELAY = 5, + /* i2c handlers */ + AVR_OP_I2C_READ = 9, + AVR_OP_I2C_WRITE = 10, + AVR_OP_I2C_RANDOM_READ = 11, + /* VPD handlers */ + AVR_OP_VPD_READ = 19, + AVR_OP_VPD_WRITE = 20, + /* SENSOR handlers */ + AVR_OP_SENSOR_FETCH = 28, + /* The following command are only relevant to V3 */ + AVR_OP_SENSOR_MON_CONTROL = 42, + AVR_OP_SENSOR_MON_SETUP = 43, + /* special version handler */ + AVR_OP_SYSINFO_2 = 62, +}; + +#define GEN2_AVR_IDENT_SIZE (20) +#define GEN2_AVR_VERSION_SIZE (50) + +#define GEN2_PN_SIZE (13) +#define GEN2_PBA_SIZE (16) +#define GEN2_SN_SIZE (10) +#define GEN2_BNAME_SIZE (14) +#define GEN2_PLATFORM_SIZE (72) +#define GEN2_VPD_SIZE_TOTAL \ + (1 + GEN2_PN_SIZE + GEN2_PBA_SIZE + GEN2_SN_SIZE + GEN2_BNAME_SIZE + GEN2_PLATFORM_SIZE + \ + 2) + +typedef struct vpd_eeprom_s { + uint8_t psu_hw_version; /* Hw revision - MUST NEVER ne overwritten. */ + /* Vital Product Data: P/N (13bytes ascii 0-9) */ + uint8_t vpd_pn[GEN2_PN_SIZE]; + /* Vital Product Data: PBA (16bytes ascii 0-9) */ + uint8_t vpd_pba[GEN2_PBA_SIZE]; + /* Vital Product Data: S/N (10bytes ascii 0-9) */ + uint8_t vpd_sn[GEN2_SN_SIZE]; + /* Vital Product Data: Board Name (10bytes ascii) (e.g. "ntmainb1e2" or "ntfront20b1") */ + uint8_t vpd_board_name[GEN2_BNAME_SIZE]; + /* + * Vital Product Data: Other (72bytes of MAC addresses or other stuff.. (gives up to 12 mac + * addresses) + */ + uint8_t vpd_platform_section[GEN2_PLATFORM_SIZE]; + /* CRC16 checksum of all of above. This field is not included in the checksum */ + uint16_t crc16; +} vpd_eeprom_t; + +typedef struct { + uint8_t psu_hw_revision; + char board_type[GEN2_BNAME_SIZE + 1]; + char product_id[GEN2_PN_SIZE + 1]; + char pba_id[GEN2_PBA_SIZE + 1]; + char serial_number[GEN2_SN_SIZE + 1]; + uint8_t product_family; + uint32_t feature_mask; + uint32_t invfeature_mask; + uint8_t no_of_macs; + uint8_t mac_address[6]; + uint16_t custom_id; + uint8_t user_id[8]; +} board_info_t; + +struct tx_rx_buf { + uint16_t size; + void *p_buf; +}; + +struct nthw_spi_v3 { + int m_time_out; + int mn_instance_no; + nthw_spim_t *mp_spim_mod; + nthw_spis_t *mp_spis_mod; +}; + +typedef struct nthw_spi_v3 nthw_spi_v3_t; +typedef struct nthw_spi_v3 nthw_spi_v3; + +nthw_spi_v3_t *nthw_spi_v3_new(void); +int nthw_spi_v3_init(nthw_spi_v3_t *p, nthw_fpga_t *p_fpga, int n_instance_no); + +int nthw_spi_v3_transfer(nthw_spi_v3_t *p, uint16_t opcode, struct tx_rx_buf *tx_buf, + struct tx_rx_buf *rx_buf); + +#endif /* __NT4GA_SPI_V3__ */ diff --git a/drivers/net/ntnic/nthw/core/include/nthw_spim.h b/drivers/net/ntnic/nthw/core/include/nthw_spim.h new file mode 100644 index 0000000000..70a49ab627 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/include/nthw_spim.h @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef __NTHW_SPIM_H__ +#define __NTHW_SPIM_H__ + +#include "nthw_fpga.h" + +struct nthw_spim { + nthw_fpga_t *mp_fpga; + nthw_module_t *mp_mod_spim; + int mn_instance; + + nthw_register_t *mp_reg_srr; + nthw_field_t *mp_fld_srr_rst; + + nthw_register_t *mp_reg_cr; + nthw_field_t *mp_fld_cr_loop; + nthw_field_t *mp_fld_cr_en; + nthw_field_t *mp_fld_cr_txrst; + nthw_field_t *mp_fld_cr_rxrst; + + nthw_register_t *mp_reg_sr; + nthw_field_t *mp_fld_sr_done; + nthw_field_t *mp_fld_sr_txempty; + nthw_field_t *mp_fld_sr_rxempty; + nthw_field_t *mp_fld_sr_txfull; + nthw_field_t *mp_fld_sr_rxfull; + nthw_field_t *mp_fld_sr_txlvl; + nthw_field_t *mp_fld_sr_rxlvl; + + nthw_register_t *mp_reg_dtr; + nthw_field_t *mp_fld_dtr_dtr; + + nthw_register_t *mp_reg_drr; + nthw_field_t *mp_fld_drr_drr; + + nthw_register_t *mp_reg_cfg; + nthw_field_t *mp_fld_cfg_pre; + + nthw_register_t *mp_reg_cfg_clk; + nthw_field_t *mp_fld_cfg_clk_mode; +}; + +typedef struct nthw_spim nthw_spim_t; +typedef struct nthw_spim nthw_spim; + +nthw_spim_t *nthw_spim_new(void); +int nthw_spim_init(nthw_spim_t *p, nthw_fpga_t *p_fpga, int n_instance); + +uint32_t nthw_spim_reset(nthw_spim_t *p); +uint32_t nthw_spim_enable(nthw_spim_t *p, bool b_enable); +uint32_t nthw_spim_get_tx_fifo_empty(nthw_spim_t *p, bool *pb_empty); +uint32_t nthw_spim_write_tx_fifo(nthw_spim_t *p, uint32_t n_data); + +#endif /* __NTHW_SPIM_H__ */ diff --git a/drivers/net/ntnic/nthw/core/include/nthw_spis.h b/drivers/net/ntnic/nthw/core/include/nthw_spis.h new file mode 100644 index 0000000000..978f239dd0 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/include/nthw_spis.h @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef __NTHW_SPIS_H__ +#define __NTHW_SPIS_H__ + +#include "nthw_fpga.h" + +struct nthw_spis { + nthw_fpga_t *mp_fpga; + nthw_module_t *mp_mod_spis; + int mn_instance; + + nthw_register_t *mp_reg_srr; + nthw_field_t *mp_fld_srr_rst; + + nthw_register_t *mp_reg_cr; + nthw_field_t *mp_fld_cr_loop; + nthw_field_t *mp_fld_cr_en; + nthw_field_t *mp_fld_cr_txrst; + nthw_field_t *mp_fld_cr_rxrst; + nthw_field_t *mp_fld_cr_debug; + + nthw_register_t *mp_reg_sr; + nthw_field_t *mp_fld_sr_done; + nthw_field_t *mp_fld_sr_txempty; + nthw_field_t *mp_fld_sr_rxempty; + nthw_field_t *mp_fld_sr_txfull; + nthw_field_t *mp_fld_sr_rxfull; + nthw_field_t *mp_fld_sr_txlvl; + nthw_field_t *mp_fld_sr_rxlvl; + nthw_field_t *mp_fld_sr_frame_err; + nthw_field_t *mp_fld_sr_read_err; + nthw_field_t *mp_fld_sr_write_err; + + nthw_register_t *mp_reg_dtr; + nthw_field_t *mp_fld_dtr_dtr; + + nthw_register_t *mp_reg_drr; + nthw_field_t *mp_fld_drr_drr; + + nthw_register_t *mp_reg_ram_ctrl; + nthw_field_t *mp_fld_ram_ctrl_adr; + nthw_field_t *mp_fld_ram_ctrl_cnt; + + nthw_register_t *mp_reg_ram_data; + nthw_field_t *mp_fld_ram_data_data; +}; + +typedef struct nthw_spis nthw_spis_t; +typedef struct nthw_spis nthw_spis; + +nthw_spis_t *nthw_spis_new(void); +int nthw_spis_init(nthw_spis_t *p, nthw_fpga_t *p_fpga, int n_instance); + +uint32_t nthw_spis_reset(nthw_spis_t *p); +uint32_t nthw_spis_enable(nthw_spis_t *p, bool b_enable); +uint32_t nthw_spis_get_rx_fifo_empty(nthw_spis_t *p, bool *pb_empty); +uint32_t nthw_spis_read_rx_fifo(nthw_spis_t *p, uint32_t *p_data); + +#endif /* __NTHW_SPIS_H__ */ diff --git a/drivers/net/ntnic/nthw/core/nt400dxx/reset/nthw_fpga_rst_nt400dxx.c b/drivers/net/ntnic/nthw/core/nt400dxx/reset/nthw_fpga_rst_nt400dxx.c index ff29101e61..73feaa4ebd 100644 --- a/drivers/net/ntnic/nthw/core/nt400dxx/reset/nthw_fpga_rst_nt400dxx.c +++ b/drivers/net/ntnic/nthw/core/nt400dxx/reset/nthw_fpga_rst_nt400dxx.c @@ -65,6 +65,8 @@ static int nthw_fpga_rst_nt400dxx_init(struct fpga_info_s *p_fpga_info) nthw_prm_nt400dxx_periph_rst(p_fpga_info->mp_nthw_agx.p_prm, 0); nt_os_wait_usec(10000); + res = nthw_fpga_avr_probe(p_fpga, 0); + if (res != 0) return res; diff --git a/drivers/net/ntnic/nthw/core/nthw_fpga.c b/drivers/net/ntnic/nthw/core/nthw_fpga.c index e54a210c9f..3166a2ba51 100644 --- a/drivers/net/ntnic/nthw/core/nthw_fpga.c +++ b/drivers/net/ntnic/nthw/core/nthw_fpga.c @@ -14,6 +14,7 @@ #include "nthw_fpga_mod_str_map.h" #include "nthw_tsm.h" +#include "nthw_spi_v3.h" #include <arpa/inet.h> @@ -151,6 +152,449 @@ int nthw_fpga_silabs_detect(nthw_fpga_t *p_fpga, const int n_instance_no, const return res; } +/* + * Calculate CRC-16-CCITT of passed data + * CRC-16-CCITT ^16 + ^12 + ^5 + 1 (0x1021) (X.25, HDLC, XMODEM, Bluetooth, + * SD, many others; known as CRC-CCITT) + */ +static uint16_t crc16(uint8_t *buffer, size_t length) +{ + uint16_t seed = 0; + + while (length--) { + seed = (uint16_t)(seed >> 8 | seed << 8); + seed = (uint16_t)(seed ^ *buffer++); + seed = (uint16_t)(seed ^ (seed & 0xff) >> 4); + seed = (uint16_t)(seed ^ seed << 8 << 4); + seed = (uint16_t)(seed ^ (seed & 0xff) << 4 << 1); + } + + return seed; +} + +int nthw_fpga_avr_probe(nthw_fpga_t *p_fpga, const int n_instance_no) +{ + struct fpga_info_s *p_fpga_info = p_fpga->p_fpga_info; + const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str; + nthw_spi_v3_t *p_avr_spi; + int res = -1; + + p_avr_spi = nthw_spi_v3_new(); + + if (p_avr_spi) { + struct avr_vpd_info_s { + /* avr info */ + uint32_t n_avr_spi_version; + uint8_t n_avr_fw_ver_major; + uint8_t n_avr_fw_ver_minor; + uint8_t n_avr_fw_ver_micro; + uint8_t a_avr_fw_ver_str[50]; + uint8_t a_avr_fw_plat_id_str[20]; + + /* vpd_eeprom_t */ + uint8_t psu_hw_version; + uint8_t vpd_pn[GEN2_PN_SIZE]; + uint8_t vpd_pba[GEN2_PBA_SIZE]; + uint8_t vpd_sn[GEN2_SN_SIZE]; + uint8_t vpd_board_name[GEN2_BNAME_SIZE]; + uint8_t vpd_platform_section[GEN2_PLATFORM_SIZE]; + + /* board_info_t aka vpd_platform_section: */ + uint32_t product_family;/* uint8_t 1: capture, 2: Inline, 3: analysis */ + uint32_t feature_mask; /* Bit 0: OC192 capable */ + uint32_t invfeature_mask; + uint8_t no_of_macs; + uint8_t mac_address[6]; + uint16_t custom_id; + uint8_t user_id[8]; + /* + * Reserved NT operations to monitor the reprogram count of user_id with + * vpduser + */ + uint16_t user_id_erase_write_count; + + /* + * AVR_OP_SYSINFO: struct version_sysinfo_request_container + * Which version of the sysinfo container to retrieve. Set to zero to fetch + * latest. Offset zero of latest always contain an uint8_t version info + */ + uint8_t sysinfo_container_version; + + /* AVR_OP_SYSINFO: struct AvrLibcVersion */ + /* The constant __AVR_LIBC_VERSION__ */ + uint32_t sysinfo_avr_libc_version; + + /* AVR_OP_SYSINFO: struct AvrLibcSignature */ + uint8_t sysinfo_signature_0; /* The constant SIGNATURE_0 */ + uint8_t sysinfo_signature_1; /* The constant SIGNATURE_1 */ + uint8_t sysinfo_signature_2; /* The constant SIGNATURE_2 */ + + /* AVR_OP_SYSINFO: struct AvrOs */ + uint8_t sysinfo_spi_version; /* SPI command layer version */ + /* + * Hardware revision. Locked to eeprom address zero. Is also available via + * VPD read opcode (prior to v1.4b, this is required) + */ + uint8_t sysinfo_hw_revision; + /* + * Number of ticks/second (Note: Be aware this may become zero if timer + * module is rewritten to a tickles system!) + */ + uint8_t sysinfo_ticks_per_second; + uint32_t sysinfo_uptime;/* Uptime in seconds since last AVR reset */ + uint8_t sysinfo_osccal; /* OSCCAL value */ + + /* + * Meta data concluded/calculated from req/reply + */ + bool b_feature_mask_valid; + bool b_crc16_valid; + uint16_t n_crc16_stored; + uint16_t n_crc16_calced; + uint64_t n_mac_val; + }; + + struct avr_vpd_info_s avr_vpd_info; + struct tx_rx_buf tx_buf; + struct tx_rx_buf rx_buf; + char rx_data[MAX_AVR_CONTAINER_SIZE]; + uint32_t u32; + + memset(&avr_vpd_info, 0, sizeof(avr_vpd_info)); + + nthw_spi_v3_init(p_avr_spi, p_fpga, n_instance_no); + + /* AVR_OP_SPI_VERSION */ + tx_buf.size = 0; + tx_buf.p_buf = NULL; + rx_buf.size = sizeof(u32); + rx_buf.p_buf = &u32; + u32 = 0; + res = nthw_spi_v3_transfer(p_avr_spi, AVR_OP_SPI_VERSION, &tx_buf, &rx_buf); + avr_vpd_info.n_avr_spi_version = u32; + NT_LOG(DBG, NTHW, "%s: AVR%d: SPI_VER: %d", p_adapter_id_str, n_instance_no, + avr_vpd_info.n_avr_spi_version); + + /* AVR_OP_VERSION */ + tx_buf.size = 0; + tx_buf.p_buf = NULL; + rx_buf.size = sizeof(rx_data); + rx_buf.p_buf = &rx_data; + res = nthw_spi_v3_transfer(p_avr_spi, AVR_OP_VERSION, &tx_buf, &rx_buf); + + avr_vpd_info.n_avr_fw_ver_major = rx_data[0]; + avr_vpd_info.n_avr_fw_ver_minor = rx_data[1]; + avr_vpd_info.n_avr_fw_ver_micro = rx_data[2]; + NT_LOG(DBG, NTHW, "%s: AVR%d: FW_VER: %c.%c.%c", p_adapter_id_str, n_instance_no, + avr_vpd_info.n_avr_fw_ver_major, avr_vpd_info.n_avr_fw_ver_minor, + avr_vpd_info.n_avr_fw_ver_micro); + + memcpy(avr_vpd_info.a_avr_fw_ver_str, &rx_data[0 + 3], + sizeof(avr_vpd_info.a_avr_fw_ver_str)); + NT_LOG(DBG, NTHW, "%s: AVR%d: FW_VER_STR: '%.*s'", p_adapter_id_str, + n_instance_no, (int)sizeof(avr_vpd_info.a_avr_fw_ver_str), + avr_vpd_info.a_avr_fw_ver_str); + + memcpy(avr_vpd_info.a_avr_fw_plat_id_str, &rx_data[0 + 3 + 50], + sizeof(avr_vpd_info.a_avr_fw_plat_id_str)); + NT_LOG(DBG, NTHW, "%s: AVR%d: FW_HW_ID_STR: '%.*s'", p_adapter_id_str, + n_instance_no, (int)sizeof(avr_vpd_info.a_avr_fw_plat_id_str), + avr_vpd_info.a_avr_fw_plat_id_str); + + snprintf(p_fpga_info->nthw_hw_info.hw_plat_id_str, + sizeof(p_fpga_info->nthw_hw_info.hw_plat_id_str), "%s", + (char *)avr_vpd_info.a_avr_fw_plat_id_str); + p_fpga_info->nthw_hw_info + .hw_plat_id_str[sizeof(p_fpga_info->nthw_hw_info.hw_plat_id_str) - 1] = 0; + + /* AVR_OP_SYSINFO_2 */ + tx_buf.size = 0; + tx_buf.p_buf = NULL; + rx_buf.size = sizeof(rx_data); + rx_buf.p_buf = &rx_data; + res = nthw_spi_v3_transfer(p_avr_spi, AVR_OP_SYSINFO_2, &tx_buf, &rx_buf); + + if (res == 0 && avr_vpd_info.n_avr_spi_version >= 3 && rx_buf.size >= 16) { + if (rx_buf.size != 16) { + NT_LOG(WRN, NTHW, + "%s: AVR%d: SYSINFO2: reply is larger than expected: %04X %04X", + p_adapter_id_str, n_instance_no, rx_buf.size, 16); + + } else { + NT_LOG(DBG, NTHW, "%s: AVR%d: SYSINFO2: OK: res=%d sz=%d", + p_adapter_id_str, n_instance_no, res, rx_buf.size); + } + + avr_vpd_info.sysinfo_container_version = rx_data[0]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SYSINFO_REQ_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_container_version); + + memcpy(&avr_vpd_info.sysinfo_avr_libc_version, &rx_data[0 + 1], + sizeof(avr_vpd_info.sysinfo_avr_libc_version)); + NT_LOG(DBG, NTHW, "%s: AVR%d: LIBC_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_avr_libc_version); + + avr_vpd_info.sysinfo_signature_0 = rx_data[5]; + avr_vpd_info.sysinfo_signature_1 = rx_data[6]; + avr_vpd_info.sysinfo_signature_2 = rx_data[7]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SIGNATURE: %02x%02x%02x", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_signature_0, + avr_vpd_info.sysinfo_signature_1, avr_vpd_info.sysinfo_signature_2); + + avr_vpd_info.sysinfo_spi_version = rx_data[8]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SPI_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_spi_version); + + avr_vpd_info.sysinfo_hw_revision = rx_data[9]; + NT_LOG(DBG, NTHW, "%s: AVR%d: HW_REV: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_hw_revision); + + avr_vpd_info.sysinfo_ticks_per_second = rx_data[10]; + NT_LOG(DBG, NTHW, "%s: AVR%d: TICKS_PER_SEC: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_ticks_per_second); + + memcpy(&avr_vpd_info.sysinfo_uptime, &rx_data[11], + sizeof(avr_vpd_info.sysinfo_uptime)); + NT_LOG(DBG, NTHW, "%s: AVR%d: UPTIME: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_uptime); + + avr_vpd_info.sysinfo_osccal = rx_data[15]; + NT_LOG(DBG, NTHW, "%s: AVR%d: OSCCAL: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_osccal); + + { + bool b_spi_ver_match = (avr_vpd_info.n_avr_spi_version == + avr_vpd_info.sysinfo_spi_version); + (void)b_spi_ver_match; + NT_LOG(DBG, NTHW, "%s: AVR%d: SPI_VER_TST: %s (%d %d)", + p_adapter_id_str, n_instance_no, + (b_spi_ver_match ? "OK" : "MISMATCH"), + avr_vpd_info.n_avr_spi_version, + avr_vpd_info.sysinfo_spi_version); + } + + /* SYSINFO2: if response: only populate hw_id not hw_id_emulated */ + p_fpga_info->nthw_hw_info.hw_id = avr_vpd_info.sysinfo_hw_revision; + + } else { + /* AVR_OP_SYSINFO */ + tx_buf.size = 0; + tx_buf.p_buf = NULL; + rx_buf.size = sizeof(rx_data); + rx_buf.p_buf = &rx_data; + res = nthw_spi_v3_transfer(p_avr_spi, AVR_OP_SYSINFO, &tx_buf, &rx_buf); + + if (res == 0 && avr_vpd_info.n_avr_spi_version >= 3 && rx_buf.size >= 16) { + if (rx_buf.size != 16) { + NT_LOG(WRN, NTHW, + "%s: AVR%d: SYSINFO: reply is larger than expected: %04X %04X", + p_adapter_id_str, n_instance_no, rx_buf.size, 16); + + } else { + NT_LOG(DBG, NTHW, "%s: AVR%d: SYSINFO: OK: res=%d sz=%d", + p_adapter_id_str, n_instance_no, res, rx_buf.size); + } + + avr_vpd_info.sysinfo_container_version = rx_data[0]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SYSINFO_REQ_VER: %d", + p_adapter_id_str, n_instance_no, + avr_vpd_info.sysinfo_container_version); + + memcpy(&avr_vpd_info.sysinfo_avr_libc_version, &rx_data[0 + 1], + sizeof(avr_vpd_info.sysinfo_avr_libc_version)); + NT_LOG(DBG, NTHW, "%s: AVR%d: LIBC_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_avr_libc_version); + + avr_vpd_info.sysinfo_signature_0 = rx_data[5]; + avr_vpd_info.sysinfo_signature_1 = rx_data[6]; + avr_vpd_info.sysinfo_signature_2 = rx_data[7]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SIGNATURE: %02x%02x%02x", + p_adapter_id_str, n_instance_no, + avr_vpd_info.sysinfo_signature_0, + avr_vpd_info.sysinfo_signature_1, + avr_vpd_info.sysinfo_signature_2); + + avr_vpd_info.sysinfo_spi_version = rx_data[8]; + NT_LOG(DBG, NTHW, "%s: AVR%d: SPI_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_spi_version); + + avr_vpd_info.sysinfo_hw_revision = rx_data[9]; + NT_LOG(DBG, NTHW, "%s: AVR%d: HW_REV: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_hw_revision); + NT_LOG(INF, NTHW, "%s: AVR%d: HW_REV: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_hw_revision); + + avr_vpd_info.sysinfo_ticks_per_second = rx_data[10]; + NT_LOG(DBG, NTHW, "%s: AVR%d: TICKS_PER_SEC: %d", + p_adapter_id_str, n_instance_no, + avr_vpd_info.sysinfo_ticks_per_second); + + memcpy(&avr_vpd_info.sysinfo_uptime, &rx_data[11], + sizeof(avr_vpd_info.sysinfo_uptime)); + NT_LOG(DBG, NTHW, "%s: AVR%d: UPTIME: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_uptime); + + avr_vpd_info.sysinfo_osccal = rx_data[15]; + NT_LOG(DBG, NTHW, "%s: AVR%d: OSCCAL: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.sysinfo_osccal); + + { + bool b_spi_ver_match = (avr_vpd_info.n_avr_spi_version == + avr_vpd_info.sysinfo_spi_version); + (void)b_spi_ver_match; + NT_LOG(DBG, NTHW, "%s: AVR%d: SPI_VER_TST: %s (%d %d)", + p_adapter_id_str, n_instance_no, + (b_spi_ver_match ? "OK" : "MISMATCH"), + avr_vpd_info.n_avr_spi_version, + avr_vpd_info.sysinfo_spi_version); + } + + p_fpga_info->nthw_hw_info.hw_id = avr_vpd_info.sysinfo_hw_revision; + p_fpga_info->nthw_hw_info.hw_id_emulated = + avr_vpd_info.sysinfo_hw_revision; + + } else { + NT_LOG(ERR, NTHW, "%s: AVR%d: SYSINFO: NA: res=%d sz=%d", + p_adapter_id_str, n_instance_no, res, rx_buf.size); + } + } + + /* AVR_OP_VPD_READ */ + tx_buf.size = 0; + tx_buf.p_buf = NULL; + rx_buf.size = sizeof(rx_data); + rx_buf.p_buf = &rx_data; + res = nthw_spi_v3_transfer(p_avr_spi, AVR_OP_VPD_READ, &tx_buf, &rx_buf); + + if (res == 0 && avr_vpd_info.n_avr_spi_version >= 3 && + rx_buf.size >= GEN2_VPD_SIZE_TOTAL) { + avr_vpd_info.n_crc16_calced = crc16(rx_buf.p_buf, rx_buf.size - 2); + memcpy(&avr_vpd_info.n_crc16_stored, &rx_data[rx_buf.size - 2], + sizeof(avr_vpd_info.n_crc16_stored)); + NT_LOG(DBG, NTHW, "%s: AVR%d: VPD_CRC: %04X %04X", p_adapter_id_str, + n_instance_no, avr_vpd_info.n_crc16_stored, + avr_vpd_info.n_crc16_calced); + + avr_vpd_info.b_crc16_valid = + (avr_vpd_info.n_crc16_stored == avr_vpd_info.n_crc16_calced); + NT_LOG(DBG, NTHW, "%s: AVR%d: CRC_TST: %s", p_adapter_id_str, + n_instance_no, (avr_vpd_info.b_crc16_valid ? "OK" : "ERROR")); + + if (avr_vpd_info.b_crc16_valid) { + memcpy(&avr_vpd_info.psu_hw_version, &rx_data[0], + sizeof(avr_vpd_info.psu_hw_version)); + NT_LOG(DBG, NTHW, "%s: AVR%d: PSU_HW_VER: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.psu_hw_version); + + memcpy(&avr_vpd_info.vpd_pn, &rx_data[0 + 1], + sizeof(avr_vpd_info.vpd_pn)); + NT_LOG(DBG, NTHW, "%s: AVR%d: PN: '%.*s'", p_adapter_id_str, + n_instance_no, GEN2_PN_SIZE, avr_vpd_info.vpd_pn); + + memcpy(&avr_vpd_info.vpd_pba, &rx_data[0 + 1 + GEN2_PN_SIZE], + sizeof(avr_vpd_info.vpd_pba)); + NT_LOG(DBG, NTHW, "%s: AVR%d: PBA: '%.*s'", p_adapter_id_str, + n_instance_no, GEN2_PBA_SIZE, avr_vpd_info.vpd_pba); + + memcpy(&avr_vpd_info.vpd_sn, + &rx_data[0 + 1 + GEN2_PN_SIZE + GEN2_PBA_SIZE], + sizeof(avr_vpd_info.vpd_sn)); + NT_LOG(DBG, NTHW, "%s: AVR%d: SN: '%.*s", p_adapter_id_str, + n_instance_no, GEN2_SN_SIZE, avr_vpd_info.vpd_sn); + + memcpy(&avr_vpd_info.vpd_board_name, + &rx_data[0 + 1 + GEN2_PN_SIZE + GEN2_PBA_SIZE + + GEN2_SN_SIZE], + sizeof(avr_vpd_info.vpd_board_name)); + NT_LOG(DBG, NTHW, "%s: AVR%d: BN: '%.*s'", p_adapter_id_str, + n_instance_no, GEN2_BNAME_SIZE, + avr_vpd_info.vpd_board_name); + + union mac_u { + uint8_t a_u8[8]; + uint16_t a_u16[4]; + uint32_t a_u32[2]; + uint64_t a_u64[1]; + } mac; + + /* vpd_platform_section */ + uint8_t *p_vpd_board_info = + (uint8_t *)(&rx_data[1 + GEN2_PN_SIZE + GEN2_PBA_SIZE + + GEN2_SN_SIZE + GEN2_BNAME_SIZE]); + memcpy(&avr_vpd_info.product_family, &p_vpd_board_info[0], + sizeof(avr_vpd_info.product_family)); + NT_LOG(DBG, NTHW, "%s: AVR%d: PROD_FAM: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.product_family); + + memcpy(&avr_vpd_info.feature_mask, &p_vpd_board_info[0 + 4], + sizeof(avr_vpd_info.feature_mask)); + NT_LOG(DBG, NTHW, "%s: AVR%d: FMSK_VAL: 0x%08X", + p_adapter_id_str, n_instance_no, avr_vpd_info.feature_mask); + + memcpy(&avr_vpd_info.invfeature_mask, &p_vpd_board_info[0 + 4 + 4], + sizeof(avr_vpd_info.invfeature_mask)); + NT_LOG(DBG, NTHW, "%s: AVR%d: FMSK_INV: 0x%08X", + p_adapter_id_str, n_instance_no, + avr_vpd_info.invfeature_mask); + + avr_vpd_info.b_feature_mask_valid = + (avr_vpd_info.feature_mask == + ~avr_vpd_info.invfeature_mask); + NT_LOG(DBG, NTHW, "%s: AVR%d: FMSK_TST: %s", p_adapter_id_str, + n_instance_no, + (avr_vpd_info.b_feature_mask_valid ? "OK" : "ERROR")); + + memcpy(&avr_vpd_info.no_of_macs, &p_vpd_board_info[0 + 4 + 4 + 4], + sizeof(avr_vpd_info.no_of_macs)); + NT_LOG(DBG, NTHW, "%s: AVR%d: NUM_MACS: %d", p_adapter_id_str, + n_instance_no, avr_vpd_info.no_of_macs); + + memcpy(&avr_vpd_info.mac_address, + &p_vpd_board_info[0 + 4 + 4 + 4 + 1], + sizeof(avr_vpd_info.mac_address)); + NT_LOG(DBG, NTHW, + "%s: AVR%d: MAC_ADDR: %02x:%02x:%02x:%02x:%02x:%02x", + p_adapter_id_str, n_instance_no, + avr_vpd_info.mac_address[0], avr_vpd_info.mac_address[1], + avr_vpd_info.mac_address[2], avr_vpd_info.mac_address[3], + avr_vpd_info.mac_address[4], avr_vpd_info.mac_address[5]); + + mac.a_u64[0] = 0; + memcpy(&mac.a_u8[2], &avr_vpd_info.mac_address, + sizeof(avr_vpd_info.mac_address)); + { + const uint32_t u1 = ntohl(mac.a_u32[0]); + + if (u1 != mac.a_u32[0]) { + const uint32_t u0 = ntohl(mac.a_u32[1]); + mac.a_u32[0] = u0; + mac.a_u32[1] = u1; + } + } + + avr_vpd_info.n_mac_val = mac.a_u64[0]; + NT_LOG(DBG, NTHW, "%s: AVR%d: MAC_U64: %012" PRIX64 "", + p_adapter_id_str, n_instance_no, avr_vpd_info.n_mac_val); + } + + p_fpga_info->nthw_hw_info.vpd_info.mn_mac_addr_count = + avr_vpd_info.no_of_macs; + p_fpga_info->nthw_hw_info.vpd_info.mn_mac_addr_value = + avr_vpd_info.n_mac_val; + memcpy(p_fpga_info->nthw_hw_info.vpd_info.ma_mac_addr_octets, + avr_vpd_info.mac_address, + ARRAY_SIZE(p_fpga_info->nthw_hw_info.vpd_info.ma_mac_addr_octets)); + + } else { + NT_LOG(ERR, NTHW, "%s:%u: res=%d", __func__, __LINE__, res); + NT_LOG(ERR, NTHW, "%s: AVR%d: SYSINFO2: NA: res=%d sz=%d", + p_adapter_id_str, n_instance_no, res, rx_buf.size); + } + } + + return res; +} + /* * NT200A02, NT200A01-HWbuild2 */ diff --git a/drivers/net/ntnic/nthw/core/nthw_spi_v3.c b/drivers/net/ntnic/nthw/core/nthw_spi_v3.c new file mode 100644 index 0000000000..0b611462a0 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_spi_v3.c @@ -0,0 +1,358 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_fpga.h" + +#include "nthw_spi_v3.h" + +#include <arpa/inet.h> + +#undef SPI_V3_DEBUG_PRINT + +nthw_spi_v3_t *nthw_spi_v3_new(void) +{ + nthw_spi_v3_t *p = malloc(sizeof(nthw_spi_v3_t)); + + if (p) + memset(p, 0, sizeof(nthw_spi_v3_t)); + + return p; +} + +static int nthw_spi_v3_set_timeout(nthw_spi_v3_t *p, int time_out) +{ + p->m_time_out = time_out; + return 0; +} + +/* + * Wait until Tx data have been sent after they have been placed in the Tx FIFO. + */ +static int wait_for_tx_data_sent(nthw_spim_t *p_spim_mod, uint64_t time_out) +{ + int result; + bool empty; + uint64_t start_time; + uint64_t cur_time; + start_time = nt_os_get_time_monotonic_counter(); + + while (true) { + nt_os_wait_usec(1000); /* Every 1ms */ + + result = nthw_spim_get_tx_fifo_empty(p_spim_mod, &empty); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spim_get_tx_fifo_empty failed"); + return result; + } + + if (empty) + break; + + cur_time = nt_os_get_time_monotonic_counter(); + + if ((cur_time - start_time) > time_out) { + NT_LOG(WRN, NTHW, "%s: Timed out", __func__); + return -1; + } + } + + return 0; +} + +/* + * Wait until Rx data have been received. + */ +static int wait_for_rx_data_ready(nthw_spis_t *p_spis_mod, uint64_t time_out) +{ + int result; + bool empty; + uint64_t start_time; + uint64_t cur_time; + start_time = nt_os_get_time_monotonic_counter(); + + /* Wait for data to become ready in the Rx FIFO */ + while (true) { + nt_os_wait_usec(10000); /* Every 10ms */ + + result = nthw_spis_get_rx_fifo_empty(p_spis_mod, &empty); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spis_get_rx_empty failed"); + return result; + } + + if (!empty) + break; + + cur_time = nt_os_get_time_monotonic_counter(); + + if ((cur_time - start_time) > time_out) { + NT_LOG(WRN, NTHW, "%s: Timed out", __func__); + return -1; + } + } + + return 0; +} + +#ifdef SPI_V3_DEBUG_PRINT +static void dump_hex(uint8_t *p_data, uint16_t count) +{ + int i; + int j = 0; + char tmp_str[128]; + + for (i = 0; i < count; i++) { + sprintf(&tmp_str[j * 3], "%02X ", *(p_data++)); + j++; + + if (j == 16 || i == count - 1) { + tmp_str[j * 3 - 1] = '\0'; + NT_LOG(DBG, NTHW, " %s", tmp_str); + j = 0; + } + } +} + +#endif + +int nthw_spi_v3_init(nthw_spi_v3_t *p, nthw_fpga_t *p_fpga, int n_instance_no) +{ + const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str; + uint32_t result; + + p->mn_instance_no = n_instance_no; + + nthw_spi_v3_set_timeout(p, 1); + + /* Initialize SPIM module */ + p->mp_spim_mod = nthw_spim_new(); + + result = nthw_spim_init(p->mp_spim_mod, p_fpga, n_instance_no); + + if (result != 0) + NT_LOG(ERR, NTHW, "%s: nthw_spis_init failed: %d", p_adapter_id_str, result); + + /* Initialize SPIS module */ + p->mp_spis_mod = nthw_spis_new(); + + result = nthw_spis_init(p->mp_spis_mod, p_fpga, n_instance_no); + + if (result != 0) + NT_LOG(ERR, NTHW, "%s: nthw_spim_init failed: %d", p_adapter_id_str, result); + + /* Reset SPIM and SPIS modules */ + result = nthw_spim_reset(p->mp_spim_mod); + + if (result != 0) + NT_LOG(ERR, NTHW, "%s: nthw_spim_reset failed: %d", p_adapter_id_str, result); + + result = nthw_spis_reset(p->mp_spis_mod); + + if (result != 0) + NT_LOG(ERR, NTHW, "%s: nthw_spis_reset failed: %d", p_adapter_id_str, result); + + return result; +} + +/* + * Send Tx data using the SPIM module and receive any data using the SPIS module. + * The data are sent and received being wrapped into a SPI v3 container. + */ +int nthw_spi_v3_transfer(nthw_spi_v3_t *p, uint16_t opcode, struct tx_rx_buf *tx_buf, + struct tx_rx_buf *rx_buf) +{ + const uint16_t max_payload_rx_size = rx_buf->size; + int result = 0; + +#pragma pack(push, 1) + union { + uint32_t raw; + + struct { + uint16_t opcode; + uint16_t size; + }; + } spi_tx_hdr; + + union { + uint32_t raw; + + struct { + uint16_t error_code; + uint16_t size; + }; + } spi_rx_hdr; + +#pragma pack(pop) + +#ifdef SPI_V3_DEBUG_PRINT + NT_LOG_DBG(DBG, NTHW, "Started"); +#endif + + /* Disable transmission from Tx FIFO */ + result = nthw_spim_enable(p->mp_spim_mod, false); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spim_enable failed"); + return result; + } + + /* Enable SPIS module */ + result = nthw_spis_enable(p->mp_spis_mod, true); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spis_enable failed"); + return result; + } + + /* Put data into Tx FIFO */ + spi_tx_hdr.opcode = opcode; + spi_tx_hdr.size = tx_buf->size; + +#ifdef SPI_V3_DEBUG_PRINT + NT_LOG(DBG, NTHW, "Opcode=0x%04X tx_bufSize=0x%04X rx_bufSize=0x%04X", opcode, + tx_buf->size, rx_buf->size); + +#endif /* SPI_V3_DEBUG_PRINT */ + + result = nthw_spim_write_tx_fifo(p->mp_spim_mod, htonl(spi_tx_hdr.raw)); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spim_write_tx_fifo failed"); + return result; + } + + { + uint8_t *tx_data = (uint8_t *)tx_buf->p_buf; + uint16_t tx_size = tx_buf->size; + uint16_t count; + uint32_t value; + + while (tx_size > 0) { + if (tx_size > 4) { + count = 4; + + } else { + count = tx_size; + value = 0; + } + + memcpy(&value, tx_data, count); + + result = nthw_spim_write_tx_fifo(p->mp_spim_mod, htonl(value)); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spim_write_tx_fifo failed"); + return result; + } + + tx_size = (uint16_t)(tx_size - count); + tx_data += count; + } + } + + /* Enable Tx FIFO */ + result = nthw_spim_enable(p->mp_spim_mod, true); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spim_enable failed"); + return result; + } + + result = wait_for_tx_data_sent(p->mp_spim_mod, p->m_time_out); + + if (result != 0) + return result; + +#ifdef SPI_V3_DEBUG_PRINT + NT_LOG(DBG, NTHW, "%s: SPI header and payload data have been sent", __func__); +#endif + + { + /* Start receiving data */ + uint16_t rx_size = + sizeof(spi_rx_hdr.raw); /* The first data to read is the header */ + uint8_t *rx_data = (uint8_t *)rx_buf->p_buf; + bool rx_hdr_read = false; + + rx_buf->size = 0; + + while (true) { + uint16_t count; + uint32_t value; + + if (!rx_hdr_read) { /* Read the header */ + result = wait_for_rx_data_ready(p->mp_spis_mod, p->m_time_out); + + if (result != 0) + return result; + + result = nthw_spis_read_rx_fifo(p->mp_spis_mod, &spi_rx_hdr.raw); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spis_read_rx_fifo failed"); + return result; + } + + spi_rx_hdr.raw = ntohl(spi_rx_hdr.raw); + rx_size = spi_rx_hdr.size; + rx_hdr_read = true; /* Next time read payload */ + +#ifdef SPI_V3_DEBUG_PRINT + NT_LOG(DBG, NTHW, + " spi_rx_hdr.error_code = 0x%04X, spi_rx_hdr.size = 0x%04X", + spi_rx_hdr.error_code, spi_rx_hdr.size); +#endif + + if (spi_rx_hdr.error_code != 0) { + result = -1; /* NT_ERROR_AVR_OPCODE_RETURNED_ERROR; */ + break; + } + + if (rx_size > max_payload_rx_size) { + result = 1; /* NT_ERROR_AVR_RX_BUFFER_TOO_SMALL; */ + break; + } + + } else {/* Read the payload */ + count = (uint16_t)(rx_size < 4U ? rx_size : 4U); + + if (count == 0) + break; + + result = wait_for_rx_data_ready(p->mp_spis_mod, p->m_time_out); + + if (result != 0) + return result; + + result = nthw_spis_read_rx_fifo(p->mp_spis_mod, &value); + + if (result != 0) { + NT_LOG(WRN, NTHW, "nthw_spis_read_rx_fifo failed"); + return result; + } + + value = ntohl(value); /* Convert to host endian */ + memcpy(rx_data, &value, count); + rx_buf->size = (uint16_t)(rx_buf->size + count); + rx_size = (uint16_t)(rx_size - count); + rx_data += count; + } + } + } + +#ifdef SPI_V3_DEBUG_PRINT + NT_LOG(DBG, NTHW, " RxData: %d", rx_buf->size); + dump_hex(rx_buf->p_buf, rx_buf->size); + NT_LOG(DBG, NTHW, "%s: Ended: %d", __func__, result); +#endif + + return result; +} diff --git a/drivers/net/ntnic/nthw/core/nthw_spim.c b/drivers/net/ntnic/nthw/core/nthw_spim.c new file mode 100644 index 0000000000..d30c11d0ff --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_spim.c @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_spim.h" + +nthw_spim_t *nthw_spim_new(void) +{ + nthw_spim_t *p = malloc(sizeof(nthw_spim_t)); + + if (p) + memset(p, 0, sizeof(nthw_spim_t)); + + return p; +} + +int nthw_spim_init(nthw_spim_t *p, nthw_fpga_t *p_fpga, int n_instance) +{ + const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str; + nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_SPIM, n_instance); + + if (p == NULL) + return mod == NULL ? -1 : 0; + + if (mod == NULL) { + NT_LOG(ERR, NTHW, "%s: SPIM %d: no such instance", p_adapter_id_str, n_instance); + return -1; + } + + p->mp_fpga = p_fpga; + p->mn_instance = n_instance; + p->mp_mod_spim = mod; + + /* SPIM is a primary communication channel - turn off debug by default */ + nthw_module_set_debug_mode(p->mp_mod_spim, 0x00); + + p->mp_reg_srr = nthw_module_get_register(p->mp_mod_spim, SPIM_SRR); + p->mp_fld_srr_rst = nthw_register_get_field(p->mp_reg_srr, SPIM_SRR_RST); + + p->mp_reg_cr = nthw_module_get_register(p->mp_mod_spim, SPIM_CR); + p->mp_fld_cr_loop = nthw_register_get_field(p->mp_reg_cr, SPIM_CR_LOOP); + p->mp_fld_cr_en = nthw_register_get_field(p->mp_reg_cr, SPIM_CR_EN); + p->mp_fld_cr_txrst = nthw_register_get_field(p->mp_reg_cr, SPIM_CR_TXRST); + p->mp_fld_cr_rxrst = nthw_register_get_field(p->mp_reg_cr, SPIM_CR_RXRST); + + p->mp_reg_sr = nthw_module_get_register(p->mp_mod_spim, SPIM_SR); + p->mp_fld_sr_done = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_DONE); + p->mp_fld_sr_txempty = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_TXEMPTY); + p->mp_fld_sr_rxempty = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_RXEMPTY); + p->mp_fld_sr_txfull = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_TXFULL); + p->mp_fld_sr_rxfull = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_RXFULL); + p->mp_fld_sr_txlvl = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_TXLVL); + p->mp_fld_sr_rxlvl = nthw_register_get_field(p->mp_reg_sr, SPIM_SR_RXLVL); + + p->mp_reg_dtr = nthw_module_get_register(p->mp_mod_spim, SPIM_DTR); + p->mp_fld_dtr_dtr = nthw_register_get_field(p->mp_reg_dtr, SPIM_DTR_DTR); + + p->mp_reg_drr = nthw_module_get_register(p->mp_mod_spim, SPIM_DRR); + p->mp_fld_drr_drr = nthw_register_get_field(p->mp_reg_drr, SPIM_DRR_DRR); + + p->mp_reg_cfg = nthw_module_get_register(p->mp_mod_spim, SPIM_CFG); + p->mp_fld_cfg_pre = nthw_register_get_field(p->mp_reg_cfg, SPIM_CFG_PRE); + + p->mp_reg_cfg_clk = nthw_module_query_register(p->mp_mod_spim, SPIM_CFG_CLK); + p->mp_fld_cfg_clk_mode = nthw_register_query_field(p->mp_reg_cfg, SPIM_CFG_CLK_MODE); + + return 0; +} + +uint32_t nthw_spim_reset(nthw_spim_t *p) +{ + nthw_register_update(p->mp_reg_srr); + nthw_field_set_val32(p->mp_fld_srr_rst, 0x0A); /* 0x0A hardcoded value - see doc */ + nthw_register_flush(p->mp_reg_srr, 1); + + return 0; +} + +uint32_t nthw_spim_enable(nthw_spim_t *p, bool b_enable) +{ + nthw_field_update_register(p->mp_fld_cr_en); + + if (b_enable) + nthw_field_set_all(p->mp_fld_cr_en); + + else + nthw_field_clr_all(p->mp_fld_cr_en); + + nthw_field_flush_register(p->mp_fld_cr_en); + + return 0; +} + +uint32_t nthw_spim_write_tx_fifo(nthw_spim_t *p, uint32_t n_data) +{ + nthw_field_set_val_flush32(p->mp_fld_dtr_dtr, n_data); + return 0; +} + +uint32_t nthw_spim_get_tx_fifo_empty(nthw_spim_t *p, bool *pb_empty) +{ + assert(pb_empty); + + *pb_empty = nthw_field_get_updated(p->mp_fld_sr_txempty) ? true : false; + + return 0; +} diff --git a/drivers/net/ntnic/nthw/core/nthw_spis.c b/drivers/net/ntnic/nthw/core/nthw_spis.c new file mode 100644 index 0000000000..3c34dec936 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_spis.c @@ -0,0 +1,121 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_spis.h" + +nthw_spis_t *nthw_spis_new(void) +{ + nthw_spis_t *p = malloc(sizeof(nthw_spis_t)); + + if (p) + memset(p, 0, sizeof(nthw_spis_t)); + + return p; +} + +int nthw_spis_init(nthw_spis_t *p, nthw_fpga_t *p_fpga, int n_instance) +{ + const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str; + nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_SPIS, n_instance); + + if (p == NULL) + return mod == NULL ? -1 : 0; + + if (mod == NULL) { + NT_LOG(ERR, NTHW, "%s: SPIS %d: no such instance", p_adapter_id_str, n_instance); + return -1; + } + + p->mp_fpga = p_fpga; + p->mn_instance = n_instance; + p->mp_mod_spis = mod; + + /* SPIS is a primary communication channel - turn off debug by default */ + nthw_module_set_debug_mode(p->mp_mod_spis, 0x00); + + p->mp_reg_srr = nthw_module_get_register(p->mp_mod_spis, SPIS_SRR); + p->mp_fld_srr_rst = nthw_register_get_field(p->mp_reg_srr, SPIS_SRR_RST); + + p->mp_reg_cr = nthw_module_get_register(p->mp_mod_spis, SPIS_CR); + p->mp_fld_cr_loop = nthw_register_get_field(p->mp_reg_cr, SPIS_CR_LOOP); + p->mp_fld_cr_en = nthw_register_get_field(p->mp_reg_cr, SPIS_CR_EN); + p->mp_fld_cr_txrst = nthw_register_get_field(p->mp_reg_cr, SPIS_CR_TXRST); + p->mp_fld_cr_rxrst = nthw_register_get_field(p->mp_reg_cr, SPIS_CR_RXRST); + p->mp_fld_cr_debug = nthw_register_get_field(p->mp_reg_cr, SPIS_CR_DEBUG); + + p->mp_reg_sr = nthw_module_get_register(p->mp_mod_spis, SPIS_SR); + p->mp_fld_sr_done = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_DONE); + p->mp_fld_sr_txempty = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_TXEMPTY); + p->mp_fld_sr_rxempty = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_RXEMPTY); + p->mp_fld_sr_txfull = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_TXFULL); + p->mp_fld_sr_rxfull = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_RXFULL); + p->mp_fld_sr_txlvl = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_TXLVL); + p->mp_fld_sr_rxlvl = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_RXLVL); + p->mp_fld_sr_frame_err = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_FRAME_ERR); + p->mp_fld_sr_read_err = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_READ_ERR); + p->mp_fld_sr_write_err = nthw_register_get_field(p->mp_reg_sr, SPIS_SR_WRITE_ERR); + + p->mp_reg_dtr = nthw_module_get_register(p->mp_mod_spis, SPIS_DTR); + p->mp_fld_dtr_dtr = nthw_register_get_field(p->mp_reg_dtr, SPIS_DTR_DTR); + + p->mp_reg_drr = nthw_module_get_register(p->mp_mod_spis, SPIS_DRR); + p->mp_fld_drr_drr = nthw_register_get_field(p->mp_reg_drr, SPIS_DRR_DRR); + + p->mp_reg_ram_ctrl = nthw_module_get_register(p->mp_mod_spis, SPIS_RAM_CTRL); + p->mp_fld_ram_ctrl_adr = nthw_register_get_field(p->mp_reg_ram_ctrl, SPIS_RAM_CTRL_ADR); + p->mp_fld_ram_ctrl_cnt = nthw_register_get_field(p->mp_reg_ram_ctrl, SPIS_RAM_CTRL_CNT); + + p->mp_reg_ram_data = nthw_module_get_register(p->mp_mod_spis, SPIS_RAM_DATA); + p->mp_fld_ram_data_data = nthw_register_get_field(p->mp_reg_ram_data, SPIS_RAM_DATA_DATA); + + return 0; +} + +uint32_t nthw_spis_reset(nthw_spis_t *p) +{ + nthw_register_update(p->mp_reg_srr); + nthw_field_set_val32(p->mp_fld_srr_rst, 0x0A); /* 0x0A hardcoded value - see doc */ + nthw_register_flush(p->mp_reg_srr, 1); + + return 0; +} + +uint32_t nthw_spis_enable(nthw_spis_t *p, bool b_enable) +{ + nthw_field_update_register(p->mp_fld_cr_en); + + if (b_enable) + nthw_field_set_all(p->mp_fld_cr_en); + + else + nthw_field_clr_all(p->mp_fld_cr_en); + + nthw_field_flush_register(p->mp_fld_cr_en); + + return 0; +} + +uint32_t nthw_spis_get_rx_fifo_empty(nthw_spis_t *p, bool *pb_empty) +{ + assert(pb_empty); + + *pb_empty = nthw_field_get_updated(p->mp_fld_sr_rxempty) ? true : false; + + return 0; +} + +uint32_t nthw_spis_read_rx_fifo(nthw_spis_t *p, uint32_t *p_data) +{ + assert(p_data); + + *p_data = nthw_field_get_updated(p->mp_fld_drr_drr); + + return 0; +} -- 2.45.0