From 56ccfe512d228ba2447d7fbd693dc8a87ac3efe8 Mon Sep 17 00:00:00 2001 From: Eliza Balas Date: Tue, 4 Feb 2025 13:31:54 +0200 Subject: [PATCH 1/4] drivers: net: Add Corundum MQNIC driver Add MQNIC (Multi-Queue Network Interface Card) source files. Corundum (MQNIC) is an open-source FPGA-based network interface card (NIC) platform. The original Corundum source files are imported from the Corundum repo: https://github.com/ucsdsysnet/corundum/tree/master/modules/mqnic Some changes are added to the original source files: - Port to linux kernel v6.12 - Cleanup code to comply with checkpatch guidelines - Fix incorrect pointer check in mqnic_platform_module_eeprom_put(); - Use cast for dma_addr_t to u64 before right-shifting by 32 bits in BAR address setup to make it compatible to 32-bit platforms; - Use div_s64 for compatibility with microblaze; - Use CORUNDUM_MQNIC_PCI entry to enable PCI support Signed-off-by: Eliza Balas --- drivers/net/Kconfig | 2 + drivers/net/Makefile | 1 + drivers/net/mqnic/Kconfig | 28 + drivers/net/mqnic/Makefile | 52 ++ drivers/net/mqnic/mqnic.h | 705 +++++++++++++++ drivers/net/mqnic/mqnic_board.c | 1155 +++++++++++++++++++++++++ drivers/net/mqnic/mqnic_clk_info.c | 96 ++ drivers/net/mqnic/mqnic_cq.c | 135 +++ drivers/net/mqnic/mqnic_dev.c | 171 ++++ drivers/net/mqnic/mqnic_devlink.c | 93 ++ drivers/net/mqnic/mqnic_eq.c | 244 ++++++ drivers/net/mqnic/mqnic_ethtool.c | 611 +++++++++++++ drivers/net/mqnic/mqnic_hw.h | 448 ++++++++++ drivers/net/mqnic/mqnic_i2c.c | 156 ++++ drivers/net/mqnic/mqnic_if.c | 460 ++++++++++ drivers/net/mqnic/mqnic_ioctl.h | 59 ++ drivers/net/mqnic/mqnic_irq.c | 123 +++ drivers/net/mqnic/mqnic_main.c | 888 +++++++++++++++++++ drivers/net/mqnic/mqnic_netdev.c | 718 +++++++++++++++ drivers/net/mqnic/mqnic_port.c | 156 ++++ drivers/net/mqnic/mqnic_ptp.c | 264 ++++++ drivers/net/mqnic/mqnic_reg_block.c | 93 ++ drivers/net/mqnic/mqnic_res.c | 78 ++ drivers/net/mqnic/mqnic_rx.c | 441 ++++++++++ drivers/net/mqnic/mqnic_sched_block.c | 110 +++ drivers/net/mqnic/mqnic_sched_port.c | 152 ++++ drivers/net/mqnic/mqnic_scheduler.c | 272 ++++++ drivers/net/mqnic/mqnic_stats.c | 33 + drivers/net/mqnic/mqnic_tx.c | 526 +++++++++++ 29 files changed, 8270 insertions(+) create mode 100644 drivers/net/mqnic/Kconfig create mode 100644 drivers/net/mqnic/Makefile create mode 100644 drivers/net/mqnic/mqnic.h create mode 100644 drivers/net/mqnic/mqnic_board.c create mode 100644 drivers/net/mqnic/mqnic_clk_info.c create mode 100644 drivers/net/mqnic/mqnic_cq.c create mode 100644 drivers/net/mqnic/mqnic_dev.c create mode 100644 drivers/net/mqnic/mqnic_devlink.c create mode 100644 drivers/net/mqnic/mqnic_eq.c create mode 100644 drivers/net/mqnic/mqnic_ethtool.c create mode 100644 drivers/net/mqnic/mqnic_hw.h create mode 100644 drivers/net/mqnic/mqnic_i2c.c create mode 100644 drivers/net/mqnic/mqnic_if.c create mode 100644 drivers/net/mqnic/mqnic_ioctl.h create mode 100644 drivers/net/mqnic/mqnic_irq.c create mode 100644 drivers/net/mqnic/mqnic_main.c create mode 100644 drivers/net/mqnic/mqnic_netdev.c create mode 100644 drivers/net/mqnic/mqnic_port.c create mode 100644 drivers/net/mqnic/mqnic_ptp.c create mode 100644 drivers/net/mqnic/mqnic_reg_block.c create mode 100644 drivers/net/mqnic/mqnic_res.c create mode 100644 drivers/net/mqnic/mqnic_rx.c create mode 100644 drivers/net/mqnic/mqnic_sched_block.c create mode 100644 drivers/net/mqnic/mqnic_sched_port.c create mode 100644 drivers/net/mqnic/mqnic_scheduler.c create mode 100644 drivers/net/mqnic/mqnic_stats.c create mode 100644 drivers/net/mqnic/mqnic_tx.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9920b3a68ed158..ba737935311572 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -552,6 +552,8 @@ source "drivers/net/mctp/Kconfig" source "drivers/net/mdio/Kconfig" +source "drivers/net/mqnic/Kconfig" + source "drivers/net/pcs/Kconfig" source "drivers/net/plip/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 13743d0e83b5fd..1198598b6bde65 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_WLAN) += wireless/ obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_WWAN) += wwan/ obj-$(CONFIG_MCTP) += mctp/ +obj-$(CONFIG_CORUNDUM_MQNIC) += mqnic/ obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o diff --git a/drivers/net/mqnic/Kconfig b/drivers/net/mqnic/Kconfig new file mode 100644 index 00000000000000..d5e74b4e5b71bd --- /dev/null +++ b/drivers/net/mqnic/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: BSD-2-Clause-Views +# Copyright (c) 2019-2023 The Regents of the University of California + +menuconfig CORUNDUM_MQNIC + tristate "Corundum MQNIC support" + select NET_DEVLINK + select I2C_ALGOBIT + depends on RTC_CLASS + help + Corundum is an open-source, FPGA-based Network Interface Card (NIC) + and platform designed for high-performance networking and in-network + compute applications. + + Say yes here to build support for Corundum network interface card. + +if CORUNDUM_MQNIC + +config CORUNDUM_MQNIC_PCI + bool "Enable PCI support for Corundum MQNIC" + depends on CORUNDUM_MQNIC && PCI + help + Enable support for Corundum MQNIC devices connected via the PCI bus. + Corundum supports both PCI and AXI interfaces, so enabling this + option allows the driver to work with PCIe-based hardware. If you are + targeting FPGA boards or systems that use PCIe for host communication, + select this option. + +endif # CORUNDUM_MQNIC diff --git a/drivers/net/mqnic/Makefile b/drivers/net/mqnic/Makefile new file mode 100644 index 00000000000000..47d0c88e61b37d --- /dev/null +++ b/drivers/net/mqnic/Makefile @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-2-Clause-Views +# Copyright (c) 2019-2023 The Regents of the University of California + +ifneq ($(KERNELRELEASE),) + +# object files to build +obj-$(CONFIG_CORUNDUM_MQNIC) += mqnic.o +mqnic-y += mqnic_main.o +mqnic-y += mqnic_devlink.o +mqnic-y += mqnic_res.o +mqnic-y += mqnic_reg_block.o +mqnic-y += mqnic_irq.o +mqnic-y += mqnic_dev.o +mqnic-y += mqnic_if.o +mqnic-y += mqnic_port.o +mqnic-y += mqnic_netdev.o +mqnic-y += mqnic_sched_block.o +mqnic-y += mqnic_sched_port.o +mqnic-y += mqnic_scheduler.o +mqnic-y += mqnic_ptp.o +mqnic-y += mqnic_i2c.o +mqnic-y += mqnic_board.o +mqnic-y += mqnic_clk_info.o +mqnic-y += mqnic_stats.o +mqnic-y += mqnic_tx.o +mqnic-y += mqnic_rx.o +mqnic-y += mqnic_cq.o +mqnic-y += mqnic_eq.o +mqnic-y += mqnic_ethtool.o + +ifneq ($(DEBUG),) +ccflags-y += -DDEBUG +endif + +else + +ifneq ($(KERNEL_SRC),) +# alternatively to variable KDIR accept variable KERNEL_SRC as used in +# PetaLinux/Yocto for example +KDIR ?= $(KERNEL_SRC) +endif + +KDIR ?= /lib/modules/$(shell uname -r)/build + +all: modules + +help modules modules_install clean: + $(MAKE) -C $(KDIR) M=$(shell pwd) $@ + +install: modules_install + +endif diff --git a/drivers/net/mqnic/mqnic.h b/drivers/net/mqnic/mqnic.h new file mode 100644 index 00000000000000..46f57eaf633da8 --- /dev/null +++ b/drivers/net/mqnic/mqnic.h @@ -0,0 +1,705 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Views + * + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#ifndef MQNIC_H +#define MQNIC_H + +#include +#ifdef CONFIG_CORUNDUM_MQNIC_PCI +#include +#endif +#ifdef CONFIG_AUXILIARY_BUS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "mqnic" +#define DRIVER_VERSION "0.1" + +#include "mqnic_hw.h" + +#ifdef CONFIG_OF +/* platform driver OF-related definitions */ +#define MQNIC_PROP_MAC_ADDR_INC_BYTE "mac-address-increment-byte" +#define MQNIC_PROP_MAC_ADDR_INC "mac-address-increment" +#define MQNIC_PROP_MAC_ADDR_LOCAL "mac-address-local" +#define MQNIC_PROP_MODULE_EEPROM "module-eeproms" +#endif + +// default interval to poll port TX/RX status, in ms +#define MQNIC_LINK_STATUS_POLL_MS 1000 + +extern unsigned int mqnic_num_eq_entries; +extern unsigned int mqnic_num_txq_entries; +extern unsigned int mqnic_num_rxq_entries; + +extern unsigned int mqnic_link_status_poll; + +struct mqnic_dev; +struct mqnic_if; + +struct mqnic_res { + unsigned int count; + u8 __iomem *base; + unsigned int stride; + // protects resource bitmap + spinlock_t lock; + unsigned long *bmap; +}; + +struct mqnic_reg_block { + u32 type; + u32 version; + u8 __iomem *regs; + u8 __iomem *base; +}; + +struct mqnic_board_ops { + int (*init)(struct mqnic_dev *mqnic); + void (*deinit)(struct mqnic_dev *mqnic); +}; + +struct mqnic_i2c_bus { + struct mqnic_dev *mqnic; + + u8 __iomem *scl_in_reg; + u8 __iomem *scl_out_reg; + u8 __iomem *sda_in_reg; + u8 __iomem *sda_out_reg; + + u32 scl_in_mask; + u32 scl_out_mask; + u32 sda_in_mask; + u32 sda_out_mask; + + struct list_head head; + + struct i2c_algo_bit_data algo; + struct i2c_adapter adapter; +}; + +struct mqnic_irq { + int index; + int irqn; + char name[16 + 3]; + struct atomic_notifier_head nh; + struct list_head list; +}; + +#ifdef CONFIG_AUXILIARY_BUS +struct mqnic_adev { + struct auxiliary_device adev; + struct mqnic_dev *mdev; + struct mqnic_adev **ptr; + char name[32]; +}; +#endif + +struct mqnic_dev { + struct device *dev; +#ifdef CONFIG_CORUNDUM_MQNIC_PCI + struct pci_dev *pdev; +#endif + struct platform_device *pfdev; + + resource_size_t hw_regs_size; + phys_addr_t hw_regs_phys; + u8 __iomem *hw_addr; + u8 __iomem *phc_hw_addr; + + resource_size_t app_hw_regs_size; + phys_addr_t app_hw_regs_phys; + u8 __iomem *app_hw_addr; + + resource_size_t ram_hw_regs_size; + phys_addr_t ram_hw_regs_phys; + u8 __iomem *ram_hw_addr; + + // protects device state + struct mutex state_lock; + + int mac_count; + u8 mac_list[MQNIC_MAX_IF][ETH_ALEN]; + + char name[16]; + + int irq_count; + struct mqnic_irq *irq[MQNIC_MAX_IRQ]; + + unsigned int id; + struct list_head dev_list_node; + + struct miscdevice misc_dev; + +#ifdef CONFIG_AUXILIARY_BUS + struct mqnic_adev *app_adev; +#endif + + struct mqnic_reg_block *rb_list; + struct mqnic_reg_block *fw_id_rb; + struct mqnic_reg_block *if_rb; + struct mqnic_reg_block *stats_rb; + struct mqnic_reg_block *clk_info_rb; + struct mqnic_reg_block *phc_rb; + + int phys_port_max; + + u32 fpga_id; + u32 fw_id; + u32 fw_ver; + u32 board_id; + u32 board_ver; + u32 build_date; + u32 git_hash; + u32 rel_info; + + u32 app_id; + + u32 stats_offset; + u32 stats_count; + u32 stats_stride; + u32 stats_flags; + + u32 core_clk_nom_per_ns_num; + u32 core_clk_nom_per_ns_denom; + u32 core_clk_nom_freq_hz; + u32 ref_clk_nom_per_ns_num; + u32 ref_clk_nom_per_ns_denom; + u32 ref_clk_nom_freq_hz; + u32 clk_info_channels; + + u32 if_offset; + u32 if_count; + u32 if_stride; + u32 if_csr_offset; + + char build_date_str[32]; + + struct mqnic_if *interface[MQNIC_MAX_IF]; + + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + + struct mqnic_board_ops *board_ops; + + struct list_head i2c_bus; + int i2c_adapter_count; + + int mod_i2c_client_count; + struct i2c_client *mod_i2c_client[MQNIC_MAX_IF]; + struct i2c_client *eeprom_i2c_client; +}; + +struct mqnic_frag { + dma_addr_t dma_addr; + u32 len; +}; + +struct mqnic_tx_info { + struct sk_buff *skb; + DEFINE_DMA_UNMAP_ADDR(dma_addr); + DEFINE_DMA_UNMAP_LEN(len); + u32 frag_count; + struct mqnic_frag frags[MQNIC_MAX_FRAGS - 1]; + int ts_requested; +}; + +struct mqnic_rx_info { + struct page *page; + u32 page_order; + u32 page_offset; + dma_addr_t dma_addr; + u32 len; +}; + +struct mqnic_ring { + // written on enqueue (i.e. start_xmit) + u32 prod_ptr; + u64 bytes; + u64 packets; + u64 dropped_packets; + struct netdev_queue *tx_queue; + + // written from completion + u32 cons_ptr ____cacheline_aligned_in_smp; + u64 ts_s; + u8 ts_valid; + + // mostly constant + u32 size; + u32 full_size; + u32 size_mask; + u32 stride; + + u32 mtu; + u32 page_order; + + u32 desc_block_size; + u32 log_desc_block_size; + + size_t buf_size; + u8 *buf; + dma_addr_t buf_dma_addr; + + union { + struct mqnic_tx_info *tx_info; + struct mqnic_rx_info *rx_info; + }; + + struct device *dev; + struct mqnic_if *interface; + struct mqnic_priv *priv; + int index; + struct mqnic_cq *cq; + int enabled; + + u8 __iomem *hw_addr; +} ____cacheline_aligned_in_smp; + +struct mqnic_cq { + u32 cons_ptr; + + u32 size; + u32 size_mask; + u32 stride; + + size_t buf_size; + u8 *buf; + dma_addr_t buf_dma_addr; + + struct device *dev; + struct mqnic_if *interface; + struct napi_struct napi; + int cqn; + struct mqnic_eq *eq; + struct mqnic_ring *src_ring; + int enabled; + + void (*handler)(struct mqnic_cq *cq); + + u8 __iomem *hw_addr; +}; + +struct mqnic_eq { + u32 cons_ptr; + + u32 size; + u32 size_mask; + u32 stride; + + size_t buf_size; + u8 *buf; + dma_addr_t buf_dma_addr; + + struct device *dev; + struct mqnic_if *interface; + int eqn; + struct mqnic_irq *irq; + int enabled; + + struct notifier_block irq_nb; + + void (*handler)(struct mqnic_eq *eq); + + // protects CQ table + spinlock_t table_lock; + struct radix_tree_root cq_table; + + u8 __iomem *hw_addr; +}; + +struct mqnic_sched { + struct device *dev; + struct mqnic_if *interface; + struct mqnic_sched_block *sched_block; + + struct mqnic_reg_block *rb; + + int index; + + u32 type; + u32 offset; + u32 queue_count; + u32 queue_stride; + + int tc_count; + int port_count; + int channel_count; + int fc_scale; + + int enable_count; + + struct list_head sched_port_list; + + u8 __iomem *hw_addr; +}; + +struct mqnic_sched_port { + struct mqnic_sched *sched; + int index; + struct list_head list; + struct list_head free_list; +}; + +struct mqnic_port { + struct device *dev; + struct mqnic_if *interface; + + struct mqnic_reg_block *port_rb; + struct mqnic_reg_block *rb_list; + struct mqnic_reg_block *port_ctrl_rb; + + int index; + int phys_index; + + u32 port_features; + + struct devlink_port dl_port; +}; + +struct mqnic_sched_block { + struct device *dev; + struct mqnic_if *interface; + + struct mqnic_reg_block *block_rb; + struct mqnic_reg_block *rb_list; + + int index; + + u32 sched_count; + struct mqnic_sched *sched[MQNIC_MAX_PORTS]; +}; + +struct mqnic_if { + struct device *dev; + struct mqnic_dev *mdev; + + struct mqnic_reg_block *rb_list; + struct mqnic_reg_block *if_ctrl_rb; + struct mqnic_reg_block *eq_rb; + struct mqnic_reg_block *cq_rb; + struct mqnic_reg_block *txq_rb; + struct mqnic_reg_block *rxq_rb; + struct mqnic_reg_block *rx_queue_map_rb; + + int index; + + u32 if_features; + + u32 max_tx_mtu; + u32 max_rx_mtu; + u32 tx_fifo_depth; + u32 rx_fifo_depth; + + struct mqnic_res *eq_res; + struct mqnic_res *cq_res; + struct mqnic_res *txq_res; + struct mqnic_res *rxq_res; + + u32 eq_count; + struct rw_semaphore eq_table_sem; + struct radix_tree_root eq_table; + + u32 port_count; + struct mqnic_port *port[MQNIC_MAX_PORTS]; + + u32 sched_block_count; + struct mqnic_sched_block *sched_block[MQNIC_MAX_PORTS]; + // protects free sched_port list + spinlock_t free_sched_port_list_lock; + struct list_head free_sched_port_list; + + u32 max_desc_block_size; + + u32 rx_queue_map_indir_table_size; + u8 __iomem *rx_queue_map_indir_table[MQNIC_MAX_PORTS]; + + resource_size_t hw_regs_size; + u8 __iomem *hw_addr; + u8 __iomem *csr_hw_addr; + + u32 ndev_count; + struct list_head ndev_list; + + struct i2c_client *mod_i2c_client; +}; + +struct mqnic_priv { + struct device *dev; + struct net_device *ndev; + struct devlink_port *dl_port; + struct mqnic_dev *mdev; + struct mqnic_if *interface; + // protects stats info + spinlock_t stats_lock; + + bool registered; + bool port_up; + + u32 if_features; + + unsigned int link_status; + struct timer_list link_status_timer; + + u32 txq_count; + u32 rxq_count; + + u32 tx_ring_size; + u32 rx_ring_size; + + struct rw_semaphore txq_table_sem; + struct radix_tree_root txq_table; + + struct rw_semaphore rxq_table_sem; + struct radix_tree_root rxq_table; + + struct mqnic_sched_port *sched_port; + struct mqnic_port *port; + + u32 max_desc_block_size; + + u32 rx_queue_map_indir_table_size; + u32 *rx_queue_map_indir_table; + + struct hwtstamp_config hwts_config; + + struct list_head ndev_list; + + struct i2c_client *mod_i2c_client; +}; + +// mqnic_main.c + +// mqnic_devlink.c +struct devlink *mqnic_devlink_alloc(struct device *dev); +void mqnic_devlink_free(struct devlink *devlink); + +// mqnic_res.c +struct mqnic_res *mqnic_create_res(unsigned int count, u8 __iomem *base, unsigned int stride); +void mqnic_destroy_res(struct mqnic_res *res); +int mqnic_res_alloc(struct mqnic_res *res); +void mqnic_res_free(struct mqnic_res *res, int index); +unsigned int mqnic_res_get_count(struct mqnic_res *res); +u8 __iomem *mqnic_res_get_addr(struct mqnic_res *res, int index); + +// mqnic_reg_block.c +struct mqnic_reg_block *mqnic_enumerate_reg_block_list(u8 __iomem *base, size_t offset, size_t size); +struct mqnic_reg_block *mqnic_find_reg_block(struct mqnic_reg_block *list, u32 type, u32 version, int index); +void mqnic_free_reg_block_list(struct mqnic_reg_block *list); + +// mqnic_irq.c +int mqnic_irq_init_pcie(struct mqnic_dev *mdev); +void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev); +int mqnic_irq_init_platform(struct mqnic_dev *mdev); + +// mqnic_dev.c +extern const struct file_operations mqnic_fops; + +// mqnic_if.c +struct mqnic_if *mqnic_create_interface(struct mqnic_dev *mdev, int index, u8 __iomem *hw_addr); +void mqnic_destroy_interface(struct mqnic_if *interface); +u32 mqnic_interface_get_tx_mtu(struct mqnic_if *interface); +void mqnic_interface_set_tx_mtu(struct mqnic_if *interface, u32 mtu); +u32 mqnic_interface_get_rx_mtu(struct mqnic_if *interface); +void mqnic_interface_set_rx_mtu(struct mqnic_if *interface, u32 mtu); +u32 mqnic_interface_get_rx_queue_map_rss_mask(struct mqnic_if *interface, int port); +void mqnic_interface_set_rx_queue_map_rss_mask(struct mqnic_if *interface, int port, u32 val); +u32 mqnic_interface_get_rx_queue_map_app_mask(struct mqnic_if *interface, int port); +void mqnic_interface_set_rx_queue_map_app_mask(struct mqnic_if *interface, int port, u32 val); +u32 mqnic_interface_get_rx_queue_map_indir_table(struct mqnic_if *interface, int port, int index); +void mqnic_interface_set_rx_queue_map_indir_table(struct mqnic_if *interface, int port, int index, u32 val); +int mqnic_interface_register_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port); +int mqnic_interface_unregister_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port); +struct mqnic_sched_port *mqnic_interface_alloc_sched_port(struct mqnic_if *interface); +void mqnic_interface_free_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port); + +// mqnic_port.c +struct mqnic_port *mqnic_create_port(struct mqnic_if *interface, int index, + int phys_index, struct mqnic_reg_block *port_rb); +void mqnic_destroy_port(struct mqnic_port *port); +u32 mqnic_port_get_tx_ctrl(struct mqnic_port *port); +void mqnic_port_set_tx_ctrl(struct mqnic_port *port, u32 val); +u32 mqnic_port_get_rx_ctrl(struct mqnic_port *port); +void mqnic_port_set_rx_ctrl(struct mqnic_port *port, u32 val); +u32 mqnic_port_get_fc_ctrl(struct mqnic_port *port); +void mqnic_port_set_fc_ctrl(struct mqnic_port *port, u32 val); +u32 mqnic_port_get_lfc_ctrl(struct mqnic_port *port); +void mqnic_port_set_lfc_ctrl(struct mqnic_port *port, u32 val); +u32 mqnic_port_get_pfc_ctrl(struct mqnic_port *port, int index); +void mqnic_port_set_pfc_ctrl(struct mqnic_port *port, int index, u32 val); + +// mqnic_netdev.c +int mqnic_start_port(struct net_device *ndev); +void mqnic_stop_port(struct net_device *ndev); +int mqnic_update_indir_table(struct net_device *ndev); +void mqnic_update_stats(struct net_device *ndev); +struct net_device *mqnic_create_netdev(struct mqnic_if *interface, struct mqnic_port *port); +void mqnic_destroy_netdev(struct net_device *ndev); + +// mqnic_sched_block.c +struct mqnic_sched_block *mqnic_create_sched_block(struct mqnic_if *interface, + int index, struct mqnic_reg_block *rb); +void mqnic_destroy_sched_block(struct mqnic_sched_block *block); +int mqnic_activate_sched_block(struct mqnic_sched_block *block); +void mqnic_deactivate_sched_block(struct mqnic_sched_block *block); + +// mqnic_scheduler.c +struct mqnic_sched *mqnic_create_scheduler(struct mqnic_sched_block *block, + int index, struct mqnic_reg_block *rb); +void mqnic_destroy_scheduler(struct mqnic_sched *sched); +int mqnic_scheduler_enable(struct mqnic_sched *sched); +void mqnic_scheduler_disable(struct mqnic_sched *sched); +int mqnic_scheduler_channel_enable(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_disable(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_set_dest(struct mqnic_sched *sched, int port, int tc, int val); +int mqnic_scheduler_channel_get_dest(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_set_pkt_budget(struct mqnic_sched *sched, int port, int tc, int val); +int mqnic_scheduler_channel_get_pkt_budget(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_set_data_budget(struct mqnic_sched *sched, int port, int tc, int val); +int mqnic_scheduler_channel_get_data_budget(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_set_pkt_limit(struct mqnic_sched *sched, int port, int tc, int val); +int mqnic_scheduler_channel_get_pkt_limit(struct mqnic_sched *sched, int port, int tc); +void mqnic_scheduler_channel_set_data_limit(struct mqnic_sched *sched, int port, int tc, int val); +int mqnic_scheduler_channel_get_data_limit(struct mqnic_sched *sched, int port, int tc); +int mqnic_scheduler_queue_enable(struct mqnic_sched *sched, int queue); +void mqnic_scheduler_queue_disable(struct mqnic_sched *sched, int queue); +void mqnic_scheduler_queue_set_pause(struct mqnic_sched *sched, int queue, int val); +int mqnic_scheduler_queue_get_pause(struct mqnic_sched *sched, int queue); +int mqnic_scheduler_queue_port_enable(struct mqnic_sched *sched, int queue, int port); +void mqnic_scheduler_queue_port_disable(struct mqnic_sched *sched, int queue, int port); +void mqnic_scheduler_queue_port_set_pause(struct mqnic_sched *sched, int queue, int port, int val); +int mqnic_scheduler_queue_port_get_pause(struct mqnic_sched *sched, int queue, int port); +void mqnic_scheduler_queue_port_set_tc(struct mqnic_sched *sched, int queue, int port, int val); +int mqnic_scheduler_queue_port_get_tc(struct mqnic_sched *sched, int queue, int port); + +// mqnic_sched_port.c +struct mqnic_sched_port *mqnic_create_sched_port(struct mqnic_sched *sched, int index); +void mqnic_destroy_sched_port(struct mqnic_sched_port *port); +int mqnic_sched_port_enable(struct mqnic_sched_port *port); +void mqnic_sched_port_disable(struct mqnic_sched_port *port); +int mqnic_sched_port_channel_enable(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_disable(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_set_dest(struct mqnic_sched_port *port, int tc, int val); +int mqnic_sched_port_channel_get_dest(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_set_pkt_budget(struct mqnic_sched_port *port, int tc, int val); +int mqnic_sched_port_channel_get_pkt_budget(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_set_data_budget(struct mqnic_sched_port *port, int tc, int val); +int mqnic_sched_port_channel_get_data_budget(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_set_pkt_limit(struct mqnic_sched_port *port, int tc, int val); +int mqnic_sched_port_channel_get_pkt_limit(struct mqnic_sched_port *port, int tc); +void mqnic_sched_port_channel_set_data_limit(struct mqnic_sched_port *port, int tc, int val); +int mqnic_sched_port_channel_get_data_limit(struct mqnic_sched_port *port, int tc); +int mqnic_sched_port_queue_enable(struct mqnic_sched_port *port, int queue); +void mqnic_sched_port_queue_disable(struct mqnic_sched_port *port, int queue); +void mqnic_sched_port_queue_set_pause(struct mqnic_sched_port *port, int queue, int val); +int mqnic_sched_port_queue_get_pause(struct mqnic_sched_port *port, int queue); +void mqnic_sched_port_queue_set_tc(struct mqnic_sched_port *port, int queue, int val); +int mqnic_sched_port_queue_get_tc(struct mqnic_sched_port *port, int queue); + +// mqnic_ptp.c +void mqnic_register_phc(struct mqnic_dev *mdev); +void mqnic_unregister_phc(struct mqnic_dev *mdev); +ktime_t mqnic_read_cpl_ts(struct mqnic_dev *mdev, struct mqnic_ring *ring, + const struct mqnic_cpl *cpl); + +// mqnic_i2c.c +struct mqnic_i2c_bus *mqnic_i2c_bus_create(struct mqnic_dev *mqnic, int index); +struct i2c_adapter *mqnic_i2c_adapter_create(struct mqnic_dev *mqnic, int index); +void mqnic_i2c_bus_release(struct mqnic_i2c_bus *bus); +void mqnic_i2c_adapter_release(struct i2c_adapter *adapter); +int mqnic_i2c_init(struct mqnic_dev *mqnic); +void mqnic_i2c_deinit(struct mqnic_dev *mqnic); + +// mqnic_board.c +int mqnic_board_init(struct mqnic_dev *mqnic); +void mqnic_board_deinit(struct mqnic_dev *mqnic); + +// mqnic_clk_info.c +void mqnic_clk_info_init(struct mqnic_dev *mdev); +u32 mqnic_get_core_clk_nom_freq_hz(struct mqnic_dev *mdev); +u32 mqnic_get_ref_clk_nom_freq_hz(struct mqnic_dev *mdev); +u32 mqnic_get_core_clk_freq_hz(struct mqnic_dev *mdev); +u32 mqnic_get_clk_freq_hz(struct mqnic_dev *mdev, int ch); +u64 mqnic_core_clk_cycles_to_ns(struct mqnic_dev *mdev, u64 cycles); +u64 mqnic_core_clk_ns_to_cycles(struct mqnic_dev *mdev, u64 ns); +u64 mqnic_ref_clk_cycles_to_ns(struct mqnic_dev *mdev, u64 cycles); +u64 mqnic_ref_clk_ns_to_cycles(struct mqnic_dev *mdev, u64 ns); + +// mqnic_stats.c +void mqnic_stats_init(struct mqnic_dev *mdev); +u64 mqnic_stats_read(struct mqnic_dev *mdev, int index); + +// mqnic_eq.c +struct mqnic_eq *mqnic_create_eq(struct mqnic_if *interface); +void mqnic_destroy_eq(struct mqnic_eq *eq); +int mqnic_open_eq(struct mqnic_eq *eq, struct mqnic_irq *irq, int size); +void mqnic_close_eq(struct mqnic_eq *eq); +int mqnic_eq_attach_cq(struct mqnic_eq *eq, struct mqnic_cq *cq); +void mqnic_eq_detach_cq(struct mqnic_eq *eq, struct mqnic_cq *cq); +void mqnic_eq_write_cons_ptr(struct mqnic_eq *eq); +void mqnic_arm_eq(struct mqnic_eq *eq); +void mqnic_process_eq(struct mqnic_eq *eq); + +// mqnic_cq.c +struct mqnic_cq *mqnic_create_cq(struct mqnic_if *interface); +void mqnic_destroy_cq(struct mqnic_cq *cq); +int mqnic_open_cq(struct mqnic_cq *cq, struct mqnic_eq *eq, int size); +void mqnic_close_cq(struct mqnic_cq *cq); +void mqnic_cq_write_cons_ptr(struct mqnic_cq *cq); +void mqnic_arm_cq(struct mqnic_cq *cq); + +// mqnic_tx.c +struct mqnic_ring *mqnic_create_tx_ring(struct mqnic_if *interface); +void mqnic_destroy_tx_ring(struct mqnic_ring *ring); +int mqnic_open_tx_ring(struct mqnic_ring *ring, struct mqnic_priv *priv, + struct mqnic_cq *cq, int size, int desc_block_size); +void mqnic_close_tx_ring(struct mqnic_ring *ring); +int mqnic_enable_tx_ring(struct mqnic_ring *ring); +void mqnic_disable_tx_ring(struct mqnic_ring *ring); +bool mqnic_is_tx_ring_empty(const struct mqnic_ring *ring); +bool mqnic_is_tx_ring_full(const struct mqnic_ring *ring); +void mqnic_tx_read_cons_ptr(struct mqnic_ring *ring); +void mqnic_tx_write_prod_ptr(struct mqnic_ring *ring); +void mqnic_free_tx_desc(struct mqnic_ring *ring, int index, int napi_budget); +int mqnic_free_tx_buf(struct mqnic_ring *ring); +int mqnic_process_tx_cq(struct mqnic_cq *cq, int napi_budget); +void mqnic_tx_irq(struct mqnic_cq *cq); +int mqnic_poll_tx_cq(struct napi_struct *napi, int budget); +netdev_tx_t mqnic_start_xmit(struct sk_buff *skb, struct net_device *dev); + +// mqnic_rx.c +struct mqnic_ring *mqnic_create_rx_ring(struct mqnic_if *interface); +void mqnic_destroy_rx_ring(struct mqnic_ring *ring); +int mqnic_open_rx_ring(struct mqnic_ring *ring, struct mqnic_priv *priv, + struct mqnic_cq *cq, int size, int desc_block_size); +void mqnic_close_rx_ring(struct mqnic_ring *ring); +int mqnic_enable_rx_ring(struct mqnic_ring *ring); +void mqnic_disable_rx_ring(struct mqnic_ring *ring); +bool mqnic_is_rx_ring_empty(const struct mqnic_ring *ring); +bool mqnic_is_rx_ring_full(const struct mqnic_ring *ring); +void mqnic_rx_read_cons_ptr(struct mqnic_ring *ring); +void mqnic_rx_write_prod_ptr(struct mqnic_ring *ring); +void mqnic_free_rx_desc(struct mqnic_ring *ring, int index); +int mqnic_free_rx_buf(struct mqnic_ring *ring); +int mqnic_prepare_rx_desc(struct mqnic_ring *ring, int index); +int mqnic_refill_rx_buffers(struct mqnic_ring *ring); +int mqnic_process_rx_cq(struct mqnic_cq *cq, int napi_budget); +void mqnic_rx_irq(struct mqnic_cq *cq); +int mqnic_poll_rx_cq(struct napi_struct *napi, int budget); + +// mqnic_ethtool.c +extern const struct ethtool_ops mqnic_ethtool_ops; + +#endif /* MQNIC_H */ diff --git a/drivers/net/mqnic/mqnic_board.c b/drivers/net/mqnic/mqnic_board.c new file mode 100644 index 00000000000000..a3ad165a575219 --- /dev/null +++ b/drivers/net/mqnic/mqnic_board.c @@ -0,0 +1,1155 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +#include +#include +#include + +static const struct property_entry i2c_mux_props[] = { + PROPERTY_ENTRY_BOOL("i2c-mux-idle-disconnect"), + {} +}; + +static const struct software_node i2c_mux_node = { + .properties = i2c_mux_props +}; + +static struct i2c_client *create_i2c_client(struct i2c_adapter *adapter, + const char *type, int addr) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + int err; + + if (!adapter) + return NULL; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, type, I2C_NAME_SIZE); + board_info.addr = addr; + board_info.swnode = &i2c_mux_node; + client = i2c_new_client_device(adapter, &board_info); + + if (!client) + return NULL; + + // force driver load (mainly for muxes so we can talk to downstream devices) + err = device_attach(&client->dev); + if (err < 0) + goto err_free_client; + + return client; + +err_free_client: + i2c_unregister_device(client); + return NULL; +} + +static struct i2c_adapter *get_i2c_mux_channel(struct i2c_client *mux, u32 chan_id) +{ + struct i2c_mux_core *muxc; + + if (!mux) + return NULL; + + muxc = i2c_get_clientdata(mux); + + if (!muxc || chan_id >= muxc->num_adapters) + return NULL; + + return muxc->adapter[chan_id]; +} + +static int init_mac_list_from_base_mac(struct mqnic_dev *mqnic, int count, char *mac) +{ + int k; + + count = min(count, MQNIC_MAX_IF); + + if (!is_valid_ether_addr(mac)) { + dev_warn(mqnic->dev, "Base MAC is not valid"); + return -1; + } + + mqnic->mac_count = count; + for (k = 0; k < mqnic->mac_count; k++) { + memcpy(mqnic->mac_list[k], mac, ETH_ALEN); + mqnic->mac_list[k][ETH_ALEN - 1] += k; + } + + return count; +} + +static int read_mac_from_eeprom(struct mqnic_dev *mqnic, + struct i2c_client *eeprom, int offset, char *mac) +{ + int ret; + + if (!eeprom) { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM; no EEPROM I2C client registered"); + return -1; + } + + ret = i2c_smbus_read_i2c_block_data(eeprom, offset, ETH_ALEN, mac); + if (ret < 0) { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM"); + return -1; + } + + return 0; +} + +static int read_mac_from_eeprom_hex(struct mqnic_dev *mqnic, + struct i2c_client *eeprom, int offset, char *mac) +{ + int ret; + char mac_hex[3 * ETH_ALEN]; + + if (!eeprom) { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM; no EEPROM I2C client registered"); + return -1; + } + + ret = i2c_smbus_read_i2c_block_data(eeprom, offset, 3 * ETH_ALEN - 1, mac_hex); + mac_hex[3 * ETH_ALEN - 1] = 0; + if (ret < 0 || !mac_pton(mac_hex, mac)) { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM"); + return -1; + } + + return 0; +} + +static int init_mac_list_from_eeprom(struct mqnic_dev *mqnic, + struct i2c_client *eeprom, int offset, int count) +{ + int ret, k; + char mac[ETH_ALEN]; + + count = min(count, MQNIC_MAX_IF); + + for (k = 0; k < count; k++) { + ret = read_mac_from_eeprom(mqnic, eeprom, offset + ETH_ALEN * k, mac); + if (ret < 0) + return ret; + + if (!is_valid_ether_addr(mac)) { + dev_warn(mqnic->dev, "MAC is not valid"); + return -1; + } + + memcpy(mqnic->mac_list[k], mac, ETH_ALEN); + mqnic->mac_count = k + 1; + } + + return 0; +} + +static int init_mac_list_from_eeprom_base(struct mqnic_dev *mqnic, + struct i2c_client *eeprom, int offset, int count) +{ + int ret; + char mac[ETH_ALEN]; + + ret = read_mac_from_eeprom(mqnic, eeprom, offset, mac); + if (ret < 0) + return ret; + + if (!is_valid_ether_addr(mac)) { + dev_warn(mqnic->dev, "EEPROM does not contain a valid base MAC"); + return -1; + } + + return init_mac_list_from_base_mac(mqnic, count, mac); +} + +static int init_mac_list_from_eeprom_base_hex(struct mqnic_dev *mqnic, + struct i2c_client *eeprom, int offset, int count) +{ + int ret; + char mac[ETH_ALEN]; + + ret = read_mac_from_eeprom_hex(mqnic, eeprom, offset, mac); + if (ret < 0) + return ret; + + if (!is_valid_ether_addr(mac)) { + dev_warn(mqnic->dev, "EEPROM does not contain a valid base MAC"); + return -1; + } + + return init_mac_list_from_base_mac(mqnic, count, mac); +} + +static int mqnic_generic_board_init(struct mqnic_dev *mqnic) +{ + struct i2c_adapter *adapter; + struct i2c_client *mux; + struct i2c_client *client; + int ret = 0; + + mqnic->mod_i2c_client_count = 0; + + if (mqnic_i2c_init(mqnic)) { + dev_err(mqnic->dev, "Failed to initialize I2C subsystem"); + return -1; + } + + switch (mqnic->board_id) { + case MQNIC_BOARD_ID_NETFPGA_SUME: + // FPGA IC12 + // TCA9548 IC31 0x74 + // CH0: SFP1 IC3 0x50 + // CH1: SFP2 IC5 0x50 + // CH2: SFP3 IC6 0x50 + // CH3: SFP4 IC8 0x50 + // CH4: DDR3 IC27 0x51 + // DDR3 IC28 0x52 + // SI5324 IC20 0x68 + // CH5: FMC + // CH6: PCON + // CH7: PMOD J11 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // IC31 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74); + + // IC3 SFP1 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // IC5 SFP2 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + // IC6 SFP3 + mqnic->mod_i2c_client[2] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50); + + // IC8 SFP4 + mqnic->mod_i2c_client[3] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 4; + + break; + case MQNIC_BOARD_ID_VCU108: + // FPGA U1 + // TCA9548 U28 0x74 + // CH0: SI570 Osc U32 0x5D + // CH1: TCA6416 Port Exp U89 0x21 + // CH2: QSFP U145 0x50 + // CH3: NC + // CH4: SI5328 U57 0x68 + // CH5: HDMI U52 0x39 + // CH6: SYSMON U1 0x32 + // CH7: NC + // PCA9544 U80 0x75 + // CH0: PMBUS + // CH1: FMC_HPC0 J22 + // CH2: FMC_HPC1 J2 + // CH3: M24C08 EEPROM U12 0x54 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U28 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74); + + // U145 QSFP + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50); + + // U80 PCA9544 I2C MUX + mux = create_i2c_client(adapter, "pca9544", 0x75); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54); + + mqnic->mod_i2c_client_count = 1; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0x20, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_VCU118: + // FPGA U1 + // TCA9548 U28 0x74 + // CH0: SI570 Osc U32 0x5D + // CH1: NC + // CH2: QSFP1 U145 0x50 + // CH3: QSFP2 U123 0x50 + // CH4: SI5328 U57 0x68 + // CH5: SI570 Osc U18 0x5D + // CH6: SYSMON U1 0x32 + // CH7: FIREFLY J6 0x50 + // TCA9548 U80 0x75 + // CH0: PMBUS + // CH1: FMCP_HSPC J22 + // CH2: FMC_HPC1 J2 + // CH3: M24C08 EEPROM U12 0x54 + // CH4: INA_PMBUS + // CH5: SI570 Osc U38 0x5D + // CH6: NC + // CH7: NC + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U28 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74); + + // U145 QSFP1 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50); + + // U123 QSFP2 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50); + + // U80 PCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x75); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0x20, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_VCU1525: + // FPGA U13 + // PCA9546 U28 0x74 + // CH0: QSFP0 J7 0x50 + // CH1: QSFP1 J9 0x50 + // CH2: M24C08 EEPROM U62 0x54 + // SI570 Osc U14 0x5D + // CH3: SYSMON U13 0x32 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U28 TCA9546 I2C MUX + mux = create_i2c_client(adapter, "pca9546", 0x74); + + // J7 QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // J9 QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c08", 0x54); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0x20, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_ZCU106: + // FPGA U1 / MSP430 U41 I2C0 + // TCA6416 U61 0x21 + // TCA6416 U97 0x20 + // PCA9544 U60 0x75 + // CH0: PS_PMBUS + // CH1: PL_PMBUS + // CH2: MAXIM_PMBUS + // CH3: SYSMON U1 0x32 + // FPGA U1 / MSP430 U41 I2C1 + // TCA9548 U34 0x74 + // CH0: M24C08 EEPROM U23 0x54 + // CH1: SI5341 U69 0x36 + // CH2: SI570 Osc U42 0x5D + // CH3: SI570 Osc U56 0x5D + // CH4: SI5328 U20 0x68 + // CH5: NC + // CH6: NC + // CH7: NC + // TCA9548 U135 0x75 + // CH0: FMC_HPC0 J5 + // CH1: FMC_HPC1 J4 + // CH2: SYSMON U1 0x32 + // CH3: DDR4 SODIMM 0x51 + // CH4: NC + // CH5: NC + // CH6: SFP1 P2 0x50 + // CH7: SFP0 P1 0x50 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U34 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74); + + // U23 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c08", 0x54); + + // U135 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x75); + + // P1 SFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 7), "24c02", 0x50); + + // P2 SFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 6), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0x20, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_DK_DEV_1SMX_H_A: + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // Virtual I2C MUX + mux = create_i2c_client(adapter, "pca9543", 0x74); + + // J4 QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // J5 QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + break; + case MQNIC_BOARD_ID_DK_DEV_1SDX_P_A: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // FPC202 default address 0x0f, module addresses 0x78 and 0x7c + // release reset and deassert lpmode + client = create_i2c_client(adapter, "24c02", 0x0f); + + if (client) { + i2c_smbus_write_i2c_block_data(client, 0x08, 1, "\x55"); + i2c_smbus_write_i2c_block_data(client, 0x0A, 1, "\x05"); + + i2c_unregister_device(client); + } + + // QSFP 1 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x78); + + // QSFP 2 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x7c); + + mqnic->mod_i2c_client_count = 2; + + // U94 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c128", 0x57); + + break; + case MQNIC_BOARD_ID_DK_DEV_AGF014EA: + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U23 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c64", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // Virtual I2C MUX + mux = create_i2c_client(adapter, "pca9543", 0x74); + + // QSFPDD0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // QSFPDD1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + break; + case MQNIC_BOARD_ID_DE10_AGILEX: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP-DD A + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP-DD B + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + break; + case MQNIC_BOARD_ID_250SOC: + // FPGA I2C + // TCA9548 U28 0x72 + // CH0: J6 (OCuLink ch 0) A + // CH1: J6 (OCuLink ch 0) B + // CH2: J7 (OCuLink ch 1) A + // CH3: J8 (OCuLink ch 2) A + // CH4: J9 (OCuLink ch 3) A + // CH5: J9 (OCuLink ch 3) B + // CH6: QSFP0 + // CH7: QSFP1 + // FPGA SMBUS + // AT24C16C U51 0x54 + // TMP431C U52 0x4C + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U34 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x70); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 6), "24c02", 0x50); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 7), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 2); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c16", 0x50); + + break; + case MQNIC_BOARD_ID_520NMX: + // FPGA I2C + // TCA9548 0x72 + // CH0: OC_2 J22 + // CH1: OC_3 J23 + // CH2: OC_0 J26 + // CH3: OC_1 J27 + // CH4: QSFP_0 + // CH5: QSFP_1 + // CH6: QSFP_2 + // CH7: QSFP_3 + // EEPROM 0x57 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x72); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 4), "24c02", 0x50); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 5), "24c02", 0x50); + + // QSFP2 + mqnic->mod_i2c_client[2] = create_i2c_client(get_i2c_mux_channel(mux, 6), "24c02", 0x50); + + // QSFP3 + mqnic->mod_i2c_client[3] = create_i2c_client(get_i2c_mux_channel(mux, 7), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 4; + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x57); + + // read MACs from EEPROM + init_mac_list_from_eeprom(mqnic, mqnic->eeprom_i2c_client, 0x4B, 16); + + break; + case MQNIC_BOARD_ID_XUSP3S: + case MQNIC_BOARD_ID_XUPP3R: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 2); + + // QSFP2 + mqnic->mod_i2c_client[2] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 3); + + // QSFP3 + mqnic->mod_i2c_client[3] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 4; + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 4); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c04", 0x50); + + // read MACs from EEPROM + init_mac_list_from_eeprom_base_hex(mqnic, mqnic->eeprom_i2c_client, 4, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_IA_420F: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP-DD + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 1; + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x57); + + // read MACs from EEPROM + init_mac_list_from_eeprom(mqnic, mqnic->eeprom_i2c_client, 0x56, 1); + + break; + case MQNIC_BOARD_ID_NEXUS_K35_S: + case MQNIC_BOARD_ID_NEXUS_K3P_S: + case MQNIC_BOARD_ID_ADM_PCIE_9V3: + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // Virtual I2C MUX + mux = create_i2c_client(adapter, "pca9543", 0x74); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + // create I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x50); + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_NEXUS_K3P_Q: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 2); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x50); + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_DNPCIE_40G_KU: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 2); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c256", 0x50); + + // read MACs from EEPROM + // init_mac_list_from_eeprom(mqnic, mqnic->eeprom_i2c_client, 0x000E, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_FB4CGG3_VU9P: + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 2); + + // QSFP2 + mqnic->mod_i2c_client[2] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 3); + + // QSFP3 + mqnic->mod_i2c_client[3] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 4; + + break; + default: + dev_warn(mqnic->dev, "Unknown board ID, not performing any board-specific init"); + } + + return ret; +} + +static void mqnic_generic_board_deinit(struct mqnic_dev *mqnic) +{ + int k; + + // unregister I2C clients + for (k = 0; k < ARRAY_SIZE(mqnic->mod_i2c_client); k++) { + if (mqnic->mod_i2c_client[k]) { + i2c_unregister_device(mqnic->mod_i2c_client[k]); + mqnic->mod_i2c_client[k] = NULL; + } + } + + if (mqnic->eeprom_i2c_client) { + i2c_unregister_device(mqnic->eeprom_i2c_client); + mqnic->eeprom_i2c_client = NULL; + } + + mqnic_i2c_deinit(mqnic); +} + +static struct mqnic_board_ops generic_board_ops = { + .init = mqnic_generic_board_init, + .deinit = mqnic_generic_board_deinit +}; + +static u32 mqnic_alveo_bmc_reg_read(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, u32 reg) +{ + iowrite32(reg, rb->regs + MQNIC_RB_ALVEO_BMC_REG_ADDR); + ioread32(rb->regs + MQNIC_RB_ALVEO_BMC_REG_DATA); // dummy read + return ioread32(rb->regs + MQNIC_RB_ALVEO_BMC_REG_DATA); +} + +static void mqnic_alveo_bmc_reg_write(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, u32 reg, u32 val) +{ + iowrite32(reg, rb->regs + MQNIC_RB_ALVEO_BMC_REG_ADDR); + iowrite32(val, rb->regs + MQNIC_RB_ALVEO_BMC_REG_DATA); + ioread32(rb->regs + MQNIC_RB_ALVEO_BMC_REG_DATA); // dummy read +} + +static int mqnic_alveo_bmc_read_mac(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, int index, char *mac) +{ + u32 reg = 0x0281a0 + index * 8; + u32 val; + + val = mqnic_alveo_bmc_reg_read(mqnic, rb, reg); + mac[0] = (val >> 8) & 0xff; + mac[1] = val & 0xff; + + val = mqnic_alveo_bmc_reg_read(mqnic, rb, reg + 4); + mac[2] = (val >> 24) & 0xff; + mac[3] = (val >> 16) & 0xff; + mac[4] = (val >> 8) & 0xff; + mac[5] = val & 0xff; + + return 0; +} + +static int mqnic_alveo_bmc_read_mac_list(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, int count) +{ + int ret, k; + char mac[ETH_ALEN]; + + count = min(count, MQNIC_MAX_IF); + + mqnic->mac_count = 0; + for (k = 0; k < count; k++) { + ret = mqnic_alveo_bmc_read_mac(mqnic, rb, k, mac); + if (ret) { + dev_warn(mqnic->dev, "Failed to read MAC from Alveo BMC"); + return -1; + } + + if (is_valid_ether_addr(mac)) { + memcpy(mqnic->mac_list[mqnic->mac_count], mac, ETH_ALEN); + mqnic->mac_count++; + } + } + + dev_info(mqnic->dev, "Read %d MACs from Alveo BMC", mqnic->mac_count); + + if (mqnic->mac_count == 0) + dev_warn(mqnic->dev, "Failed to read any valid MACs from Alveo BMC"); + + return mqnic->mac_count; +} + +static int mqnic_alveo_board_init(struct mqnic_dev *mqnic) +{ + struct i2c_adapter *adapter; + struct i2c_client *mux; + struct mqnic_reg_block *rb; + int ret = 0; + + mqnic->mod_i2c_client_count = 0; + + if (mqnic_i2c_init(mqnic)) { + dev_err(mqnic->dev, "Failed to initialize I2C subsystem"); + return -1; + } + + switch (mqnic->board_id) { + case MQNIC_BOARD_ID_AU200: + case MQNIC_BOARD_ID_AU250: + // FPGA U13 + // PCA9546 U28 0x74 + // CH0: QSFP0 J7 0x50 + // CH1: QSFP1 J9 0x50 + // CH2: M24C08 EEPROM U62 0x54 + // SI570 Osc U14 0x5D + // CH3: SYSMON U13 0x32 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // U28 TCA9546 I2C MUX + mux = create_i2c_client(adapter, "pca9546", 0x74); + + // J7 QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50); + + // J9 QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c08", 0x54); + + mqnic->mod_i2c_client_count = 2; + + break; + case MQNIC_BOARD_ID_AU45: + case MQNIC_BOARD_ID_AU50: + case MQNIC_BOARD_ID_AU55: + case MQNIC_BOARD_ID_AU280: + // no I2C interfaces + + break; + default: + dev_warn(mqnic->dev, "Unknown Alveo board ID"); + } + + // init BMC + rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_ALVEO_BMC_TYPE, MQNIC_RB_ALVEO_BMC_VER, 0); + + if (rb) { + if (mqnic_alveo_bmc_reg_read(mqnic, rb, 0x020000) == 0 || + mqnic_alveo_bmc_reg_read(mqnic, rb, 0x028000) != 0x74736574) { + dev_info(mqnic->dev, "Resetting Alveo CMS"); + + mqnic_alveo_bmc_reg_write(mqnic, rb, 0x020000, 0); + mqnic_alveo_bmc_reg_write(mqnic, rb, 0x020000, 1); + msleep(200); + } + + if (mqnic_alveo_bmc_reg_read(mqnic, rb, 0x028000) != 0x74736574) + dev_warn(mqnic->dev, "Alveo CMS not responding"); + else + mqnic_alveo_bmc_read_mac_list(mqnic, rb, 8); + } else { + dev_warn(mqnic->dev, "Alveo CMS not found"); + } + + return ret; +} + +static struct mqnic_board_ops alveo_board_ops = { + .init = mqnic_alveo_board_init, + .deinit = mqnic_generic_board_deinit +}; + +static int mqnic_gecko_bmc_read(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb) +{ + u32 val; + int timeout = 200; + + while (1) { + val = ioread32(rb->regs + MQNIC_RB_GECKO_BMC_REG_STATUS); + if (val & BIT(19)) { + if (val & BIT(18)) { + // timed out + dev_warn(mqnic->dev, "Timed out waiting for Gecko BMC response"); + msleep(20); + return -2; + } + return val & 0xffff; + } + + timeout--; + if (timeout == 0) { + dev_warn(mqnic->dev, "Timed out waiting for Gecko BMC interface"); + return -1; + } + usleep_range(1000, 100000); + } + + return -1; +} + +static int mqnic_gecko_bmc_write(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, u16 cmd, u32 data) +{ + int ret; + + ret = mqnic_gecko_bmc_read(mqnic, rb); + + if (ret == -1) + return ret; + + iowrite32(data, rb->regs + MQNIC_RB_GECKO_BMC_REG_DATA); + iowrite32(cmd << 16, rb->regs + MQNIC_RB_GECKO_BMC_REG_CMD); + + return 0; +} + +static int mqnic_gecko_bmc_query(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, u16 cmd, u32 data) +{ + int ret; + + ret = mqnic_gecko_bmc_write(mqnic, rb, cmd, data); + + if (ret) + return ret; + + return mqnic_gecko_bmc_read(mqnic, rb); +} + +static int mqnic_gecko_bmc_read_mac(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, int index, char *mac) +{ + int i; + u16 val; + + for (i = 0; i < ETH_ALEN; i += 2) { + val = mqnic_gecko_bmc_query(mqnic, rb, 0x2003, 0 + index * ETH_ALEN + i); + if (val < 0) + return val; + mac[i] = val & 0xff; + mac[i + 1] = (val >> 8) & 0xff; + } + + return 0; +} + +static int mqnic_gecko_bmc_read_mac_list(struct mqnic_dev *mqnic, struct mqnic_reg_block *rb, int count) +{ + int ret, k; + char mac[ETH_ALEN]; + + count = min(count, MQNIC_MAX_IF); + + mqnic->mac_count = 0; + for (k = 0; k < count; k++) { + ret = mqnic_gecko_bmc_read_mac(mqnic, rb, k, mac); + if (ret) { + dev_warn(mqnic->dev, "Failed to read MAC from Gecko BMC"); + return -1; + } + + if (is_valid_ether_addr(mac)) { + memcpy(mqnic->mac_list[mqnic->mac_count], mac, ETH_ALEN); + mqnic->mac_count++; + } + } + + dev_info(mqnic->dev, "Read %d MACs from Gecko BMC", mqnic->mac_count); + + if (mqnic->mac_count == 0) + dev_warn(mqnic->dev, "Failed to read any valid MACs from Gecko BMC"); + + return mqnic->mac_count; +} + +static int mqnic_gecko_board_init(struct mqnic_dev *mqnic) +{ + struct i2c_adapter *adapter; + struct mqnic_reg_block *rb; + int ret = 0; + + mqnic->mod_i2c_client_count = 0; + + if (mqnic_i2c_init(mqnic)) { + dev_err(mqnic->dev, "Failed to initialize I2C subsystem"); + return -1; + } + + switch (mqnic->board_id) { + case MQNIC_BOARD_ID_FB2CG_KU15P: + // FPGA U1 I2C0 + // QSFP0 J3 0x50 + // FPGA U1 I2C1 + // QSFP1 J4 0x50 + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, 1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50); + + mqnic->mod_i2c_client_count = 2; + + break; + default: + dev_warn(mqnic->dev, "Unknown board ID (Silicom Gecko BMC)"); + } + + // init BMC + rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_GECKO_BMC_TYPE, MQNIC_RB_GECKO_BMC_VER, 0); + + if (rb) { + if (mqnic_gecko_bmc_query(mqnic, rb, 0x7006, 0) <= 0) { + dev_warn(mqnic->dev, "Gecko BMC not responding"); + } else { + u16 v_l = mqnic_gecko_bmc_query(mqnic, rb, 0x7005, 0); + u16 v_h = mqnic_gecko_bmc_query(mqnic, rb, 0x7006, 0); + + dev_info(mqnic->dev, "Gecko BMC version %d.%d.%d.%d", + (v_h >> 8) & 0xff, v_h & 0xff, (v_l >> 8) & 0xff, v_l & 0xff); + + mqnic_gecko_bmc_read_mac_list(mqnic, rb, 8); + } + } else { + dev_warn(mqnic->dev, "Gecko BMC not found"); + } + + return ret; +} + +static struct mqnic_board_ops gecko_board_ops = { + .init = mqnic_gecko_board_init, + .deinit = mqnic_generic_board_deinit +}; + +int mqnic_board_init(struct mqnic_dev *mqnic) +{ + switch (mqnic->board_id) { + case MQNIC_BOARD_ID_AU45: + case MQNIC_BOARD_ID_AU50: + case MQNIC_BOARD_ID_AU55: + case MQNIC_BOARD_ID_AU200: + case MQNIC_BOARD_ID_AU250: + case MQNIC_BOARD_ID_AU280: + mqnic->board_ops = &alveo_board_ops; + break; + case MQNIC_BOARD_ID_FB2CG_KU15P: + mqnic->board_ops = &gecko_board_ops; + break; + default: + mqnic->board_ops = &generic_board_ops; + } + + if (!mqnic->board_ops) + return -1; + + return mqnic->board_ops->init(mqnic); +} + +void mqnic_board_deinit(struct mqnic_dev *mqnic) +{ + if (!mqnic->board_ops) + return; + + mqnic->board_ops->deinit(mqnic); + mqnic->board_ops = NULL; +} diff --git a/drivers/net/mqnic/mqnic_clk_info.c b/drivers/net/mqnic/mqnic_clk_info.c new file mode 100644 index 00000000000000..76620cff04587a --- /dev/null +++ b/drivers/net/mqnic/mqnic_clk_info.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2022-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +void mqnic_clk_info_init(struct mqnic_dev *mdev) +{ + u32 val; + + mdev->clk_info_rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_CLK_INFO_TYPE, MQNIC_RB_CLK_INFO_VER, 0); + + if (!mdev->clk_info_rb) + return; + + val = ioread32(mdev->clk_info_rb->regs + MQNIC_RB_CLK_INFO_REF_NOM_PER); + + mdev->ref_clk_nom_per_ns_num = val >> 16; + mdev->ref_clk_nom_per_ns_denom = val & 0xffff; + mdev->ref_clk_nom_freq_hz = div_s64((mdev->ref_clk_nom_per_ns_denom * 1000000000ull), mdev->ref_clk_nom_per_ns_num); + + val = ioread32(mdev->clk_info_rb->regs + MQNIC_RB_CLK_INFO_CLK_NOM_PER); + + mdev->core_clk_nom_per_ns_num = val >> 16; + mdev->core_clk_nom_per_ns_denom = val & 0xffff; + mdev->core_clk_nom_freq_hz = div_s64((mdev->core_clk_nom_per_ns_denom * 1000000000ull), mdev->core_clk_nom_per_ns_num); + + mdev->clk_info_channels = ioread32(mdev->clk_info_rb->regs + MQNIC_RB_CLK_INFO_COUNT); +} + +u32 mqnic_get_core_clk_nom_freq_hz(struct mqnic_dev *mdev) +{ + return mdev->core_clk_nom_freq_hz; +} +EXPORT_SYMBOL(mqnic_get_core_clk_nom_freq_hz); + +u32 mqnic_get_ref_clk_nom_freq_hz(struct mqnic_dev *mdev) +{ + return mdev->ref_clk_nom_freq_hz; +} +EXPORT_SYMBOL(mqnic_get_ref_clk_nom_freq_hz); + +u32 mqnic_get_core_clk_freq_hz(struct mqnic_dev *mdev) +{ + if (!mdev->clk_info_rb) + return 0; + + return ioread32(mdev->clk_info_rb->regs + MQNIC_RB_CLK_INFO_CLK_FREQ); +} +EXPORT_SYMBOL(mqnic_get_core_clk_freq_hz); + +u32 mqnic_get_clk_freq_hz(struct mqnic_dev *mdev, int ch) +{ + if (!mdev->clk_info_rb || ch < 0 || ch >= mdev->clk_info_channels) + return 0; + + return ioread32(mdev->clk_info_rb->regs + MQNIC_RB_CLK_INFO_FREQ_BASE + ch * 4); +} +EXPORT_SYMBOL(mqnic_get_clk_freq_hz); + +u64 mqnic_core_clk_cycles_to_ns(struct mqnic_dev *mdev, u64 cycles) +{ + if (!mdev->clk_info_rb || !mdev->core_clk_nom_per_ns_denom) + return 0; + + return (cycles * div_s64((u64)mdev->core_clk_nom_per_ns_num, (u64)mdev->core_clk_nom_per_ns_denom)); +} +EXPORT_SYMBOL(mqnic_core_clk_cycles_to_ns); + +u64 mqnic_core_clk_ns_to_cycles(struct mqnic_dev *mdev, u64 ns) +{ + if (!mdev->clk_info_rb || !mdev->core_clk_nom_per_ns_num) + return 0; + + return (ns * div_s64((u64)mdev->core_clk_nom_per_ns_denom, (u64)mdev->core_clk_nom_per_ns_num)); +} +EXPORT_SYMBOL(mqnic_core_clk_ns_to_cycles); + +u64 mqnic_ref_clk_cycles_to_ns(struct mqnic_dev *mdev, u64 cycles) +{ + if (!mdev->clk_info_rb || !mdev->ref_clk_nom_per_ns_denom) + return 0; + + return (cycles * div_s64((u64)mdev->ref_clk_nom_per_ns_num, (u64)mdev->ref_clk_nom_per_ns_denom)); +} +EXPORT_SYMBOL(mqnic_ref_clk_cycles_to_ns); + +u64 mqnic_ref_clk_ns_to_cycles(struct mqnic_dev *mdev, u64 ns) +{ + if (!mdev->clk_info_rb || !mdev->ref_clk_nom_per_ns_num) + return 0; + + return (ns * div_s64((u64)mdev->ref_clk_nom_per_ns_denom, (u64)mdev->ref_clk_nom_per_ns_num)); +} +EXPORT_SYMBOL(mqnic_ref_clk_ns_to_cycles); diff --git a/drivers/net/mqnic/mqnic_cq.c b/drivers/net/mqnic/mqnic_cq.c new file mode 100644 index 00000000000000..aadb3869031184 --- /dev/null +++ b/drivers/net/mqnic/mqnic_cq.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_cq *mqnic_create_cq(struct mqnic_if *interface) +{ + struct mqnic_cq *cq; + + cq = kzalloc(sizeof(*cq), GFP_KERNEL); + if (!cq) + return ERR_PTR(-ENOMEM); + + cq->dev = interface->dev; + cq->interface = interface; + + cq->cqn = -1; + cq->enabled = 0; + + cq->hw_addr = NULL; + + cq->cons_ptr = 0; + + return cq; +} + +void mqnic_destroy_cq(struct mqnic_cq *cq) +{ + mqnic_close_cq(cq); + + kfree(cq); +} + +int mqnic_open_cq(struct mqnic_cq *cq, struct mqnic_eq *eq, int size) +{ + int ret; + + if (cq->enabled || cq->hw_addr || cq->buf || !eq) + return -EINVAL; + + cq->cqn = mqnic_res_alloc(cq->interface->cq_res); + if (cq->cqn < 0) + return -ENOMEM; + + cq->size = roundup_pow_of_two(size); + cq->size_mask = cq->size - 1; + cq->stride = roundup_pow_of_two(MQNIC_CPL_SIZE); + + cq->buf_size = cq->size * cq->stride; + cq->buf = dma_alloc_coherent(cq->dev, cq->buf_size, &cq->buf_dma_addr, GFP_KERNEL); + if (!cq->buf) { + ret = -ENOMEM; + goto fail; + } + + cq->eq = eq; + mqnic_eq_attach_cq(eq, cq); + cq->hw_addr = mqnic_res_get_addr(cq->interface->cq_res, cq->cqn); + + cq->cons_ptr = 0; + + // clear all phase tag bits + memset(cq->buf, 0, cq->buf_size); + + // deactivate queue + iowrite32(MQNIC_CQ_CMD_SET_ENABLE | 0, cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + // set base address + iowrite32((cq->buf_dma_addr & 0xfffff000), + cq->hw_addr + MQNIC_CQ_BASE_ADDR_VF_REG + 0); + iowrite32(((u64)cq->buf_dma_addr) >> 32, + cq->hw_addr + MQNIC_CQ_BASE_ADDR_VF_REG + 4); + // set size + iowrite32(MQNIC_CQ_CMD_SET_SIZE | ilog2(cq->size), + cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + // set EQN + iowrite32(MQNIC_CQ_CMD_SET_EQN | cq->eq->eqn, + cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + // set pointers + iowrite32(MQNIC_CQ_CMD_SET_PROD_PTR | 0, + cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + iowrite32(MQNIC_CQ_CMD_SET_CONS_PTR | (cq->cons_ptr & MQNIC_CQ_PTR_MASK), + cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + // activate queue + iowrite32(MQNIC_CQ_CMD_SET_ENABLE | 1, cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + + cq->enabled = 1; + + return 0; + +fail: + mqnic_close_cq(cq); + return ret; +} + +void mqnic_close_cq(struct mqnic_cq *cq) +{ + if (cq->hw_addr) { + // deactivate queue + iowrite32(MQNIC_CQ_CMD_SET_ENABLE | 0, cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); + } + + if (cq->eq) { + mqnic_eq_detach_cq(cq->eq, cq); + cq->eq = NULL; + } + + cq->hw_addr = NULL; + + if (cq->buf) { + dma_free_coherent(cq->dev, cq->buf_size, cq->buf, cq->buf_dma_addr); + cq->buf = NULL; + cq->buf_dma_addr = 0; + } + + mqnic_res_free(cq->interface->cq_res, cq->cqn); + cq->cqn = -1; + + cq->enabled = 0; +} + +void mqnic_cq_write_cons_ptr(struct mqnic_cq *cq) +{ + iowrite32(MQNIC_CQ_CMD_SET_CONS_PTR | (cq->cons_ptr & MQNIC_CQ_PTR_MASK), + cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); +} + +void mqnic_arm_cq(struct mqnic_cq *cq) +{ + if (!cq->enabled) + return; + + iowrite32(MQNIC_CQ_CMD_SET_ARM | 1, cq->hw_addr + MQNIC_CQ_CTRL_STATUS_REG); +} diff --git a/drivers/net/mqnic/mqnic_dev.c b/drivers/net/mqnic/mqnic_dev.c new file mode 100644 index 00000000000000..ffcf91abf160f2 --- /dev/null +++ b/drivers/net/mqnic/mqnic_dev.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" +#include "mqnic_ioctl.h" + +#include + +static int mqnic_open(struct inode *inode, struct file *file) +{ + // struct miscdevice *miscdev = file->private_data; + // struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + + return 0; +} + +static int mqnic_release(struct inode *inode, struct file *file) +{ + // struct miscdevice *miscdev = file->private_data; + // struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + + return 0; +} + +static int mqnic_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct miscdevice *miscdev = file->private_data; + struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + int index; + u64 pgoff, req_len, req_start; + + index = vma->vm_pgoff >> (40 - PAGE_SHIFT); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + + switch (index) { + case 0: + if (req_start + req_len > mqnic->hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + case 1: + if (req_start + req_len > mqnic->app_hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->app_hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + case 2: + if (req_start + req_len > mqnic->ram_hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->ram_hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + default: + dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset 0x%lx", + __func__, vma->vm_pgoff); + return -EINVAL; + } + + return -EINVAL; +} + +static long mqnic_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct miscdevice *miscdev = file->private_data; + struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + size_t minsz; + + if (cmd == MQNIC_IOCTL_GET_API_VERSION) { + // Get API version + return MQNIC_IOCTL_API_VERSION; + } else if (cmd == MQNIC_IOCTL_GET_DEVICE_INFO) { + // Get device information + struct mqnic_ioctl_device_info info; + + minsz = offsetofend(struct mqnic_ioctl_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + info.fw_id = mqnic->fw_id; + info.fw_ver = mqnic->fw_ver; + info.board_id = mqnic->board_id; + info.board_ver = mqnic->board_ver; + info.build_date = mqnic->build_date; + info.git_hash = mqnic->git_hash; + info.rel_info = mqnic->rel_info; + info.num_regions = 3; + info.num_irqs = 0; + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + + } else if (cmd == MQNIC_IOCTL_GET_REGION_INFO) { + // Get region information + struct mqnic_ioctl_region_info info; + + minsz = offsetofend(struct mqnic_ioctl_region_info, name); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + info.type = MQNIC_REGION_TYPE_UNIMPLEMENTED; + info.next = 0; + info.child = 0; + info.size = 0; + info.offset = ((u64)info.index) << 40; + info.name[0] = 0; + + switch (info.index) { + case 0: + info.type = MQNIC_REGION_TYPE_NIC_CTRL; + info.next = 1; + info.child = 0; + info.size = mqnic->hw_regs_size; + info.offset = ((u64)info.index) << 40; + strscpy(info.name, "ctrl", sizeof(info.name)); + break; + case 1: + info.type = MQNIC_REGION_TYPE_APP_CTRL; + info.next = 2; + info.child = 0; + info.size = mqnic->app_hw_regs_size; + info.offset = ((u64)info.index) << 40; + strscpy(info.name, "app", sizeof(info.name)); + break; + case 2: + info.type = MQNIC_REGION_TYPE_RAM; + info.next = 3; + info.child = 0; + info.size = mqnic->ram_hw_regs_size; + info.offset = ((u64)info.index) << 40; + strscpy(info.name, "ram", sizeof(info.name)); + break; + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + } + + return -EINVAL; +} + +const struct file_operations mqnic_fops = { + .owner = THIS_MODULE, + .open = mqnic_open, + .release = mqnic_release, + .mmap = mqnic_mmap, + .unlocked_ioctl = mqnic_ioctl, +}; diff --git a/drivers/net/mqnic/mqnic_devlink.c b/drivers/net/mqnic/mqnic_devlink.c new file mode 100644 index 00000000000000..47de62f141c730 --- /dev/null +++ b/drivers/net/mqnic/mqnic_devlink.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2023 The Regents of the University of California + */ + +#include "mqnic.h" + +#include + +static int mqnic_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, struct netlink_ext_ack *extack) +{ + struct mqnic_dev *mdev = devlink_priv(devlink); + char str[32]; + int err; + + snprintf(str, sizeof(str), "%08x", mdev->fpga_id); + + err = devlink_info_version_fixed_put(req, "fpga.id", str); + if (err) + return err; + + snprintf(str, sizeof(str), "%08x", mdev->board_id); + + err = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, str); + if (err) + return err; + + snprintf(str, sizeof(str), "%d.%d.%d.%d", + mdev->board_ver >> 24, (mdev->board_ver >> 16) & 0xff, + (mdev->board_ver >> 8) & 0xff, mdev->board_ver & 0xff); + + err = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, str); + if (err) + return err; + + snprintf(str, sizeof(str), "%08x", mdev->fw_id); + + err = devlink_info_version_running_put(req, "fw.id", str); + if (err) + return err; + + snprintf(str, sizeof(str), "%d.%d.%d.%d", + mdev->fw_ver >> 24, (mdev->fw_ver >> 16) & 0xff, + (mdev->fw_ver >> 8) & 0xff, mdev->fw_ver & 0xff); + + err = devlink_info_version_running_put(req, "fw.version", str); + if (err) + return err; + err = devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW, str); + if (err) + return err; + + err = devlink_info_version_running_put(req, "fw.build_date", mdev->build_date_str); + if (err) + return err; + + snprintf(str, sizeof(str), "%08x", mdev->git_hash); + + err = devlink_info_version_running_put(req, "fw.git_hash", str); + if (err) + return err; + + snprintf(str, sizeof(str), "%08x", mdev->rel_info); + + err = devlink_info_version_running_put(req, "fw.rel_info", str); + if (err) + return err; + + if (mdev->app_id) { + snprintf(str, sizeof(str), "%08x", mdev->app_id); + + err = devlink_info_version_running_put(req, "fw.app.id", str); + if (err) + return err; + } + + return 0; +} + +static const struct devlink_ops mqnic_devlink_ops = { + .info_get = mqnic_devlink_info_get, +}; + +struct devlink *mqnic_devlink_alloc(struct device *dev) +{ + return devlink_alloc(&mqnic_devlink_ops, sizeof(struct mqnic_dev), dev); +} + +void mqnic_devlink_free(struct devlink *devlink) +{ + devlink_free(devlink); +} diff --git a/drivers/net/mqnic/mqnic_eq.c b/drivers/net/mqnic/mqnic_eq.c new file mode 100644 index 00000000000000..5879df28e19d05 --- /dev/null +++ b/drivers/net/mqnic/mqnic_eq.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +static int mqnic_eq_int(struct notifier_block *nb, unsigned long action, void *data) +{ + struct mqnic_eq *eq = container_of(nb, struct mqnic_eq, irq_nb); + + mqnic_process_eq(eq); + mqnic_arm_eq(eq); + + return NOTIFY_DONE; +} + +struct mqnic_eq *mqnic_create_eq(struct mqnic_if *interface) +{ + struct mqnic_eq *eq; + + eq = kzalloc(sizeof(*eq), GFP_KERNEL); + if (!eq) + return ERR_PTR(-ENOMEM); + + eq->dev = interface->dev; + eq->interface = interface; + + eq->eqn = -1; + eq->enabled = 0; + + eq->irq_nb.notifier_call = mqnic_eq_int; + + eq->hw_addr = NULL; + + eq->cons_ptr = 0; + + spin_lock_init(&eq->table_lock); + + INIT_RADIX_TREE(&eq->cq_table, GFP_KERNEL); + + return eq; +} + +void mqnic_destroy_eq(struct mqnic_eq *eq) +{ + mqnic_close_eq(eq); + + kfree(eq); +} + +int mqnic_open_eq(struct mqnic_eq *eq, struct mqnic_irq *irq, int size) +{ + int ret; + + if (eq->enabled || eq->hw_addr || eq->buf || !irq) + return -EINVAL; + + eq->eqn = mqnic_res_alloc(eq->interface->eq_res); + if (eq->eqn < 0) + return -ENOMEM; + + eq->size = roundup_pow_of_two(size); + eq->size_mask = eq->size - 1; + eq->stride = roundup_pow_of_two(MQNIC_EVENT_SIZE); + + eq->buf_size = eq->size * eq->stride; + eq->buf = dma_alloc_coherent(eq->dev, eq->buf_size, &eq->buf_dma_addr, GFP_KERNEL); + if (!eq->buf) { + ret = -ENOMEM; + goto fail; + } + + // register interrupt + ret = atomic_notifier_chain_register(&irq->nh, &eq->irq_nb); + if (ret) + goto fail; + + eq->irq = irq; + + eq->hw_addr = mqnic_res_get_addr(eq->interface->eq_res, eq->eqn); + + eq->cons_ptr = 0; + + // clear all phase tag bits + memset(eq->buf, 0, eq->buf_size); + + // deactivate queue + iowrite32(MQNIC_EQ_CMD_SET_ENABLE | 0, eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + // set base address + iowrite32((eq->buf_dma_addr & 0xfffff000), + eq->hw_addr + MQNIC_EQ_BASE_ADDR_VF_REG + 0); + iowrite32(((u64)eq->buf_dma_addr) >> 32, + eq->hw_addr + MQNIC_EQ_BASE_ADDR_VF_REG + 4); + // set size + iowrite32(MQNIC_EQ_CMD_SET_SIZE | ilog2(eq->size), + eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + // set IRQN + iowrite32(MQNIC_EQ_CMD_SET_IRQN | eq->irq->index, + eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + // set pointers + iowrite32(MQNIC_EQ_CMD_SET_PROD_PTR | 0, + eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + iowrite32(MQNIC_EQ_CMD_SET_CONS_PTR | (eq->cons_ptr & MQNIC_EQ_PTR_MASK), + eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + // activate queue + iowrite32(MQNIC_EQ_CMD_SET_ENABLE | 1, eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + + eq->enabled = 1; + + return 0; + +fail: + mqnic_close_eq(eq); + return ret; +} + +void mqnic_close_eq(struct mqnic_eq *eq) +{ + int ret; + + if (eq->hw_addr) { + // deactivate queue + iowrite32(MQNIC_EQ_CMD_SET_ENABLE | 0, eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); + } + + // unregister interrupt + if (eq->irq) + ret = atomic_notifier_chain_unregister(&eq->irq->nh, &eq->irq_nb); + + eq->irq = NULL; + + eq->hw_addr = NULL; + + if (eq->buf) { + dma_free_coherent(eq->dev, eq->buf_size, eq->buf, eq->buf_dma_addr); + eq->buf = NULL; + eq->buf_dma_addr = 0; + } + + mqnic_res_free(eq->interface->eq_res, eq->eqn); + eq->eqn = -1; + + eq->enabled = 0; +} + +int mqnic_eq_attach_cq(struct mqnic_eq *eq, struct mqnic_cq *cq) +{ + int ret; + + spin_lock_irq(&eq->table_lock); + ret = radix_tree_insert(&eq->cq_table, cq->cqn, cq); + spin_unlock_irq(&eq->table_lock); + return ret; +} + +void mqnic_eq_detach_cq(struct mqnic_eq *eq, struct mqnic_cq *cq) +{ + struct mqnic_cq *item; + + spin_lock_irq(&eq->table_lock); + item = radix_tree_delete(&eq->cq_table, cq->cqn); + spin_unlock_irq(&eq->table_lock); + + if (IS_ERR(item)) { + dev_err(eq->dev, "%s on IF %d EQ %d: radix_tree_delete failed: %ld", + __func__, eq->interface->index, eq->eqn, PTR_ERR(item)); + } else if (!item) { + dev_err(eq->dev, "%s on IF %d EQ %d: CQ %d not in table", + __func__, eq->interface->index, eq->eqn, cq->cqn); + } else if (item != cq) { + dev_err(eq->dev, "%s on IF %d EQ %d: entry mismatch when removing CQ %d", + __func__, eq->interface->index, eq->eqn, cq->cqn); + } +} + +void mqnic_eq_write_cons_ptr(struct mqnic_eq *eq) +{ + iowrite32(MQNIC_EQ_CMD_SET_CONS_PTR | (eq->cons_ptr & MQNIC_EQ_PTR_MASK), + eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); +} + +void mqnic_arm_eq(struct mqnic_eq *eq) +{ + if (!eq->enabled) + return; + + iowrite32(MQNIC_EQ_CMD_SET_ARM | 1, eq->hw_addr + MQNIC_EQ_CTRL_STATUS_REG); +} + +void mqnic_process_eq(struct mqnic_eq *eq) +{ + struct mqnic_if *interface = eq->interface; + struct mqnic_event *event; + struct mqnic_cq *cq; + u32 eq_index; + u32 eq_cons_ptr; + int done = 0; + + eq_cons_ptr = eq->cons_ptr; + eq_index = eq_cons_ptr & eq->size_mask; + + while (1) { + event = (struct mqnic_event *)(eq->buf + eq_index * eq->stride); + + if (!!(event->phase & cpu_to_le32(0x80000000)) == !!(eq_cons_ptr & eq->size)) + break; + + dma_rmb(); + + if (event->type == MQNIC_EVENT_TYPE_CPL) { + // completion event + rcu_read_lock(); + cq = radix_tree_lookup(&eq->cq_table, le16_to_cpu(event->source)); + rcu_read_unlock(); + + if (likely(cq)) { + if (likely(cq->handler)) + cq->handler(cq); + } else { + dev_err(eq->dev, "%s on IF %d EQ %d: unknown event source %d (index %d, type %d)", + __func__, interface->index, eq->eqn, le16_to_cpu(event->source), + eq_index, le16_to_cpu(event->type)); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_NONE, 16, 1, + event, MQNIC_EVENT_SIZE, true); + } + } else { + dev_err(eq->dev, "%s on IF %d EQ %d: unknown event type %d (index %d, source %d)", + __func__, interface->index, eq->eqn, le16_to_cpu(event->type), + eq_index, le16_to_cpu(event->source)); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_NONE, 16, 1, + event, MQNIC_EVENT_SIZE, true); + } + + done++; + + eq_cons_ptr++; + eq_index = eq_cons_ptr & eq->size_mask; + } + + // update EQ consumer pointer + eq->cons_ptr = eq_cons_ptr; + mqnic_eq_write_cons_ptr(eq); +} diff --git a/drivers/net/mqnic/mqnic_ethtool.c b/drivers/net/mqnic/mqnic_ethtool.c new file mode 100644 index 00000000000000..163e14e6caa88d --- /dev/null +++ b/drivers/net/mqnic/mqnic_ethtool.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +#include +#include + +#define SFF_MODULE_ID_SFP 0x03 +#define SFF_MODULE_ID_QSFP 0x0c +#define SFF_MODULE_ID_QSFP_PLUS 0x0d +#define SFF_MODULE_ID_QSFP28 0x11 + +static void mqnic_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *drvinfo) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_dev *mdev = priv->mdev; + + strscpy(drvinfo->driver, DRIVER_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, DRIVER_VERSION, sizeof(drvinfo->version)); + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d.%d", + mdev->fw_ver >> 24, (mdev->fw_ver >> 16) & 0xff, + (mdev->fw_ver >> 8) & 0xff, mdev->fw_ver & 0xff); + strscpy(drvinfo->bus_info, dev_name(mdev->dev), sizeof(drvinfo->bus_info)); + + drvinfo->regdump_len = priv->mdev->hw_regs_size; +} + +static int mqnic_get_regs_len(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + return priv->mdev->hw_regs_size; +} + +static void mqnic_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *p) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + u32 *out = p; + int k; + + for (k = 0; k < priv->mdev->hw_regs_size / 4; k++) + out[k] = ioread32(priv->mdev->hw_addr + k * 4); +} + +static void mqnic_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *ext_ack) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + param->rx_max_pending = MQNIC_MAX_RX_RING_SZ; + param->tx_max_pending = MQNIC_MAX_TX_RING_SZ; + + param->rx_pending = priv->rx_ring_size; + param->tx_pending = priv->tx_ring_size; + + kernel_param->cqe_size = MQNIC_CPL_SIZE; +} + +static int mqnic_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kernel_param, + struct netlink_ext_ack *ext_ack) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + u32 tx_ring_size, rx_ring_size; + int port_up = priv->port_up; + int ret = 0; + + if (param->rx_mini_pending || param->rx_jumbo_pending) + return -EINVAL; + + if (param->rx_pending < MQNIC_MIN_RX_RING_SZ) + return -EINVAL; + + if (param->rx_pending > MQNIC_MAX_RX_RING_SZ) + return -EINVAL; + + if (param->tx_pending < MQNIC_MIN_TX_RING_SZ) + return -EINVAL; + + if (param->tx_pending > MQNIC_MAX_TX_RING_SZ) + return -EINVAL; + + rx_ring_size = roundup_pow_of_two(param->rx_pending); + tx_ring_size = roundup_pow_of_two(param->tx_pending); + + if (rx_ring_size == priv->rx_ring_size && tx_ring_size == priv->tx_ring_size) + return 0; + + netdev_info(ndev, "New TX ring size: %d", tx_ring_size); + netdev_info(ndev, "New RX ring size: %d", rx_ring_size); + + mutex_lock(&priv->mdev->state_lock); + + if (port_up) + mqnic_stop_port(ndev); + + priv->tx_ring_size = tx_ring_size; + priv->rx_ring_size = rx_ring_size; + + if (port_up) { + ret = mqnic_start_port(ndev); + + if (ret) + netdev_err(ndev, "%s: Failed to start port: %d", __func__, ret); + } + + mutex_unlock(&priv->mdev->state_lock); + + return ret; +} + +static void mqnic_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *param) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + u32 val; + + param->autoneg = 0; + param->rx_pause = 0; + param->tx_pause = 0; + + if (!(priv->if_features & MQNIC_IF_FEATURE_LFC)) + return; + + val = mqnic_port_get_lfc_ctrl(priv->port); + + param->rx_pause = !!(val & MQNIC_PORT_LFC_CTRL_RX_LFC_EN); + param->tx_pause = !!(val & MQNIC_PORT_LFC_CTRL_TX_LFC_EN); +} + +static int mqnic_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *param) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + u32 val; + + if (!(priv->if_features & MQNIC_IF_FEATURE_LFC)) + return -EOPNOTSUPP; + + if (param->autoneg) + return -EINVAL; + + val = mqnic_port_get_lfc_ctrl(priv->port); + + if (param->rx_pause) + val |= MQNIC_PORT_LFC_CTRL_RX_LFC_EN; + else + val &= ~MQNIC_PORT_LFC_CTRL_RX_LFC_EN; + + if (param->tx_pause) + val |= MQNIC_PORT_LFC_CTRL_TX_LFC_EN; + else + val &= ~MQNIC_PORT_LFC_CTRL_TX_LFC_EN; + + mqnic_port_set_lfc_ctrl(priv->port, val); + + return 0; +} + +static int mqnic_get_rxnfc(struct net_device *ndev, + struct ethtool_rxnfc *rxnfc, u32 *rule_locs) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + switch (rxnfc->cmd) { + case ETHTOOL_GRXRINGS: + rxnfc->data = priv->rxq_count; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static u32 mqnic_get_rxfh_indir_size(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + return priv->rx_queue_map_indir_table_size; +} + +static int mqnic_get_rxfh(struct net_device *ndev, struct ethtool_rxfh_param *param) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + int k; + + param->hfunc = ETH_RSS_HASH_TOP; + + if (param->indir) + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) + param->indir[k] = priv->rx_queue_map_indir_table[k]; + + return 0; +} + +static int mqnic_set_rxfh(struct net_device *ndev, struct ethtool_rxfh_param *param, + struct netlink_ext_ack *extack) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + int k; + + if (param->hfunc != ETH_RSS_HASH_NO_CHANGE && param->hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + + if (!param->indir) + return 0; + + if (param->indir) { + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) { + if (param->indir[k] >= priv->rxq_count) + return -EINVAL; + } + + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) + priv->rx_queue_map_indir_table[k] = param->indir[k]; + } + + return mqnic_update_indir_table(ndev); +} + +static void mqnic_get_channels(struct net_device *ndev, + struct ethtool_channels *channel) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + channel->max_rx = mqnic_res_get_count(priv->interface->rxq_res); + channel->max_tx = mqnic_res_get_count(priv->interface->txq_res); + + channel->rx_count = priv->rxq_count; + channel->tx_count = priv->txq_count; +} + +static int mqnic_set_channels(struct net_device *ndev, + struct ethtool_channels *channel) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + u32 txq_count, rxq_count; + int port_up = priv->port_up; + int ret = 0; + int k; + + if (channel->combined_count || channel->other_count) + return -EINVAL; + + if (priv->rxq_count > mqnic_res_get_count(priv->interface->rxq_res)) + return -EINVAL; + + if (priv->txq_count > mqnic_res_get_count(priv->interface->txq_res)) + return -EINVAL; + + rxq_count = channel->rx_count; + txq_count = channel->tx_count; + + if (rxq_count == priv->rxq_count && txq_count == priv->txq_count) + return 0; + + netdev_info(ndev, "New TX channel count: %d", txq_count); + netdev_info(ndev, "New RX channel count: %d", rxq_count); + + if (rxq_count != priv->rxq_count) + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) + priv->rx_queue_map_indir_table[k] = k % rxq_count; + + mutex_lock(&priv->mdev->state_lock); + + if (port_up) + mqnic_stop_port(ndev); + + priv->txq_count = txq_count; + priv->rxq_count = rxq_count; + + if (port_up) { + ret = mqnic_start_port(ndev); + + if (ret) + netdev_err(ndev, "%s: Failed to start port: %d", __func__, ret); + } + + mutex_unlock(&priv->mdev->state_lock); + + return ret; +} + +static int mqnic_get_ts_info(struct net_device *ndev, + struct kernel_ethtool_ts_info *info) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_dev *mdev = priv->mdev; + + ethtool_op_get_ts_info(ndev, info); + + if (mdev->ptp_clock) + info->phc_index = ptp_clock_index(mdev->ptp_clock); + + if (!(priv->if_features & MQNIC_IF_FEATURE_PTP_TS) || !mdev->ptp_clock) + return 0; + + info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; + + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + +static int mqnic_read_module_eeprom(struct net_device *ndev, + u16 offset, u16 len, u8 *data) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + if (!priv->mod_i2c_client) + return -EINVAL; + + if (len > I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX; + + return i2c_smbus_read_i2c_block_data(priv->mod_i2c_client, offset, len, data); +} + +static int mqnic_write_module_eeprom(struct net_device *ndev, + u16 offset, u16 len, u8 *data) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + if (!priv->mod_i2c_client) + return -EINVAL; + + if (len > I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX; + + return i2c_smbus_write_i2c_block_data(priv->mod_i2c_client, offset, len, data); +} + +static int mqnic_query_module_id(struct net_device *ndev) +{ + int ret; + u8 data; + + ret = mqnic_read_module_eeprom(ndev, 0, 1, &data); + + if (ret < 0) + return ret; + + return data; +} + +static int mqnic_query_module_eeprom_by_page(struct net_device *ndev, + unsigned short i2c_addr, u16 page, + u16 bank, u16 offset, + u16 len, u8 *data) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + int module_id; + u8 d; + int ret; + unsigned short orig_i2c_addr; + + module_id = mqnic_query_module_id(ndev); + + if (module_id < 0) { + netdev_err(ndev, "%s: Failed to read module ID (%d)", __func__, module_id); + return module_id; + } + + switch (module_id) { + case SFF_MODULE_ID_SFP: + if (page > 0 || bank > 0) + return -EINVAL; + if (i2c_addr != 0x50 && i2c_addr != 0x51) + return -EINVAL; + break; + case SFF_MODULE_ID_QSFP: + case SFF_MODULE_ID_QSFP_PLUS: + case SFF_MODULE_ID_QSFP28: + if (page > 3 || bank > 0) + return -EINVAL; + if (i2c_addr != 0x50) + return -EINVAL; + break; + default: + netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id); + return -EINVAL; + } + + // set page + switch (module_id) { + case SFF_MODULE_ID_SFP: + break; + case SFF_MODULE_ID_QSFP: + case SFF_MODULE_ID_QSFP_PLUS: + case SFF_MODULE_ID_QSFP28: + if (offset + len >= 128) { + // select page + d = page; + mqnic_write_module_eeprom(ndev, 127, 1, &d); + usleep_range(1000, 2000); + } + break; + default: + netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id); + return -EINVAL; + } + + // set i2c address of the mod_i2c_client + // This code section should not be called concurrently since the IOCTL + // handler for ethtool operations is called under the RTNL lock. + // In addition the i2c_client is used only within code inside this lock. + if (!priv->mod_i2c_client) + return -EINVAL; + + orig_i2c_addr = priv->mod_i2c_client->addr; + priv->mod_i2c_client->addr = i2c_addr; + + // read data + ret = mqnic_read_module_eeprom(ndev, offset, len, data); + + // reset i2c addr + priv->mod_i2c_client->addr = orig_i2c_addr; + + return ret; +} + +static int mqnic_query_module_eeprom(struct net_device *ndev, + u16 offset, u16 len, u8 *data) +{ + int module_id; + unsigned short i2c_addr = 0x50; + u16 page = 0; + u16 bank = 0; + + module_id = mqnic_query_module_id(ndev); + + if (module_id < 0) { + netdev_err(ndev, "%s: Failed to read module ID (%d)", __func__, module_id); + return module_id; + } + + switch (module_id) { + case SFF_MODULE_ID_SFP: + i2c_addr = 0x50; + page = 0; + if (offset >= 256) { + offset -= 256; + i2c_addr = 0x51; + } + break; + case SFF_MODULE_ID_QSFP: + case SFF_MODULE_ID_QSFP_PLUS: + case SFF_MODULE_ID_QSFP28: + i2c_addr = 0x50; + if (offset < 256) { + page = 0; + } else { + page = 1 + ((offset - 256) / 128); + offset -= page * 128; + } + break; + default: + netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id); + return -EINVAL; + } + + // clip request to end of page + if (offset + len > 256) + len = 256 - offset; + + return mqnic_query_module_eeprom_by_page(ndev, i2c_addr, + page, bank, offset, len, data); +} + +static int mqnic_get_module_info(struct net_device *ndev, + struct ethtool_modinfo *modinfo) +{ + int read_len = 0; + u8 data[16]; + + // read module ID and revision + read_len = mqnic_read_module_eeprom(ndev, 0, 2, data); + + if (read_len < 0) + return read_len; + + if (read_len < 2) + return -EIO; + + // check identifier byte at address 0 + switch (data[0]) { + case SFF_MODULE_ID_SFP: + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + break; + case SFF_MODULE_ID_QSFP: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + case SFF_MODULE_ID_QSFP_PLUS: + // check revision at address 1 + if (data[1] >= 0x03) { + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + } + break; + case SFF_MODULE_ID_QSFP28: + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; + break; + default: + netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, data[0]); + return -EINVAL; + } + + return 0; +} + +static int mqnic_get_module_eeprom(struct net_device *ndev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + int i = 0; + int read_len; + + if (eeprom->len == 0) + return -EINVAL; + + memset(data, 0, eeprom->len); + + while (i < eeprom->len) { + read_len = mqnic_query_module_eeprom(ndev, eeprom->offset + i, + eeprom->len - i, data + i); + + if (read_len == 0) + return 0; + + if (read_len < 0) { + netdev_err(ndev, "%s: Failed to read module EEPROM (%d)", __func__, read_len); + return read_len; + } + + i += read_len; + } + + return 0; +} + +static int mqnic_get_module_eeprom_by_page(struct net_device *ndev, + const struct ethtool_module_eeprom *eeprom, + struct netlink_ext_ack *extack) +{ + int i = 0; + int read_len; + + if (eeprom->length == 0) + return -EINVAL; + + memset(eeprom->data, 0, eeprom->length); + + while (i < eeprom->length) { + read_len = mqnic_query_module_eeprom_by_page(ndev, eeprom->i2c_address, + eeprom->page, eeprom->bank, + eeprom->offset + i, + eeprom->length - i, + eeprom->data + i); + + if (read_len == 0) + return 0; + + if (read_len < 0) { + netdev_err(ndev, "%s: Failed to read module EEPROM (%d)", __func__, read_len); + return read_len; + } + + i += read_len; + } + + return i; +} + +const struct ethtool_ops mqnic_ethtool_ops = { + .get_drvinfo = mqnic_get_drvinfo, + .get_regs_len = mqnic_get_regs_len, + .get_regs = mqnic_get_regs, + .get_link = ethtool_op_get_link, + .get_ringparam = mqnic_get_ringparam, + .set_ringparam = mqnic_set_ringparam, + .get_pauseparam = mqnic_get_pauseparam, + .set_pauseparam = mqnic_set_pauseparam, + .get_rxnfc = mqnic_get_rxnfc, + .get_rxfh_indir_size = mqnic_get_rxfh_indir_size, + .get_rxfh = mqnic_get_rxfh, + .set_rxfh = mqnic_set_rxfh, + .get_channels = mqnic_get_channels, + .set_channels = mqnic_set_channels, + .get_ts_info = mqnic_get_ts_info, + .get_module_info = mqnic_get_module_info, + .get_module_eeprom = mqnic_get_module_eeprom, + .get_module_eeprom_by_page = mqnic_get_module_eeprom_by_page, +}; diff --git a/drivers/net/mqnic/mqnic_hw.h b/drivers/net/mqnic/mqnic_hw.h new file mode 100644 index 00000000000000..40ec5c7c603116 --- /dev/null +++ b/drivers/net/mqnic/mqnic_hw.h @@ -0,0 +1,448 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Views + * + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#ifndef MQNIC_HW_H +#define MQNIC_HW_H + +#include +#include + +#define MQNIC_MAX_IRQ 256 + +#define MQNIC_MAX_IF 8 +#define MQNIC_MAX_PORTS 8 +#define MQNIC_MAX_SCHED 8 + +#define MQNIC_MAX_FRAGS 8 + +#define MQNIC_MAX_EQ 256 +#define MQNIC_MAX_TXQ 8192 +#define MQNIC_MAX_RXQ MQNIC_MAX_TXQ +#define MQNIC_MAX_CQ (MQNIC_MAX_TXQ * 2) + +#define MQNIC_MIN_TX_RING_SZ (4096 / 16) +#define MQNIC_MAX_TX_RING_SZ 32768 +#define MQNIC_MIN_RX_RING_SZ (4096 / 16) +#define MQNIC_MAX_RX_RING_SZ 32768 + +#define MQNIC_MAX_I2C_ADAPTERS 4 + +#define MQNIC_BOARD_ID_NETFPGA_SUME 0x10ee7028 +#define MQNIC_BOARD_ID_AU45 0x10ee902d +#define MQNIC_BOARD_ID_AU50 0x10ee9032 +#define MQNIC_BOARD_ID_AU55 0x10ee9037 +#define MQNIC_BOARD_ID_AU200 0x10ee90c8 +#define MQNIC_BOARD_ID_AU250 0x10ee90fa +#define MQNIC_BOARD_ID_AU280 0x10ee9118 +#define MQNIC_BOARD_ID_VCU108 0x10ee806c +#define MQNIC_BOARD_ID_VCU118 0x10ee9076 +#define MQNIC_BOARD_ID_VCU1525 0x10ee95f5 +#define MQNIC_BOARD_ID_ZCU106 0x10ee906a +#define MQNIC_BOARD_ID_DK_DEV_1SMX_H_A 0x11720001 +#define MQNIC_BOARD_ID_DK_DEV_1SDX_P_A 0x1172a00d +#define MQNIC_BOARD_ID_DK_DEV_AGF014EA 0x1172b00e +#define MQNIC_BOARD_ID_DE10_AGILEX 0x1172b00a +#define MQNIC_BOARD_ID_XUSP3S 0x12ba8823 +#define MQNIC_BOARD_ID_XUPP3R 0x12ba9823 +#define MQNIC_BOARD_ID_IA_420F 0x12bab5d4 +#define MQNIC_BOARD_ID_520NMX 0x198a0521 +#define MQNIC_BOARD_ID_250SOC 0x198a250e +#define MQNIC_BOARD_ID_FB4CGG3_VU9P 0x1c2c9403 +#define MQNIC_BOARD_ID_FB2CG_KU15P 0x1c2ca00e +#define MQNIC_BOARD_ID_NEXUS_K35_S 0x1ce40003 +#define MQNIC_BOARD_ID_NEXUS_K3P_S 0x1ce40009 +#define MQNIC_BOARD_ID_NEXUS_K3P_Q 0x1ce4000a +#define MQNIC_BOARD_ID_DNPCIE_40G_KU 0x17df1a00 +#define MQNIC_BOARD_ID_ADM_PCIE_9V3 0x41449003 + +// Register blocks +#define MQNIC_RB_REG_TYPE 0x00 +#define MQNIC_RB_REG_VER 0x04 +#define MQNIC_RB_REG_NEXT_PTR 0x08 + +#define MQNIC_RB_FW_ID_TYPE 0xFFFFFFFF +#define MQNIC_RB_FW_ID_VER 0x00000100 +#define MQNIC_RB_FW_ID_REG_FPGA_ID 0x0C +#define MQNIC_RB_FW_ID_REG_FW_ID 0x10 +#define MQNIC_RB_FW_ID_REG_FW_VER 0x14 +#define MQNIC_RB_FW_ID_REG_BOARD_ID 0x18 +#define MQNIC_RB_FW_ID_REG_BOARD_VER 0x1C +#define MQNIC_RB_FW_ID_REG_BUILD_DATE 0x20 +#define MQNIC_RB_FW_ID_REG_GIT_HASH 0x24 +#define MQNIC_RB_FW_ID_REG_REL_INFO 0x28 + +#define MQNIC_RB_GPIO_TYPE 0x0000C100 +#define MQNIC_RB_GPIO_VER 0x00000100 +#define MQNIC_RB_GPIO_REG_GPIO_IN 0x0C +#define MQNIC_RB_GPIO_REG_GPIO_OUT 0x10 + +#define MQNIC_RB_I2C_TYPE 0x0000C110 +#define MQNIC_RB_I2C_VER 0x00000100 +#define MQNIC_RB_I2C_REG_CTRL 0x0C + +#define MQNIC_REG_GPIO_I2C_SCL_IN 0x00000001 +#define MQNIC_REG_GPIO_I2C_SCL_OUT 0x00000002 +#define MQNIC_REG_GPIO_I2C_SDA_IN 0x00000100 +#define MQNIC_REG_GPIO_I2C_SDA_OUT 0x00000200 + +#define MQNIC_RB_SPI_FLASH_TYPE 0x0000C120 +#define MQNIC_RB_SPI_FLASH_VER 0x00000200 +#define MQNIC_RB_SPI_FLASH_REG_FORMAT 0x0C +#define MQNIC_RB_SPI_FLASH_REG_CTRL_0 0x10 +#define MQNIC_RB_SPI_FLASH_REG_CTRL_1 0x14 + +#define MQNIC_RB_BPI_FLASH_TYPE 0x0000C121 +#define MQNIC_RB_BPI_FLASH_VER 0x00000200 +#define MQNIC_RB_BPI_FLASH_REG_FORMAT 0x0C +#define MQNIC_RB_BPI_FLASH_REG_ADDR 0x10 +#define MQNIC_RB_BPI_FLASH_REG_DATA 0x14 +#define MQNIC_RB_BPI_FLASH_REG_CTRL 0x18 + +#define MQNIC_RB_ALVEO_BMC_TYPE 0x0000C140 +#define MQNIC_RB_ALVEO_BMC_VER 0x00000100 +#define MQNIC_RB_ALVEO_BMC_REG_ADDR 0x0C +#define MQNIC_RB_ALVEO_BMC_REG_DATA 0x10 + +#define MQNIC_RB_GECKO_BMC_TYPE 0x0000C141 +#define MQNIC_RB_GECKO_BMC_VER 0x00000100 +#define MQNIC_RB_GECKO_BMC_REG_STATUS 0x0C +#define MQNIC_RB_GECKO_BMC_REG_DATA 0x10 +#define MQNIC_RB_GECKO_BMC_REG_CMD 0x14 + +#define MQNIC_RB_STATS_TYPE 0x0000C006 +#define MQNIC_RB_STATS_VER 0x00000100 +#define MQNIC_RB_STATS_REG_OFFSET 0x0C +#define MQNIC_RB_STATS_REG_COUNT 0x10 +#define MQNIC_RB_STATS_REG_STRIDE 0x14 +#define MQNIC_RB_STATS_REG_FLAGS 0x18 + +#define MQNIC_RB_IRQ_TYPE 0x0000C007 +#define MQNIC_RB_IRQ_VER 0x00000100 + +#define MQNIC_RB_CLK_INFO_TYPE 0x0000C008 +#define MQNIC_RB_CLK_INFO_VER 0x00000100 +#define MQNIC_RB_CLK_INFO_COUNT 0x0C +#define MQNIC_RB_CLK_INFO_REF_NOM_PER 0x10 +#define MQNIC_RB_CLK_INFO_CLK_NOM_PER 0x18 +#define MQNIC_RB_CLK_INFO_CLK_FREQ 0x1C +#define MQNIC_RB_CLK_INFO_FREQ_BASE 0x20 + +#define MQNIC_RB_PHC_TYPE 0x0000C080 +#define MQNIC_RB_PHC_VER 0x00000200 +#define MQNIC_RB_PHC_REG_CTRL 0x0C +#define MQNIC_RB_PHC_REG_CUR_FNS 0x10 +#define MQNIC_RB_PHC_REG_CUR_TOD_NS 0x14 +#define MQNIC_RB_PHC_REG_CUR_TOD_SEC_L 0x18 +#define MQNIC_RB_PHC_REG_CUR_TOD_SEC_H 0x1C +#define MQNIC_RB_PHC_REG_CUR_REL_NS_L 0x20 +#define MQNIC_RB_PHC_REG_CUR_REL_NS_H 0x24 +#define MQNIC_RB_PHC_REG_CUR_PTM_NS_L 0x28 +#define MQNIC_RB_PHC_REG_CUR_PTM_NS_H 0x2C +#define MQNIC_RB_PHC_REG_SNAP_FNS 0x30 +#define MQNIC_RB_PHC_REG_SNAP_TOD_NS 0x34 +#define MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L 0x38 +#define MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H 0x3C +#define MQNIC_RB_PHC_REG_SNAP_REL_NS_L 0x40 +#define MQNIC_RB_PHC_REG_SNAP_REL_NS_H 0x44 +#define MQNIC_RB_PHC_REG_SNAP_PTM_NS_L 0x48 +#define MQNIC_RB_PHC_REG_SNAP_PTM_NS_H 0x4C +#define MQNIC_RB_PHC_REG_OFFSET_TOD_NS 0x50 +#define MQNIC_RB_PHC_REG_SET_TOD_NS 0x54 +#define MQNIC_RB_PHC_REG_SET_TOD_SEC_L 0x58 +#define MQNIC_RB_PHC_REG_SET_TOD_SEC_H 0x5C +#define MQNIC_RB_PHC_REG_SET_REL_NS_L 0x60 +#define MQNIC_RB_PHC_REG_SET_REL_NS_H 0x64 +#define MQNIC_RB_PHC_REG_OFFSET_REL_NS 0x68 +#define MQNIC_RB_PHC_REG_OFFSET_FNS 0x6C +#define MQNIC_RB_PHC_REG_NOM_PERIOD_FNS 0x70 +#define MQNIC_RB_PHC_REG_NOM_PERIOD_NS 0x74 +#define MQNIC_RB_PHC_REG_PERIOD_FNS 0x78 +#define MQNIC_RB_PHC_REG_PERIOD_NS 0x7C + +#define MQNIC_RB_PHC_PEROUT_TYPE 0x0000C081 +#define MQNIC_RB_PHC_PEROUT_VER 0x00000100 +#define MQNIC_RB_PHC_PEROUT_REG_CTRL 0x0C +#define MQNIC_RB_PHC_PEROUT_REG_START_FNS 0x10 +#define MQNIC_RB_PHC_PEROUT_REG_START_NS 0x14 +#define MQNIC_RB_PHC_PEROUT_REG_START_SEC_L 0x18 +#define MQNIC_RB_PHC_PEROUT_REG_START_SEC_H 0x1C +#define MQNIC_RB_PHC_PEROUT_REG_PERIOD_FNS 0x20 +#define MQNIC_RB_PHC_PEROUT_REG_PERIOD_NS 0x24 +#define MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_L 0x28 +#define MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_H 0x2C +#define MQNIC_RB_PHC_PEROUT_REG_WIDTH_FNS 0x30 +#define MQNIC_RB_PHC_PEROUT_REG_WIDTH_NS 0x34 +#define MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_L 0x38 +#define MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_H 0x3C + +#define MQNIC_RB_IF_TYPE 0x0000C000 +#define MQNIC_RB_IF_VER 0x00000100 +#define MQNIC_RB_IF_REG_OFFSET 0x0C +#define MQNIC_RB_IF_REG_COUNT 0x10 +#define MQNIC_RB_IF_REG_STRIDE 0x14 +#define MQNIC_RB_IF_REG_CSR_OFFSET 0x18 + +#define MQNIC_RB_IF_CTRL_TYPE 0x0000C001 +#define MQNIC_RB_IF_CTRL_VER 0x00000400 +#define MQNIC_RB_IF_CTRL_REG_FEATURES 0x0C +#define MQNIC_RB_IF_CTRL_REG_PORT_COUNT 0x10 +#define MQNIC_RB_IF_CTRL_REG_SCHED_COUNT 0x14 +#define MQNIC_RB_IF_CTRL_REG_MAX_TX_MTU 0x20 +#define MQNIC_RB_IF_CTRL_REG_MAX_RX_MTU 0x24 +#define MQNIC_RB_IF_CTRL_REG_TX_MTU 0x28 +#define MQNIC_RB_IF_CTRL_REG_RX_MTU 0x2C +#define MQNIC_RB_IF_CTRL_REG_TX_FIFO_DEPTH 0x30 +#define MQNIC_RB_IF_CTRL_REG_RX_FIFO_DEPTH 0x34 + +#define MQNIC_IF_FEATURE_RSS BIT(0) +#define MQNIC_IF_FEATURE_PTP_TS BIT(4) +#define MQNIC_IF_FEATURE_TX_CSUM BIT(8) +#define MQNIC_IF_FEATURE_RX_CSUM BIT(9) +#define MQNIC_IF_FEATURE_RX_HASH BIT(10) +#define MQNIC_IF_FEATURE_LFC BIT(11) +#define MQNIC_IF_FEATURE_PFC BIT(12) + +#define MQNIC_RB_RX_QUEUE_MAP_TYPE 0x0000C090 +#define MQNIC_RB_RX_QUEUE_MAP_VER 0x00000200 +#define MQNIC_RB_RX_QUEUE_MAP_REG_CFG 0x0C +#define MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET 0x10 +#define MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE 0x10 +#define MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET 0x00 +#define MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK 0x04 +#define MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK 0x08 + +#define MQNIC_RB_EQM_TYPE 0x0000C010 +#define MQNIC_RB_EQM_VER 0x00000400 +#define MQNIC_RB_EQM_REG_OFFSET 0x0C +#define MQNIC_RB_EQM_REG_COUNT 0x10 +#define MQNIC_RB_EQM_REG_STRIDE 0x14 + +#define MQNIC_RB_CQM_TYPE 0x0000C020 +#define MQNIC_RB_CQM_VER 0x00000400 +#define MQNIC_RB_CQM_REG_OFFSET 0x0C +#define MQNIC_RB_CQM_REG_COUNT 0x10 +#define MQNIC_RB_CQM_REG_STRIDE 0x14 + +#define MQNIC_RB_TX_QM_TYPE 0x0000C030 +#define MQNIC_RB_TX_QM_VER 0x00000400 +#define MQNIC_RB_TX_QM_REG_OFFSET 0x0C +#define MQNIC_RB_TX_QM_REG_COUNT 0x10 +#define MQNIC_RB_TX_QM_REG_STRIDE 0x14 + +#define MQNIC_RB_RX_QM_TYPE 0x0000C031 +#define MQNIC_RB_RX_QM_VER 0x00000400 +#define MQNIC_RB_RX_QM_REG_OFFSET 0x0C +#define MQNIC_RB_RX_QM_REG_COUNT 0x10 +#define MQNIC_RB_RX_QM_REG_STRIDE 0x14 + +#define MQNIC_RB_PORT_TYPE 0x0000C002 +#define MQNIC_RB_PORT_VER 0x00000200 +#define MQNIC_RB_PORT_REG_OFFSET 0x0C + +#define MQNIC_RB_PORT_CTRL_TYPE 0x0000C003 +#define MQNIC_RB_PORT_CTRL_VER 0x00000300 +#define MQNIC_RB_PORT_CTRL_REG_FEATURES 0x0C +#define MQNIC_RB_PORT_CTRL_REG_TX_CTRL 0x10 +#define MQNIC_RB_PORT_CTRL_REG_RX_CTRL 0x14 +#define MQNIC_RB_PORT_CTRL_REG_FC_CTRL 0x18 +#define MQNIC_RB_PORT_CTRL_REG_LFC_CTRL 0x1C +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 0x20 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL1 0x24 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL2 0x28 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL3 0x2C +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL4 0x30 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL5 0x34 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL6 0x38 +#define MQNIC_RB_PORT_CTRL_REG_PFC_CTRL7 0x3C + +#define MQNIC_PORT_FEATURE_LFC BIT(0) +#define MQNIC_PORT_FEATURE_PFC BIT(1) +#define MQNIC_PORT_FEATURE_INT_MAC_CTRL BIT(2) + +#define MQNIC_PORT_TX_CTRL_EN BIT(0) +#define MQNIC_PORT_TX_CTRL_PAUSE BIT(8) +#define MQNIC_PORT_TX_CTRL_STATUS BIT(16) +#define MQNIC_PORT_TX_CTRL_RESET BIT(17) +#define MQNIC_PORT_TX_CTRL_PAUSE_REQ BIT(24) +#define MQNIC_PORT_TX_CTRL_PAUSE_ACK BIT(25) + +#define MQNIC_PORT_RX_CTRL_EN BIT(0) +#define MQNIC_PORT_RX_CTRL_PAUSE BIT(8) +#define MQNIC_PORT_RX_CTRL_STATUS BIT(16) +#define MQNIC_PORT_RX_CTRL_RESET BIT(17) +#define MQNIC_PORT_RX_CTRL_PAUSE_REQ BIT(24) +#define MQNIC_PORT_RX_CTRL_PAUSE_ACK BIT(25) + +#define MQNIC_PORT_LFC_CTRL_TX_LFC_EN BIT(24) +#define MQNIC_PORT_LFC_CTRL_RX_LFC_EN BIT(25) +#define MQNIC_PORT_LFC_CTRL_TX_LFC_REQ BIT(28) +#define MQNIC_PORT_LFC_CTRL_RX_LFC_REQ BIT(29) + +#define MQNIC_PORT_PFC_CTRL_TX_PFC_EN BIT(24) +#define MQNIC_PORT_PFC_CTRL_RX_PFC_EN BIT(25) +#define MQNIC_PORT_PFC_CTRL_TX_PFC_REQ BIT(28) +#define MQNIC_PORT_PFC_CTRL_RX_PFC_REQ BIT(29) + +#define MQNIC_RB_SCHED_BLOCK_TYPE 0x0000C004 +#define MQNIC_RB_SCHED_BLOCK_VER 0x00000300 +#define MQNIC_RB_SCHED_BLOCK_REG_OFFSET 0x0C + +#define MQNIC_RB_SCHED_RR_TYPE 0x0000C040 +#define MQNIC_RB_SCHED_RR_VER 0x00000200 +#define MQNIC_RB_SCHED_RR_REG_OFFSET 0x0C +#define MQNIC_RB_SCHED_RR_REG_QUEUE_COUNT 0x10 +#define MQNIC_RB_SCHED_RR_REG_QUEUE_STRIDE 0x14 +#define MQNIC_RB_SCHED_RR_REG_CTRL 0x18 +#define MQNIC_RB_SCHED_RR_REG_CFG 0x1C +#define MQNIC_RB_SCHED_RR_REG_CH_STRIDE 0x10 +#define MQNIC_RB_SCHED_RR_REG_CH0_CTRL 0x20 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC1 0x24 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC1_DEST 0x24 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC1_PB 0x26 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC2 0x28 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC2_DB 0x28 +#define MQNIC_RB_SCHED_RR_REG_CH0_FC2_PL 0x2A +#define MQNIC_RB_SCHED_RR_REG_CH0_FC3 0x2C +#define MQNIC_RB_SCHED_RR_REG_CH0_FC3_DL 0x2C + +#define MQNIC_SCHED_RR_PORT_TC (0x7 << 0) +#define MQNIC_SCHED_RR_PORT_EN BIT(3) +#define MQNIC_SCHED_RR_PORT_PAUSE BIT(4) +#define MQNIC_SCHED_RR_PORT_SCHEDULED BIT(5) +#define MQNIC_SCHED_RR_QUEUE_EN BIT(6) +#define MQNIC_SCHED_RR_QUEUE_PAUSE BIT(7) +#define MQNIC_SCHED_RR_QUEUE_ACTIVE BIT(14) + +#define MQNIC_SCHED_RR_CMD_SET_PORT_TC 0x80010000 +#define MQNIC_SCHED_RR_CMD_SET_PORT_ENABLE 0x80020000 +#define MQNIC_SCHED_RR_CMD_SET_PORT_PAUSE 0x80030000 +#define MQNIC_SCHED_RR_CMD_SET_QUEUE_ENABLE 0x40000100 +#define MQNIC_SCHED_RR_CMD_SET_QUEUE_PAUSE 0x40000200 + +#define MQNIC_RB_SCHED_CTRL_TDMA_TYPE 0x0000C050 +#define MQNIC_RB_SCHED_CTRL_TDMA_VER 0x00000100 +#define MQNIC_RB_SCHED_CTRL_TDMA_REG_OFFSET 0x0C +#define MQNIC_RB_SCHED_CTRL_TDMA_REG_CH_COUNT 0x10 +#define MQNIC_RB_SCHED_CTRL_TDMA_REG_CH_STRIDE 0x14 +#define MQNIC_RB_SCHED_CTRL_TDMA_REG_CTRL 0x18 +#define MQNIC_RB_SCHED_CTRL_TDMA_REG_TS_COUNT 0x1C + +#define MQNIC_RB_TDMA_SCH_TYPE 0x0000C060 +#define MQNIC_RB_TDMA_SCH_VER 0x00000200 +#define MQNIC_RB_TDMA_SCH_REG_CTRL 0x0C +#define MQNIC_RB_TDMA_SCH_REG_SCH_START_FNS 0x10 +#define MQNIC_RB_TDMA_SCH_REG_SCH_START_NS 0x14 +#define MQNIC_RB_TDMA_SCH_REG_SCH_START_SEC_L 0x18 +#define MQNIC_RB_TDMA_SCH_REG_SCH_START_SEC_H 0x1C +#define MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_FNS 0x20 +#define MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_NS 0x24 +#define MQNIC_RB_TDMA_SCH_REG_TS_PERIOD_NS 0x28 +#define MQNIC_RB_TDMA_SCH_REG_ACTIVE_PERIOD_NS 0x2C + +#define MQNIC_RB_APP_INFO_TYPE 0x0000C005 +#define MQNIC_RB_APP_INFO_VER 0x00000200 +#define MQNIC_RB_APP_INFO_REG_ID 0x0C + +#define MQNIC_QUEUE_BASE_ADDR_VF_REG 0x00 +#define MQNIC_QUEUE_CTRL_STATUS_REG 0x08 +#define MQNIC_QUEUE_SIZE_CQN_REG 0x0C +#define MQNIC_QUEUE_PTR_REG 0x10 +#define MQNIC_QUEUE_PROD_PTR_REG 0x10 +#define MQNIC_QUEUE_CONS_PTR_REG 0x12 + +#define MQNIC_QUEUE_ENABLE_MASK 0x00000001 +#define MQNIC_QUEUE_ACTIVE_MASK 0x00000008 +#define MQNIC_QUEUE_PTR_MASK 0xFFFF + +#define MQNIC_QUEUE_CMD_SET_VF_ID 0x80010000 +#define MQNIC_QUEUE_CMD_SET_SIZE 0x80020000 +#define MQNIC_QUEUE_CMD_SET_CQN 0xC0000000 +#define MQNIC_QUEUE_CMD_SET_PROD_PTR 0x80800000 +#define MQNIC_QUEUE_CMD_SET_CONS_PTR 0x80900000 +#define MQNIC_QUEUE_CMD_SET_ENABLE 0x40000100 + +#define MQNIC_CQ_BASE_ADDR_VF_REG 0x00 +#define MQNIC_CQ_CTRL_STATUS_REG 0x08 +#define MQNIC_CQ_PTR_REG 0x0C +#define MQNIC_CQ_PROD_PTR_REG 0x0C +#define MQNIC_CQ_CONS_PTR_REG 0x0E + +#define MQNIC_CQ_ENABLE_MASK 0x00010000 +#define MQNIC_CQ_ARM_MASK 0x00020000 +#define MQNIC_CQ_ACTIVE_MASK 0x00080000 +#define MQNIC_CQ_PTR_MASK 0xFFFF + +#define MQNIC_CQ_CMD_SET_VF_ID 0x80010000 +#define MQNIC_CQ_CMD_SET_SIZE 0x80020000 +#define MQNIC_CQ_CMD_SET_EQN 0xC0000000 +#define MQNIC_CQ_CMD_SET_PROD_PTR 0x80800000 +#define MQNIC_CQ_CMD_SET_CONS_PTR 0x80900000 +#define MQNIC_CQ_CMD_SET_CONS_PTR_ARM 0x80910000 +#define MQNIC_CQ_CMD_SET_ENABLE 0x40000100 +#define MQNIC_CQ_CMD_SET_ARM 0x40000200 + +#define MQNIC_EQ_BASE_ADDR_VF_REG 0x00 +#define MQNIC_EQ_CTRL_STATUS_REG 0x08 +#define MQNIC_EQ_PTR_REG 0x0C +#define MQNIC_EQ_PROD_PTR_REG 0x0C +#define MQNIC_EQ_CONS_PTR_REG 0x0E + +#define MQNIC_EQ_ENABLE_MASK 0x00010000 +#define MQNIC_EQ_ARM_MASK 0x00020000 +#define MQNIC_EQ_ACTIVE_MASK 0x00080000 +#define MQNIC_EQ_PTR_MASK 0xFFFF + +#define MQNIC_EQ_CMD_SET_VF_ID 0x80010000 +#define MQNIC_EQ_CMD_SET_SIZE 0x80020000 +#define MQNIC_EQ_CMD_SET_IRQN 0xC0000000 +#define MQNIC_EQ_CMD_SET_PROD_PTR 0x80800000 +#define MQNIC_EQ_CMD_SET_CONS_PTR 0x80900000 +#define MQNIC_EQ_CMD_SET_CONS_PTR_ARM 0x80910000 +#define MQNIC_EQ_CMD_SET_ENABLE 0x40000100 +#define MQNIC_EQ_CMD_SET_ARM 0x40000200 + +#define MQNIC_EVENT_TYPE_CPL 0x0000 + +#define MQNIC_DESC_SIZE 16 +#define MQNIC_CPL_SIZE 32 +#define MQNIC_EVENT_SIZE 32 + +struct mqnic_desc { + __le16 rsvd0; + __le16 tx_csum_cmd; + __le32 len; + __le64 addr; +}; + +struct mqnic_cpl { + __le16 queue; + __le16 index; + __le16 len; + __le16 ts_fns; + __le32 ts_ns; + __le16 ts_s; + __le16 rx_csum; + __le32 rx_hash; + __u8 rx_hash_type; + __u8 port; + __u8 rsvd2; + __u8 rsvd3; + __le32 rsvd4; + __le32 phase; +}; + +struct mqnic_event { + __le16 type; + __le16 source; + __le32 rsvd0; + __le32 rsvd1; + __le32 rsvd2; + __le32 rsvd3; + __le32 rsvd4; + __le32 rsvd5; + __le32 phase; +}; + +#endif /* MQNIC_HW_H */ diff --git a/drivers/net/mqnic/mqnic_i2c.c b/drivers/net/mqnic/mqnic_i2c.c new file mode 100644 index 00000000000000..7e8a1d9fca7cc3 --- /dev/null +++ b/drivers/net/mqnic/mqnic_i2c.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +static void mqnic_i2c_set_scl(void *data, int state) +{ + struct mqnic_i2c_bus *bus = data; + + if (state) + iowrite32(ioread32(bus->scl_out_reg) | bus->scl_out_mask, bus->scl_out_reg); + else + iowrite32(ioread32(bus->scl_out_reg) & ~bus->scl_out_mask, bus->scl_out_reg); +} + +static void mqnic_i2c_set_sda(void *data, int state) +{ + struct mqnic_i2c_bus *bus = data; + + if (state) + iowrite32(ioread32(bus->sda_out_reg) | bus->sda_out_mask, bus->sda_out_reg); + else + iowrite32(ioread32(bus->sda_out_reg) & ~bus->sda_out_mask, bus->sda_out_reg); +} + +static int mqnic_i2c_get_scl(void *data) +{ + struct mqnic_i2c_bus *bus = data; + + return !!(ioread32(bus->scl_in_reg) & bus->scl_in_mask); +} + +static int mqnic_i2c_get_sda(void *data) +{ + struct mqnic_i2c_bus *bus = data; + + return !!(ioread32(bus->sda_in_reg) & bus->sda_in_mask); +} + +struct mqnic_i2c_bus *mqnic_i2c_bus_create(struct mqnic_dev *mqnic, int index) +{ + struct mqnic_i2c_bus *bus; + struct i2c_algo_bit_data *algo; + struct i2c_adapter *adapter; + struct mqnic_reg_block *rb; + + rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_I2C_TYPE, MQNIC_RB_I2C_VER, index); + + if (!rb) + return NULL; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + + if (!bus) + return NULL; + + // set private data + bus->mqnic = mqnic; + bus->scl_in_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL; + bus->scl_out_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL; + bus->sda_in_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL; + bus->sda_out_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL; + bus->scl_in_mask = MQNIC_REG_GPIO_I2C_SCL_IN; + bus->scl_out_mask = MQNIC_REG_GPIO_I2C_SCL_OUT; + bus->sda_in_mask = MQNIC_REG_GPIO_I2C_SDA_IN; + bus->sda_out_mask = MQNIC_REG_GPIO_I2C_SDA_OUT; + + // bit-bang algorithm setup + algo = &bus->algo; + algo->udelay = 5; + algo->timeout = usecs_to_jiffies(2000); + algo->setsda = mqnic_i2c_set_sda; + algo->setscl = mqnic_i2c_set_scl; + algo->getsda = mqnic_i2c_get_sda; + algo->getscl = mqnic_i2c_get_scl; + algo->data = bus; + + // adapter setup + adapter = &bus->adapter; + adapter->owner = THIS_MODULE; + adapter->algo_data = algo; + adapter->dev.parent = mqnic->dev; + snprintf(adapter->name, sizeof(adapter->name), "%s I2C%d", mqnic->name, + mqnic->i2c_adapter_count); + + if (i2c_bit_add_bus(adapter)) { + dev_err(mqnic->dev, "Failed to register I2C adapter"); + goto err_free_bus; + } + + list_add_tail(&bus->head, &mqnic->i2c_bus); + + mqnic->i2c_adapter_count++; + + return bus; + +err_free_bus: + kfree(bus); + return NULL; +} + +struct i2c_adapter *mqnic_i2c_adapter_create(struct mqnic_dev *mqnic, int index) +{ + struct mqnic_i2c_bus *bus = mqnic_i2c_bus_create(mqnic, index); + + if (!bus) + return NULL; + + return &bus->adapter; +} + +void mqnic_i2c_bus_release(struct mqnic_i2c_bus *bus) +{ + struct mqnic_dev *mqnic; + + if (!bus) + return; + + mqnic = bus->mqnic; + + mqnic->i2c_adapter_count--; + + i2c_del_adapter(&bus->adapter); + list_del(&bus->head); + kfree(bus); +} + +void mqnic_i2c_adapter_release(struct i2c_adapter *adapter) +{ + struct mqnic_i2c_bus *bus; + + if (!adapter) + return; + + bus = container_of(adapter, struct mqnic_i2c_bus, adapter); + mqnic_i2c_bus_release(bus); +} + +int mqnic_i2c_init(struct mqnic_dev *mqnic) +{ + INIT_LIST_HEAD(&mqnic->i2c_bus); + + return 0; +} + +void mqnic_i2c_deinit(struct mqnic_dev *mqnic) +{ + struct mqnic_i2c_bus *bus; + + while (!list_empty(&mqnic->i2c_bus)) { + bus = list_first_entry(&mqnic->i2c_bus, typeof(*bus), head); + mqnic_i2c_bus_release(bus); + } +} diff --git a/drivers/net/mqnic/mqnic_if.c b/drivers/net/mqnic/mqnic_if.c new file mode 100644 index 00000000000000..1eec558c54b45f --- /dev/null +++ b/drivers/net/mqnic/mqnic_if.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2021-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_if *mqnic_create_interface(struct mqnic_dev *mdev, int index, u8 __iomem *hw_addr) +{ + struct device *dev = mdev->dev; + struct mqnic_if *interface; + struct mqnic_reg_block *rb; + int ret = 0; + int k; + u32 count, offset, stride; + u32 desc_block_size; + u32 val; + + interface = kzalloc(sizeof(*interface), GFP_KERNEL); + if (!interface) + return ERR_PTR(-ENOMEM); + + interface->mdev = mdev; + interface->dev = dev; + + interface->index = index; + + interface->hw_regs_size = mdev->if_stride; + interface->hw_addr = hw_addr; + interface->csr_hw_addr = hw_addr + mdev->if_csr_offset; + + init_rwsem(&interface->eq_table_sem); + INIT_RADIX_TREE(&interface->eq_table, GFP_KERNEL); + + INIT_LIST_HEAD(&interface->free_sched_port_list); + spin_lock_init(&interface->free_sched_port_list_lock); + + INIT_LIST_HEAD(&interface->ndev_list); + + // Enumerate registers + interface->rb_list = mqnic_enumerate_reg_block_list(interface->hw_addr, mdev->if_csr_offset, interface->hw_regs_size); + if (!interface->rb_list) { + ret = -EIO; + dev_err(dev, "Failed to enumerate blocks"); + goto fail; + } + + dev_info(dev, "Interface-level register blocks:"); + for (rb = interface->rb_list; rb->regs; rb++) + dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24, + (rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff); + + interface->if_ctrl_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_IF_CTRL_TYPE, MQNIC_RB_IF_CTRL_VER, 0); + + if (!interface->if_ctrl_rb) { + ret = -EIO; + dev_err(dev, "Interface control block not found"); + goto fail; + } + + interface->if_features = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_FEATURES); + interface->port_count = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_PORT_COUNT); + interface->sched_block_count = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_SCHED_COUNT); + interface->max_tx_mtu = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_MAX_TX_MTU); + interface->max_rx_mtu = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_MAX_RX_MTU); + interface->tx_fifo_depth = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_TX_FIFO_DEPTH); + interface->rx_fifo_depth = ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_RX_FIFO_DEPTH); + + dev_info(dev, "IF features: 0x%08x", interface->if_features); + dev_info(dev, "Port count: %d", interface->port_count); + dev_info(dev, "Scheduler block count: %d", interface->sched_block_count); + dev_info(dev, "Max TX MTU: %d", interface->max_tx_mtu); + dev_info(dev, "Max RX MTU: %d", interface->max_rx_mtu); + + interface->eq_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_EQM_TYPE, MQNIC_RB_EQM_VER, 0); + + if (!interface->eq_rb) { + ret = -EIO; + dev_err(dev, "EQ block not found"); + goto fail; + } + + offset = ioread32(interface->eq_rb->regs + MQNIC_RB_EQM_REG_OFFSET); + count = ioread32(interface->eq_rb->regs + MQNIC_RB_EQM_REG_COUNT); + stride = ioread32(interface->eq_rb->regs + MQNIC_RB_EQM_REG_STRIDE); + + dev_info(dev, "EQ offset: 0x%08x", offset); + dev_info(dev, "EQ count: %d", count); + dev_info(dev, "EQ stride: 0x%08x", stride); + + count = min_t(u32, count, MQNIC_MAX_EQ); + + interface->eq_res = mqnic_create_res(count, hw_addr + offset, stride); + + if (IS_ERR_OR_NULL(interface->eq_res)) { + ret = PTR_ERR(interface->eq_res); + goto fail; + } + + interface->cq_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_CQM_TYPE, MQNIC_RB_CQM_VER, 0); + + if (!interface->cq_rb) { + ret = -EIO; + dev_err(dev, "CQ block not found"); + goto fail; + } + + offset = ioread32(interface->cq_rb->regs + MQNIC_RB_CQM_REG_OFFSET); + count = ioread32(interface->cq_rb->regs + MQNIC_RB_CQM_REG_COUNT); + stride = ioread32(interface->cq_rb->regs + MQNIC_RB_CQM_REG_STRIDE); + + dev_info(dev, "CQ offset: 0x%08x", offset); + dev_info(dev, "CQ count: %d", count); + dev_info(dev, "CQ stride: 0x%08x", stride); + + count = min_t(u32, count, MQNIC_MAX_CQ); + + interface->cq_res = mqnic_create_res(count, hw_addr + offset, stride); + + if (IS_ERR_OR_NULL(interface->cq_res)) { + ret = PTR_ERR(interface->cq_res); + goto fail; + } + + interface->txq_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_TX_QM_TYPE, MQNIC_RB_TX_QM_VER, 0); + + if (!interface->txq_rb) { + ret = -EIO; + dev_err(dev, "TXQ block not found"); + goto fail; + } + + offset = ioread32(interface->txq_rb->regs + MQNIC_RB_TX_QM_REG_OFFSET); + count = ioread32(interface->txq_rb->regs + MQNIC_RB_TX_QM_REG_COUNT); + stride = ioread32(interface->txq_rb->regs + MQNIC_RB_TX_QM_REG_STRIDE); + + dev_info(dev, "TXQ offset: 0x%08x", offset); + dev_info(dev, "TXQ count: %d", count); + dev_info(dev, "TXQ stride: 0x%08x", stride); + + count = min_t(u32, count, MQNIC_MAX_TXQ); + + interface->txq_res = mqnic_create_res(count, hw_addr + offset, stride); + + if (IS_ERR_OR_NULL(interface->txq_res)) { + ret = PTR_ERR(interface->txq_res); + goto fail; + } + + interface->rxq_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_RX_QM_TYPE, MQNIC_RB_RX_QM_VER, 0); + + if (!interface->rxq_rb) { + ret = -EIO; + dev_err(dev, "RXQ block not found"); + goto fail; + } + + offset = ioread32(interface->rxq_rb->regs + MQNIC_RB_RX_QM_REG_OFFSET); + count = ioread32(interface->rxq_rb->regs + MQNIC_RB_RX_QM_REG_COUNT); + stride = ioread32(interface->rxq_rb->regs + MQNIC_RB_RX_QM_REG_STRIDE); + + dev_info(dev, "RXQ offset: 0x%08x", offset); + dev_info(dev, "RXQ count: %d", count); + dev_info(dev, "RXQ stride: 0x%08x", stride); + + count = min_t(u32, count, MQNIC_MAX_RXQ); + + interface->rxq_res = mqnic_create_res(count, hw_addr + offset, stride); + + if (IS_ERR_OR_NULL(interface->rxq_res)) { + ret = PTR_ERR(interface->rxq_res); + goto fail; + } + + interface->rx_queue_map_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_RX_QUEUE_MAP_TYPE, MQNIC_RB_RX_QUEUE_MAP_VER, 0); + + if (!interface->rx_queue_map_rb) { + ret = -EIO; + dev_err(dev, "RX queue map block not found"); + goto fail; + } + + val = ioread32(interface->rx_queue_map_rb->regs + MQNIC_RB_RX_QUEUE_MAP_REG_CFG); + interface->rx_queue_map_indir_table_size = 1 << ((val >> 8) & 0xff); + + dev_info(dev, "RX queue map indirection table size: %d", interface->rx_queue_map_indir_table_size); + + for (k = 0; k < interface->port_count; k++) { + interface->rx_queue_map_indir_table[k] = interface->hw_addr + ioread32(interface->rx_queue_map_rb->regs + + MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET + + MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE * k + + MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET); + + mqnic_interface_set_rx_queue_map_rss_mask(interface, k, 0); + mqnic_interface_set_rx_queue_map_app_mask(interface, k, 0); + mqnic_interface_set_rx_queue_map_indir_table(interface, k, 0, 0); + } + + // determine desc block size + iowrite32(MQNIC_QUEUE_CMD_SET_SIZE | 0xff00, mqnic_res_get_addr(interface->txq_res, 0) + MQNIC_QUEUE_CTRL_STATUS_REG); + interface->max_desc_block_size = 1 << ((ioread32(mqnic_res_get_addr(interface->txq_res, 0) + MQNIC_QUEUE_SIZE_CQN_REG) >> 28) & 0xf); + iowrite32(MQNIC_QUEUE_CMD_SET_SIZE | 0x0000, mqnic_res_get_addr(interface->txq_res, 0) + MQNIC_QUEUE_CTRL_STATUS_REG); + + dev_info(dev, "Max desc block size: %d", interface->max_desc_block_size); + + interface->max_desc_block_size = min_t(u32, interface->max_desc_block_size, MQNIC_MAX_FRAGS); + + desc_block_size = min_t(u32, interface->max_desc_block_size, 4); + + // disable queues + for (k = 0; k < mqnic_res_get_count(interface->eq_res); k++) + iowrite32(MQNIC_EQ_CMD_SET_ENABLE | 0, mqnic_res_get_addr(interface->eq_res, k) + MQNIC_EQ_CTRL_STATUS_REG); + + for (k = 0; k < mqnic_res_get_count(interface->cq_res); k++) + iowrite32(MQNIC_CQ_CMD_SET_ENABLE | 0, mqnic_res_get_addr(interface->cq_res, k) + MQNIC_CQ_CTRL_STATUS_REG); + + for (k = 0; k < mqnic_res_get_count(interface->txq_res); k++) + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, mqnic_res_get_addr(interface->txq_res, k) + MQNIC_QUEUE_CTRL_STATUS_REG); + + for (k = 0; k < mqnic_res_get_count(interface->rxq_res); k++) + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, mqnic_res_get_addr(interface->rxq_res, k) + MQNIC_QUEUE_CTRL_STATUS_REG); + + // create ports + for (k = 0; k < interface->port_count; k++) { + struct mqnic_port *port; + struct mqnic_reg_block *port_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_PORT_TYPE, MQNIC_RB_PORT_VER, k); + + if (!port_rb) { + ret = -EIO; + dev_err(dev, "Port index %d not found", k); + goto fail; + } + + port = mqnic_create_port(interface, k, mdev->phys_port_max++, port_rb); + if (IS_ERR_OR_NULL(port)) { + ret = PTR_ERR(port); + goto fail; + } + interface->port[k] = port; + } + + // create schedulers + for (k = 0; k < interface->sched_block_count; k++) { + struct mqnic_sched_block *sched_block; + struct mqnic_reg_block *sched_block_rb = mqnic_find_reg_block(interface->rb_list, MQNIC_RB_SCHED_BLOCK_TYPE, MQNIC_RB_SCHED_BLOCK_VER, k); + + if (!sched_block_rb) { + ret = -EIO; + dev_err(dev, "Scheduler block index %d not found", k); + goto fail; + } + + sched_block = mqnic_create_sched_block(interface, k, sched_block_rb); + if (IS_ERR_OR_NULL(sched_block)) { + ret = PTR_ERR(sched_block); + goto fail; + } + interface->sched_block[k] = sched_block; + } + + // create EQs + interface->eq_count = mqnic_res_get_count(interface->eq_res); + for (k = 0; k < interface->eq_count; k++) { + struct mqnic_eq *eq = mqnic_create_eq(interface); + + if (IS_ERR_OR_NULL(eq)) { + ret = PTR_ERR(eq); + goto fail; + } + + down_write(&interface->eq_table_sem); + ret = radix_tree_insert(&interface->eq_table, k, eq); + up_write(&interface->eq_table_sem); + if (ret) { + mqnic_destroy_eq(eq); + goto fail; + } + + ret = mqnic_open_eq(eq, mdev->irq[k % mdev->irq_count], mqnic_num_eq_entries); + if (ret) + goto fail; + + mqnic_arm_eq(eq); + } + + // create net_devices + interface->ndev_count = interface->port_count; + for (k = 0; k < interface->ndev_count; k++) { + struct net_device *ndev; + struct mqnic_priv *priv; + + ndev = mqnic_create_netdev(interface, interface->port[k]); + if (IS_ERR_OR_NULL(ndev)) { + ret = PTR_ERR(ndev); + goto fail; + } + priv = netdev_priv(ndev); + list_add_tail(&priv->ndev_list, &interface->ndev_list); + } + + return interface; + +fail: + mqnic_destroy_interface(interface); + return ERR_PTR(ret); +} + +void mqnic_destroy_interface(struct mqnic_if *interface) +{ + struct mqnic_priv *priv, *priv_safe; + struct radix_tree_iter iter; + void **slot; + int k; + + // destroy associated net_devices + list_for_each_entry_safe(priv, priv_safe, &interface->ndev_list, ndev_list) { + list_del(&priv->ndev_list); + mqnic_destroy_netdev(priv->ndev); + } + + // free EQs + down_write(&interface->eq_table_sem); + radix_tree_for_each_slot(slot, &interface->eq_table, &iter, 0) { + struct mqnic_eq *eq = (struct mqnic_eq *)*slot; + + mqnic_destroy_eq(eq); + } + up_write(&interface->eq_table_sem); + + // free schedulers + for (k = 0; k < ARRAY_SIZE(interface->sched_block); k++) { + if (interface->sched_block[k]) { + mqnic_destroy_sched_block(interface->sched_block[k]); + interface->sched_block[k] = NULL; + } + } + + // free ports + for (k = 0; k < ARRAY_SIZE(interface->port); k++) { + if (interface->port[k]) { + mqnic_destroy_port(interface->port[k]); + interface->port[k] = NULL; + } + } + + mqnic_destroy_res(interface->eq_res); + mqnic_destroy_res(interface->cq_res); + mqnic_destroy_res(interface->txq_res); + mqnic_destroy_res(interface->rxq_res); + + if (interface->rb_list) + mqnic_free_reg_block_list(interface->rb_list); + + kfree(interface); +} + +u32 mqnic_interface_get_tx_mtu(struct mqnic_if *interface) +{ + return ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_TX_MTU); +} +EXPORT_SYMBOL(mqnic_interface_get_tx_mtu); + +void mqnic_interface_set_tx_mtu(struct mqnic_if *interface, u32 mtu) +{ + iowrite32(mtu, interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_TX_MTU); +} +EXPORT_SYMBOL(mqnic_interface_set_tx_mtu); + +u32 mqnic_interface_get_rx_mtu(struct mqnic_if *interface) +{ + return ioread32(interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_RX_MTU); +} +EXPORT_SYMBOL(mqnic_interface_get_rx_mtu); + +void mqnic_interface_set_rx_mtu(struct mqnic_if *interface, u32 mtu) +{ + iowrite32(mtu, interface->if_ctrl_rb->regs + MQNIC_RB_IF_CTRL_REG_RX_MTU); +} +EXPORT_SYMBOL(mqnic_interface_set_rx_mtu); + +u32 mqnic_interface_get_rx_queue_map_rss_mask(struct mqnic_if *interface, int port) +{ + return ioread32(interface->rx_queue_map_rb->regs + MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET + + MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE * port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK); +} +EXPORT_SYMBOL(mqnic_interface_get_rx_queue_map_rss_mask); + +void mqnic_interface_set_rx_queue_map_rss_mask(struct mqnic_if *interface, int port, u32 val) +{ + iowrite32(val, interface->rx_queue_map_rb->regs + MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET + + MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE * port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK); +} +EXPORT_SYMBOL(mqnic_interface_set_rx_queue_map_rss_mask); + +u32 mqnic_interface_get_rx_queue_map_app_mask(struct mqnic_if *interface, int port) +{ + return ioread32(interface->rx_queue_map_rb->regs + MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET + + MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE * port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK); +} +EXPORT_SYMBOL(mqnic_interface_get_rx_queue_map_app_mask); + +void mqnic_interface_set_rx_queue_map_app_mask(struct mqnic_if *interface, int port, u32 val) +{ + iowrite32(val, interface->rx_queue_map_rb->regs + MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET + + MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE * port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK); +} +EXPORT_SYMBOL(mqnic_interface_set_rx_queue_map_app_mask); + +u32 mqnic_interface_get_rx_queue_map_indir_table(struct mqnic_if *interface, int port, int index) +{ + return ioread32(interface->rx_queue_map_indir_table[port] + index * 4); +} +EXPORT_SYMBOL(mqnic_interface_get_rx_queue_map_indir_table); + +void mqnic_interface_set_rx_queue_map_indir_table(struct mqnic_if *interface, int port, int index, u32 val) +{ + iowrite32(val, interface->rx_queue_map_indir_table[port] + index * 4); +} +EXPORT_SYMBOL(mqnic_interface_set_rx_queue_map_indir_table); + +int mqnic_interface_register_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port) +{ + spin_lock(&interface->free_sched_port_list_lock); + list_add_tail(&port->free_list, &interface->free_sched_port_list); + spin_unlock(&interface->free_sched_port_list_lock); + return 0; +} + +int mqnic_interface_unregister_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port) +{ + spin_lock(&interface->free_sched_port_list_lock); + list_del(&port->free_list); + spin_unlock(&interface->free_sched_port_list_lock); + return 0; +} + +struct mqnic_sched_port *mqnic_interface_alloc_sched_port(struct mqnic_if *interface) +{ + struct mqnic_sched_port *port = NULL; + + spin_lock(&interface->free_sched_port_list_lock); + + port = list_first_entry_or_null(&interface->free_sched_port_list, struct mqnic_sched_port, free_list); + + if (port) + list_del(&port->free_list); + + spin_unlock(&interface->free_sched_port_list_lock); + + return port; +} + +void mqnic_interface_free_sched_port(struct mqnic_if *interface, struct mqnic_sched_port *port) +{ + if (!port) + return; + spin_lock(&interface->free_sched_port_list_lock); + list_add_tail(&port->free_list, &interface->free_sched_port_list); + spin_unlock(&interface->free_sched_port_list_lock); +} diff --git a/drivers/net/mqnic/mqnic_ioctl.h b/drivers/net/mqnic/mqnic_ioctl.h new file mode 100644 index 00000000000000..7b3c76527a6825 --- /dev/null +++ b/drivers/net/mqnic/mqnic_ioctl.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Views */ +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#ifndef MQNIC_IOCTL_H +#define MQNIC_IOCTL_H + +#include + +#define MQNIC_IOCTL_API_VERSION 0 + +#define MQNIC_IOCTL_TYPE 0x88 +#define MQNIC_IOCTL_BASE 0xC0 + +enum { + MQNIC_REGION_TYPE_UNIMPLEMENTED = 0x00000000, + MQNIC_REGION_TYPE_CTRL = 0x00001000, + MQNIC_REGION_TYPE_NIC_CTRL = 0x00001001, + MQNIC_REGION_TYPE_APP_CTRL = 0x00001002, + MQNIC_REGION_TYPE_RAM = 0x00002000 +}; + +// get API version +#define MQNIC_IOCTL_GET_API_VERSION _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 0) + +// get device information +struct mqnic_ioctl_device_info { + __u32 argsz; + __u32 flags; + __u32 fw_id; + __u32 fw_ver; + __u32 board_id; + __u32 board_ver; + __u32 build_date; + __u32 git_hash; + __u32 rel_info; + __u32 num_regions; + __u32 num_irqs; +}; + +#define MQNIC_IOCTL_GET_DEVICE_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 1) + +// get region information +struct mqnic_ioctl_region_info { + __u32 argsz; + __u32 flags; + __u32 index; + __u32 type; + __u32 next; + __u32 child; + __u64 size; + __u64 offset; + __u8 name[32]; +}; + +#define MQNIC_IOCTL_GET_REGION_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 2) + +#endif /* MQNIC_IOCTL_H */ diff --git a/drivers/net/mqnic/mqnic_irq.c b/drivers/net/mqnic/mqnic_irq.c new file mode 100644 index 00000000000000..fd358a083e4b96 --- /dev/null +++ b/drivers/net/mqnic/mqnic_irq.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2021-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +static irqreturn_t mqnic_irq_handler(int irqn, void *data) +{ + struct mqnic_irq *irq = data; + + atomic_notifier_call_chain(&irq->nh, 0, NULL); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_CORUNDUM_MQNIC_PCI +int mqnic_irq_init_pcie(struct mqnic_dev *mdev) +{ + struct pci_dev *pdev = mdev->pdev; + struct device *dev = mdev->dev; + int ret = 0; + int k; + + // Allocate MSI IRQs + mdev->irq_count = pci_alloc_irq_vectors(pdev, 1, MQNIC_MAX_IRQ, PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (mdev->irq_count < 0) { + dev_err(dev, "Failed to allocate IRQs"); + return -ENOMEM; + } + + // Set up interrupts + for (k = 0; k < mdev->irq_count; k++) { + struct mqnic_irq *irq; + + irq = kzalloc(sizeof(*irq), GFP_KERNEL); + if (!irq) { + ret = -ENOMEM; + goto fail; + } + + ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); + + ret = pci_request_irq(pdev, k, mqnic_irq_handler, NULL, + irq, "%s-%d", mdev->name, k); + if (ret < 0) { + kfree(irq); + ret = -ENOMEM; + dev_err(dev, "Failed to request IRQ %d", k); + goto fail; + } + + irq->index = k; + irq->irqn = pci_irq_vector(pdev, k); + mdev->irq[k] = irq; + } + + dev_info(dev, "Configured %d IRQs", mdev->irq_count); + + return 0; +fail: + mqnic_irq_deinit_pcie(mdev); + return ret; +} + +void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev) +{ + struct pci_dev *pdev = mdev->pdev; + int k; + + for (k = 0; k < MQNIC_MAX_IRQ; k++) { + if (mdev->irq[k]) { + pci_free_irq(pdev, k, mdev->irq[k]); + kfree(mdev->irq[k]); + mdev->irq[k] = NULL; + } + } + + pci_free_irq_vectors(pdev); +} +#endif + +int mqnic_irq_init_platform(struct mqnic_dev *mdev) +{ + struct platform_device *pdev = mdev->pfdev; + struct device *dev = mdev->dev; + int k; + + // Allocate IRQs + mdev->irq_count = platform_irq_count(pdev); + + // Set up interrupts + for (k = 0; k < mdev->irq_count; k++) { + int irqn; + struct mqnic_irq *irq; + int ret; + + irqn = platform_get_irq(pdev, k); + if (irqn < 0) + return irqn; + + irq = devm_kzalloc(dev, sizeof(*irq), GFP_KERNEL); + if (!irq) + return -ENOMEM; + + ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); + + snprintf(irq->name, sizeof(irq->name), "%s-%u", mdev->name, k); + ret = devm_request_irq(dev, irqn, mqnic_irq_handler, 0, irq->name, irq); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ %d (interrupt number %d)", k, irqn); + return ret; + } + + irq->index = k; + irq->irqn = irqn; + mdev->irq[k] = irq; + } + + dev_info(dev, "Configured %d IRQs", mdev->irq_count); + + return 0; +} diff --git a/drivers/net/mqnic/mqnic_main.c b/drivers/net/mqnic/mqnic_main.c new file mode 100644 index 00000000000000..4d06c39e40df43 --- /dev/null +++ b/drivers/net/mqnic/mqnic_main.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" +#include +#include +#include +#include +#include + +unsigned int mqnic_num_eq_entries = 1024; +unsigned int mqnic_num_txq_entries = 1024; +unsigned int mqnic_num_rxq_entries = 1024; + +module_param_named(num_eq_entries, mqnic_num_eq_entries, uint, 0444); +MODULE_PARM_DESC(num_eq_entries, "number of entries to allocate per event queue (default: 1024)"); +module_param_named(num_txq_entries, mqnic_num_txq_entries, uint, 0444); +MODULE_PARM_DESC(num_txq_entries, "number of entries to allocate per transmit queue (default: 1024)"); +module_param_named(num_rxq_entries, mqnic_num_rxq_entries, uint, 0444); +MODULE_PARM_DESC(num_rxq_entries, "number of entries to allocate per receive queue (default: 1024)"); + +unsigned int mqnic_link_status_poll = MQNIC_LINK_STATUS_POLL_MS; + +module_param_named(link_status_poll, mqnic_link_status_poll, uint, 0444); +MODULE_PARM_DESC(link_status_poll, + "link status polling interval, in ms (default: 1000; 0 to turn off)"); + +#ifdef CONFIG_CORUNDUM_MQNIC_PCI +static const struct pci_device_id mqnic_pci_id_table[] = { + {PCI_DEVICE(0x1234, 0x1001)}, + {PCI_DEVICE(0x5543, 0x1001)}, + {0 /* end */ } +}; + +MODULE_DEVICE_TABLE(pci, mqnic_pci_id_table); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id mqnic_of_id_table[] = { + { .compatible = "corundum,mqnic" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mqnic_of_id_table); +#endif + +static LIST_HEAD(mqnic_devices); +static DEFINE_SPINLOCK(mqnic_devices_lock); + +static unsigned int mqnic_get_free_id(void) +{ + struct mqnic_dev *mqnic; + unsigned int id = 0; + bool available = false; + + while (!available) { + available = true; + list_for_each_entry(mqnic, &mqnic_devices, dev_list_node) { + if (mqnic->id == id) { + available = false; + id++; + break; + } + } + } + + return id; +} + +static void mqnic_assign_id(struct mqnic_dev *mqnic) +{ + spin_lock(&mqnic_devices_lock); + mqnic->id = mqnic_get_free_id(); + list_add_tail(&mqnic->dev_list_node, &mqnic_devices); + spin_unlock(&mqnic_devices_lock); + + snprintf(mqnic->name, sizeof(mqnic->name), DRIVER_NAME "%d", mqnic->id); +} + +static void mqnic_free_id(struct mqnic_dev *mqnic) +{ + spin_lock(&mqnic_devices_lock); + list_del(&mqnic->dev_list_node); + spin_unlock(&mqnic_devices_lock); +} + +static int mqnic_common_setdma(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + + // Set mask + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + dev_warn(dev, "Warning: failed to set 64 bit PCI DMA mask"); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Failed to set PCI DMA mask"); + return ret; + } + } + + // Set max segment size + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + return ret; +} + +#ifdef CONFIG_OF +static int mqnic_platform_get_mac_address(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + char mac_base[ETH_ALEN]; + struct device_node *np; + u32 inc_idx; + u32 inc; + int k; + + /* NOTE: Not being able to get a (base) MAC address shall not be an + * error to fail on intentionally. Thus we are warning, only. + */ + ret = eth_platform_get_mac_address(dev, mac_base); + if (ret) { + dev_warn(dev, "Unable to get MAC address\n"); + return 0; + } + + np = mqnic->dev->of_node; + if (!np) + return 0; + + if (of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC_BYTE, &inc_idx)) + inc_idx = 5; + if (inc_idx < 3 || inc_idx > 5) { + dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC_BYTE "\"\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC, &inc); + if (ret == -EINVAL) { + inc = 0; + } else if (ret) { + dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC "\"\n"); + return ret; + } + + if (of_property_read_bool(np, MQNIC_PROP_MAC_ADDR_LOCAL)) + mac_base[0] |= BIT(1); + + mqnic->mac_count = mqnic->if_count; + for (k = 0; k < mqnic->mac_count; k++) { + memcpy(mqnic->mac_list[k], mac_base, ETH_ALEN); + mqnic->mac_list[k][inc_idx] += inc + k; + } + + return 0; +} + +static void mqnic_platform_module_eeprom_put(struct mqnic_dev *mqnic) +{ + int k; + + for (k = 0; k < mqnic->if_count; k++) + if (mqnic->mod_i2c_client[k]) + put_device(&mqnic->mod_i2c_client[k]->dev); +} + +static int mqnic_platform_module_eeprom_get(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + int k; + + ret = 0; + + if (!dev->of_node) + return 0; + + for (k = 0; k < mqnic->if_count; k++) { + struct device_node *np; + struct i2c_client *cl; + + /* NOTE: Not being able to get a phandle for module EEPROM shall + * not be an error to fail on intentionally. Thus we are + * warning, only. + */ + np = of_parse_phandle(dev->of_node, MQNIC_PROP_MODULE_EEPROM, k); + if (!np) { + dev_warn(dev, "Missing phandle to module EEPROM for interface %d\n", k); + continue; + } + + cl = of_find_i2c_device_by_node(np); + if (!cl) { + ret = -ENOENT; + dev_err(dev, "Failed to find I2C device for module of interface %d\n", k); + of_node_put(np); + break; + } + mqnic->mod_i2c_client[k] = cl; + mqnic->mod_i2c_client_count++; + of_node_put(np); + } + + if (ret) + mqnic_platform_module_eeprom_put(mqnic); + + return ret; +} +#endif + +static void mqnic_common_remove(struct mqnic_dev *mqnic); + +#ifdef CONFIG_AUXILIARY_BUS +static void mqnic_adev_release(struct device *dev) +{ + struct mqnic_adev *mqnic_adev = container_of(dev, struct mqnic_adev, adev.dev); + + if (mqnic_adev->ptr) + *mqnic_adev->ptr = NULL; + kfree(mqnic_adev); +} +#endif + +static int mqnic_common_probe(struct mqnic_dev *mqnic) +{ + int ret = 0; + struct devlink *devlink = priv_to_devlink(mqnic); + struct device *dev = mqnic->dev; + struct mqnic_reg_block *rb; + struct rtc_time tm; + + int k = 0; + + devlink_register(devlink); + + // Enumerate registers + mqnic->rb_list = mqnic_enumerate_reg_block_list(mqnic->hw_addr, 0, mqnic->hw_regs_size); + if (!mqnic->rb_list) { + dev_err(dev, "Failed to enumerate blocks"); + return -EIO; + } + + dev_info(dev, "Device-level register blocks:"); + for (rb = mqnic->rb_list; rb->regs; rb++) + dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24, + (rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff); + + // Read ID registers + mqnic->fw_id_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_FW_ID_TYPE, MQNIC_RB_FW_ID_VER, 0); + + if (!mqnic->fw_id_rb) { + ret = -EIO; + dev_err(dev, "Error: FW ID block not found"); + goto fail_rb_init; + } + + mqnic->fpga_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FPGA_ID); + mqnic->fw_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FW_ID); + mqnic->fw_ver = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FW_VER); + mqnic->board_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BOARD_ID); + mqnic->board_ver = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BOARD_VER); + mqnic->build_date = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BUILD_DATE); + mqnic->git_hash = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_GIT_HASH); + mqnic->rel_info = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_REL_INFO); + + rtc_time64_to_tm(mqnic->build_date, &tm); + + dev_info(dev, "FPGA ID: 0x%08x", mqnic->fpga_id); + dev_info(dev, "FW ID: 0x%08x", mqnic->fw_id); + dev_info(dev, "FW version: %d.%d.%d.%d", mqnic->fw_ver >> 24, + (mqnic->fw_ver >> 16) & 0xff, + (mqnic->fw_ver >> 8) & 0xff, + mqnic->fw_ver & 0xff); + dev_info(dev, "Board ID: 0x%08x", mqnic->board_id); + dev_info(dev, "Board version: %d.%d.%d.%d", mqnic->board_ver >> 24, + (mqnic->board_ver >> 16) & 0xff, + (mqnic->board_ver >> 8) & 0xff, + mqnic->board_ver & 0xff); + + snprintf(mqnic->build_date_str, sizeof(mqnic->build_date_str), "%ptRd %ptRt", &tm, &tm); + + dev_info(dev, "Build date: %s UTC (raw: 0x%08x)", mqnic->build_date_str, mqnic->build_date); + dev_info(dev, "Git hash: %08x", mqnic->git_hash); + dev_info(dev, "Release info: %08x", mqnic->rel_info); + + rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_APP_INFO_TYPE, MQNIC_RB_APP_INFO_VER, 0); + + if (rb) { + mqnic->app_id = ioread32(rb->regs + MQNIC_RB_APP_INFO_REG_ID); + dev_info(dev, "Application ID: 0x%08x", mqnic->app_id); + + if (!mqnic->app_hw_addr) + dev_warn(dev, "Warning: application section present, but application BAR not mapped"); + } + + mqnic_clk_info_init(mqnic); + mqnic_stats_init(mqnic); + + mqnic->phc_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_PHC_TYPE, MQNIC_RB_PHC_VER, 0); + + // Enumerate interfaces + mqnic->if_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_IF_TYPE, MQNIC_RB_IF_VER, 0); + + if (!mqnic->if_rb) { + ret = -EIO; + dev_err(dev, "Error: interface block not found"); + goto fail_rb_init; + } + + mqnic->if_offset = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_OFFSET); + mqnic->if_count = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_COUNT); + mqnic->if_stride = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_STRIDE); + mqnic->if_csr_offset = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_CSR_OFFSET); + + dev_info(dev, "IF offset: 0x%08x", mqnic->if_offset); + dev_info(dev, "IF count: %d", mqnic->if_count); + dev_info(dev, "IF stride: 0x%08x", mqnic->if_stride); + dev_info(dev, "IF CSR offset: 0x%08x", mqnic->if_csr_offset); + + // check BAR size + if (mqnic->if_count * mqnic->if_stride > mqnic->hw_regs_size) { + ret = -EIO; + dev_err(dev, "Invalid BAR configuration (%d IF * 0x%x > 0x%llx)", + mqnic->if_count, mqnic->if_stride, (unsigned long long)mqnic->hw_regs_size); + goto fail_bar_size; + } + + if (mqnic->pfdev) { +#ifdef CONFIG_OF + ret = mqnic_platform_get_mac_address(mqnic); + if (ret) + goto fail_board; + + ret = mqnic_platform_module_eeprom_get(mqnic); + if (ret) + goto fail_board; +#endif + } else { + // Board-specific init + ret = mqnic_board_init(mqnic); + if (ret) { + dev_err(dev, "Failed to initialize board"); + goto fail_board; + } + } + + // register PHC + if (mqnic->phc_rb) + mqnic_register_phc(mqnic); + + mutex_init(&mqnic->state_lock); + + // Set up interfaces + mqnic->phys_port_max = 0; + + mqnic->if_count = min_t(u32, mqnic->if_count, MQNIC_MAX_IF); + + for (k = 0; k < mqnic->if_count; k++) { + struct mqnic_if *interface; + + dev_info(dev, "Creating interface %d", k); + interface = mqnic_create_interface(mqnic, k, mqnic->hw_addr + k * mqnic->if_stride); + if (IS_ERR_OR_NULL(interface)) { + dev_err(dev, "Failed to create interface: %d", ret); + goto fail_create_if; + } + mqnic->interface[k] = interface; + } + + // pass module I2C clients to interface instances + for (k = 0; k < mqnic->if_count; k++) { + struct mqnic_priv *priv; + struct mqnic_if *interface = mqnic->interface[k]; + + interface->mod_i2c_client = mqnic->mod_i2c_client[k]; + + list_for_each_entry(priv, &interface->ndev_list, ndev_list) + priv->mod_i2c_client = mqnic->mod_i2c_client[k]; + } + +fail_create_if: + mqnic->misc_dev.minor = MISC_DYNAMIC_MINOR; + mqnic->misc_dev.name = mqnic->name; + mqnic->misc_dev.fops = &mqnic_fops; + mqnic->misc_dev.parent = dev; + + ret = misc_register(&mqnic->misc_dev); + if (ret) { + mqnic->misc_dev.this_device = NULL; + dev_err(dev, "misc_register failed: %d\n", ret); + goto fail_miscdev; + } + + dev_info(dev, "Registered device %s", mqnic->name); + +#ifdef CONFIG_AUXILIARY_BUS + if (mqnic->app_id) { + mqnic->app_adev = kzalloc(sizeof(*mqnic->app_adev), GFP_KERNEL); + if (!mqnic->app_adev) { + ret = -ENOMEM; + goto fail_adev; + } + + snprintf(mqnic->app_adev->name, sizeof(mqnic->app_adev->name), "app_%08x", mqnic->app_id); + + mqnic->app_adev->adev.id = mqnic->id; + mqnic->app_adev->adev.name = mqnic->app_adev->name; + mqnic->app_adev->adev.dev.parent = dev; + mqnic->app_adev->adev.dev.release = mqnic_adev_release; + mqnic->app_adev->mdev = mqnic; + mqnic->app_adev->ptr = &mqnic->app_adev; + + ret = auxiliary_device_init(&mqnic->app_adev->adev); + if (ret) { + kfree(mqnic->app_adev); + mqnic->app_adev = NULL; + goto fail_adev; + } + + ret = auxiliary_device_add(&mqnic->app_adev->adev); + if (ret) { + auxiliary_device_uninit(&mqnic->app_adev->adev); + mqnic->app_adev = NULL; + goto fail_adev; + } + + dev_info(dev, "Registered auxiliary bus device " DRIVER_NAME ".%s.%d", + mqnic->app_adev->adev.name, mqnic->app_adev->adev.id); + } +#endif + + // probe complete + return 0; + + // error handling +#ifdef CONFIG_AUXILIARY_BUS +fail_adev: +#endif +fail_miscdev: +fail_board: +fail_bar_size: +fail_rb_init: + mqnic_common_remove(mqnic); + return ret; +} + +static void mqnic_common_remove(struct mqnic_dev *mqnic) +{ + struct devlink *devlink = priv_to_devlink(mqnic); + int k = 0; + +#ifdef CONFIG_AUXILIARY_BUS + if (mqnic->app_adev) { + auxiliary_device_delete(&mqnic->app_adev->adev); + auxiliary_device_uninit(&mqnic->app_adev->adev); + } +#endif + + if (mqnic->misc_dev.this_device) + misc_deregister(&mqnic->misc_dev); + + for (k = 0; k < ARRAY_SIZE(mqnic->interface); k++) { + if (mqnic->interface[k]) { + mqnic_destroy_interface(mqnic->interface[k]); + mqnic->interface[k] = NULL; + } + } + + mqnic_unregister_phc(mqnic); + if (mqnic->pfdev) { +#ifdef CONFIG_OF + mqnic_platform_module_eeprom_put(mqnic); +#endif + } else { + mqnic_board_deinit(mqnic); + } + if (mqnic->rb_list) + mqnic_free_reg_block_list(mqnic->rb_list); + + devlink_unregister(devlink); +} + +#ifdef CONFIG_CORUNDUM_MQNIC_PCI +static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret = 0; + struct mqnic_dev *mqnic; + struct devlink *devlink; + struct device *dev = &pdev->dev; + struct pci_dev *bridge = pci_upstream_bridge(pdev); + + dev_info(dev, DRIVER_NAME " PCI probe"); + dev_info(dev, " Vendor: 0x%04x", pdev->vendor); + dev_info(dev, " Device: 0x%04x", pdev->device); + dev_info(dev, " Subsystem vendor: 0x%04x", pdev->subsystem_vendor); + dev_info(dev, " Subsystem device: 0x%04x", pdev->subsystem_device); + dev_info(dev, " Class: 0x%06x", pdev->class); + dev_info(dev, " PCI ID: %04x:%02x:%02x.%d", pci_domain_nr(pdev->bus), + pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + if (pdev->pcie_cap) { + u16 devctl; + u32 lnkcap; + u16 lnkctl; + u16 lnksta; + + pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_DEVCTL, &devctl); + pci_read_config_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCAP, &lnkcap); + pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKCTL, &lnkctl); + pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKSTA, &lnksta); + + dev_info(dev, " Max payload size: %d bytes", + 128 << ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5)); + dev_info(dev, " Max read request size: %d bytes", + 128 << ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12)); + dev_info(dev, " Read completion boundary: %d bytes", + lnkctl & PCI_EXP_LNKCTL_RCB ? 128 : 64); + dev_info(dev, " Link capability: gen %d x%d", + lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4); + dev_info(dev, " Link status: gen %d x%d", + lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4); + dev_info(dev, " Relaxed ordering: %s", + devctl & PCI_EXP_DEVCTL_RELAX_EN ? "enabled" : "disabled"); + dev_info(dev, " Phantom functions: %s", + devctl & PCI_EXP_DEVCTL_PHANTOM ? "enabled" : "disabled"); + dev_info(dev, " Extended tags: %s", + devctl & PCI_EXP_DEVCTL_EXT_TAG ? "enabled" : "disabled"); + dev_info(dev, " No snoop: %s", + devctl & PCI_EXP_DEVCTL_NOSNOOP_EN ? "enabled" : "disabled"); + } + +#ifdef CONFIG_NUMA + dev_info(dev, " NUMA node: %d", pdev->dev.numa_node); +#endif + + if (bridge) { + dev_info(dev, " PCI ID (bridge): %04x:%02x:%02x.%d", pci_domain_nr(bridge->bus), + bridge->bus->number, PCI_SLOT(bridge->devfn), PCI_FUNC(bridge->devfn)); + } + + if (bridge && bridge->pcie_cap) { + u32 lnkcap; + u16 lnksta; + + pci_read_config_dword(bridge, bridge->pcie_cap + PCI_EXP_LNKCAP, &lnkcap); + pci_read_config_word(bridge, bridge->pcie_cap + PCI_EXP_LNKSTA, &lnksta); + + dev_info(dev, " Link capability (bridge): gen %d x%d", + lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4); + dev_info(dev, " Link status (bridge): gen %d x%d", + lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4); + } + + pcie_print_link_status(pdev); + + devlink = mqnic_devlink_alloc(dev); + if (!devlink) + return -ENOMEM; + + mqnic = devlink_priv(devlink); + mqnic->dev = dev; + mqnic->pdev = pdev; + pci_set_drvdata(pdev, mqnic); + + // assign ID and add to list + mqnic_assign_id(mqnic); + + // Disable ASPM + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); + + // Enable device + ret = pci_enable_device_mem(pdev); + if (ret) { + dev_err(dev, "Failed to enable PCI device"); + goto fail_enable_device; + } + + // Set DMA properties + ret = mqnic_common_setdma(mqnic); + if (ret) + goto fail_regions; + + // Reserve regions + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret) { + dev_err(dev, "Failed to reserve regions"); + goto fail_regions; + } + + mqnic->hw_regs_size = pci_resource_len(pdev, 0); + mqnic->hw_regs_phys = pci_resource_start(pdev, 0); + mqnic->app_hw_regs_size = pci_resource_len(pdev, 2); + mqnic->app_hw_regs_phys = pci_resource_start(pdev, 2); + mqnic->ram_hw_regs_size = pci_resource_len(pdev, 4); + mqnic->ram_hw_regs_phys = pci_resource_start(pdev, 4); + + // Map BARs + dev_info(dev, "Control BAR size: %llu", (unsigned long long)mqnic->hw_regs_size); + mqnic->hw_addr = pci_ioremap_bar(pdev, 0); + if (!mqnic->hw_addr) { + ret = -ENOMEM; + dev_err(dev, "Failed to map control BAR"); + goto fail_map_bars; + } + + if (mqnic->app_hw_regs_size) { + dev_info(dev, "Application BAR size: %llu", (unsigned long long)mqnic->app_hw_regs_size); + mqnic->app_hw_addr = pci_ioremap_bar(pdev, 2); + if (!mqnic->app_hw_addr) { + ret = -ENOMEM; + dev_err(dev, "Failed to map application BAR"); + goto fail_map_bars; + } + } + + if (mqnic->ram_hw_regs_size) { + dev_info(dev, "RAM BAR size: %llu", (unsigned long long)mqnic->ram_hw_regs_size); + mqnic->ram_hw_addr = pci_ioremap_bar(pdev, 4); + if (!mqnic->ram_hw_addr) { + ret = -ENOMEM; + dev_err(dev, "Failed to map RAM BAR"); + goto fail_map_bars; + } + } + + // Check if device needs to be reset + if (ioread32(mqnic->hw_addr + 4) == 0xffffffff) { + ret = -EIO; + dev_err(dev, "Device needs to be reset"); + goto fail_reset; + } + + // Set up interrupts + ret = mqnic_irq_init_pcie(mqnic); + if (ret) { + dev_err(dev, "Failed to set up interrupts"); + goto fail_init_irq; + } + + // Enable bus mastering for DMA + pci_set_master(pdev); + + // Common init + ret = mqnic_common_probe(mqnic); + if (ret) + goto fail_common; + + // probe complete + return 0; + + // error handling +fail_common: + pci_clear_master(pdev); + mqnic_irq_deinit_pcie(mqnic); +fail_reset: +fail_init_irq: +fail_map_bars: + if (mqnic->hw_addr) + pci_iounmap(pdev, mqnic->hw_addr); + if (mqnic->app_hw_addr) + pci_iounmap(pdev, mqnic->app_hw_addr); + if (mqnic->ram_hw_addr) + pci_iounmap(pdev, mqnic->ram_hw_addr); + pci_release_regions(pdev); +fail_regions: + pci_disable_device(pdev); +fail_enable_device: + mqnic_free_id(mqnic); + mqnic_devlink_free(devlink); + return ret; +} + +static void mqnic_pci_remove(struct pci_dev *pdev) +{ + struct mqnic_dev *mqnic = pci_get_drvdata(pdev); + struct devlink *devlink = priv_to_devlink(mqnic); + + dev_info(&pdev->dev, DRIVER_NAME " PCI remove"); + + mqnic_common_remove(mqnic); + + pci_clear_master(pdev); + mqnic_irq_deinit_pcie(mqnic); + if (mqnic->hw_addr) + pci_iounmap(pdev, mqnic->hw_addr); + if (mqnic->app_hw_addr) + pci_iounmap(pdev, mqnic->app_hw_addr); + if (mqnic->ram_hw_addr) + pci_iounmap(pdev, mqnic->ram_hw_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + mqnic_free_id(mqnic); + mqnic_devlink_free(devlink); +} + +static void mqnic_pci_shutdown(struct pci_dev *pdev) +{ + dev_info(&pdev->dev, DRIVER_NAME " PCI shutdown"); + + mqnic_pci_remove(pdev); +} + +static struct pci_driver mqnic_pci_driver = { + .name = DRIVER_NAME, + .id_table = mqnic_pci_id_table, + .probe = mqnic_pci_probe, + .remove = mqnic_pci_remove, + .shutdown = mqnic_pci_shutdown +}; +#endif /* CONFIG_CORUNDUM_MQNIC_PCI */ + +static int mqnic_platform_probe(struct platform_device *pdev) +{ + int ret; + struct mqnic_dev *mqnic; + struct devlink *devlink; + struct device *dev = &pdev->dev; + struct resource *res; + struct reset_control *rst; + + dev_info(dev, DRIVER_NAME " platform probe"); + +#ifdef CONFIG_NUMA + dev_info(dev, " NUMA node: %d", pdev->dev.numa_node); +#endif + + devlink = mqnic_devlink_alloc(dev); + if (!devlink) + return -ENOMEM; + + mqnic = devlink_priv(devlink); + mqnic->dev = dev; + mqnic->pfdev = pdev; + platform_set_drvdata(pdev, mqnic); + + // assign ID and add to list + mqnic_assign_id(mqnic); + + // Set DMA properties + ret = mqnic_common_setdma(mqnic); + if (ret) + goto fail; + + // Reset device + rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(rst)) { + dev_warn(dev, "Cannot control device reset"); + } else { + dev_info(dev, "Resetting device"); + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + } + + // Reserve and map regions + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mqnic->hw_regs_size = resource_size(res); + mqnic->hw_regs_phys = res->start; + + dev_info(dev, "Control BAR size: %llu", (unsigned long long)mqnic->hw_regs_size); + mqnic->hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mqnic->hw_addr)) { + ret = PTR_ERR(mqnic->hw_addr); + dev_err(dev, "Failed to map control BAR"); + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + void __iomem *hw_addr; + + mqnic->app_hw_regs_size = resource_size(res); + mqnic->app_hw_regs_phys = res->start; + + dev_info(dev, "Application BAR size: %llu", (unsigned long long)mqnic->app_hw_regs_size); + hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw_addr)) { + ret = PTR_ERR(hw_addr); + dev_err(dev, "Failed to map application BAR"); + goto fail; + } + mqnic->app_hw_addr = hw_addr; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (res) { + void __iomem *hw_addr; + + mqnic->ram_hw_regs_size = resource_size(res); + mqnic->ram_hw_regs_phys = res->start; + + dev_info(dev, "RAM BAR size: %llu", (unsigned long long)mqnic->ram_hw_regs_size); + hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw_addr)) { + ret = PTR_ERR(hw_addr); + dev_err(dev, "Failed to map RAM BAR"); + goto fail; + } + mqnic->ram_hw_addr = hw_addr; + } + + // Set up interrupts + ret = mqnic_irq_init_platform(mqnic); + if (ret) { + dev_err(dev, "Failed to set up interrupts"); + goto fail; + } + + // Common init + ret = mqnic_common_probe(mqnic); + if (ret) + goto fail; + + // probe complete + return 0; + + // error handling +fail: + mqnic_free_id(mqnic); + mqnic_devlink_free(devlink); + return ret; +} + +static void mqnic_platform_remove(struct platform_device *pdev) +{ + struct mqnic_dev *mqnic = platform_get_drvdata(pdev); + struct devlink *devlink = priv_to_devlink(mqnic); + + dev_info(&pdev->dev, DRIVER_NAME " platform remove"); + + mqnic_common_remove(mqnic); + + mqnic_free_id(mqnic); + mqnic_devlink_free(devlink); +} + +static struct platform_driver mqnic_platform_driver = { + .probe = mqnic_platform_probe, + .remove = mqnic_platform_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(mqnic_of_id_table), + }, +}; + +static int __init mqnic_init(void) +{ + int rc; + +#ifdef CONFIG_CORUNDUM_MQNIC_PCI + rc = pci_register_driver(&mqnic_pci_driver); + if (rc) + return rc; +#endif + + rc = platform_driver_register(&mqnic_platform_driver); + if (rc) + goto err; + + return 0; + +err: +#ifdef CONFIG_CORUNDUM_MQNIC_PCI + pci_unregister_driver(&mqnic_pci_driver); +#endif + return rc; +} + +static void __exit mqnic_exit(void) +{ + platform_driver_unregister(&mqnic_platform_driver); + +#ifdef CONFIG_CORUNDUM_MQNIC_PCI + pci_unregister_driver(&mqnic_pci_driver); +#endif +} + +module_init(mqnic_init); +module_exit(mqnic_exit); + +MODULE_DESCRIPTION("mqnic driver"); +MODULE_AUTHOR("Alex Forencich"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/net/mqnic/mqnic_netdev.c b/drivers/net/mqnic/mqnic_netdev.c new file mode 100644 index 00000000000000..bd93aba441207a --- /dev/null +++ b/drivers/net/mqnic/mqnic_netdev.c @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +#include + +int mqnic_start_port(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_if *iface = priv->interface; + struct mqnic_ring *q; + struct mqnic_cq *cq; + struct mqnic_eq *eq; + struct radix_tree_iter iter; + void **slot; + int k; + int ret; + u32 desc_block_size; + + netdev_info(ndev, "%s on interface %d", __func__, iface->index); + + netif_set_real_num_tx_queues(ndev, priv->txq_count); + netif_set_real_num_rx_queues(ndev, priv->rxq_count); + + desc_block_size = min_t(u32, priv->interface->max_desc_block_size, 4); + + // allocate scheduler port + priv->sched_port = mqnic_interface_alloc_sched_port(iface); + if (!priv->sched_port) { + ret = -ENOMEM; + goto fail; + } + + // set up RX queues + for (k = 0; k < priv->rxq_count; k++) { + // create CQ + cq = mqnic_create_cq(iface); + if (IS_ERR_OR_NULL(cq)) { + ret = PTR_ERR(cq); + goto fail; + } + + rcu_read_lock(); + eq = radix_tree_lookup(&iface->eq_table, k % iface->eq_count); + rcu_read_unlock(); + + ret = mqnic_open_cq(cq, eq, priv->rx_ring_size); + if (ret) { + mqnic_destroy_cq(cq); + goto fail; + } + + netif_napi_add(ndev, &cq->napi, mqnic_poll_rx_cq); + + napi_enable(&cq->napi); + + mqnic_arm_cq(cq); + + // create RX queue + q = mqnic_create_rx_ring(iface); + if (IS_ERR_OR_NULL(q)) { + ret = PTR_ERR(q); + mqnic_destroy_cq(cq); + goto fail; + } + + q->mtu = ndev->mtu; + if (ndev->mtu + ETH_HLEN <= PAGE_SIZE) + q->page_order = 0; + else + q->page_order = ilog2((ndev->mtu + ETH_HLEN + PAGE_SIZE - 1) / PAGE_SIZE - 1) + 1; + + ret = mqnic_open_rx_ring(q, priv, cq, priv->rx_ring_size, 1); + if (ret) { + mqnic_destroy_rx_ring(q); + mqnic_destroy_cq(cq); + goto fail; + } + + down_write(&priv->rxq_table_sem); + ret = radix_tree_insert(&priv->rxq_table, k, q); + up_write(&priv->rxq_table_sem); + if (ret) { + mqnic_destroy_rx_ring(q); + mqnic_destroy_cq(cq); + goto fail; + } + } + + // set up TX queues + for (k = 0; k < priv->txq_count; k++) { + // create CQ + cq = mqnic_create_cq(iface); + if (IS_ERR_OR_NULL(cq)) { + ret = PTR_ERR(cq); + goto fail; + } + + rcu_read_lock(); + eq = radix_tree_lookup(&iface->eq_table, k % iface->eq_count); + rcu_read_unlock(); + + ret = mqnic_open_cq(cq, eq, priv->tx_ring_size); + if (ret) { + mqnic_destroy_cq(cq); + goto fail; + } + + netif_napi_add_tx(ndev, &cq->napi, mqnic_poll_tx_cq); + + napi_enable(&cq->napi); + + mqnic_arm_cq(cq); + + // create TX queue + q = mqnic_create_tx_ring(iface); + if (IS_ERR_OR_NULL(q)) { + ret = PTR_ERR(q); + mqnic_destroy_cq(cq); + goto fail; + } + + q->tx_queue = netdev_get_tx_queue(ndev, k); + + ret = mqnic_open_tx_ring(q, priv, cq, priv->tx_ring_size, desc_block_size); + if (ret) { + mqnic_destroy_tx_ring(q); + mqnic_destroy_cq(cq); + goto fail; + } + + down_write(&priv->txq_table_sem); + ret = radix_tree_insert(&priv->txq_table, k, q); + up_write(&priv->txq_table_sem); + if (ret) { + mqnic_destroy_tx_ring(q); + mqnic_destroy_cq(cq); + goto fail; + } + } + + // set MTU + mqnic_interface_set_tx_mtu(iface, ndev->mtu + ETH_HLEN); + mqnic_interface_set_rx_mtu(iface, ndev->mtu + ETH_HLEN); + + // configure RX indirection and RSS + mqnic_update_indir_table(ndev); + + priv->port_up = true; + + // enable TX and RX queues + down_read(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_enable_tx_ring(q); + } + up_read(&priv->txq_table_sem); + + down_read(&priv->rxq_table_sem); + radix_tree_for_each_slot(slot, &priv->rxq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_enable_rx_ring(q); + } + up_read(&priv->rxq_table_sem); + + mqnic_port_set_tx_ctrl(priv->port, MQNIC_PORT_TX_CTRL_EN); + + // configure scheduler + down_read(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_sched_port_queue_set_tc(priv->sched_port, q->index, 0); + mqnic_sched_port_queue_enable(priv->sched_port, q->index); + } + up_read(&priv->txq_table_sem); + + // configure scheduler flow control + mqnic_sched_port_channel_set_dest(priv->sched_port, 0, (priv->port->index << 4) | 0); + mqnic_sched_port_channel_set_pkt_budget(priv->sched_port, 0, 1); + mqnic_sched_port_channel_set_data_budget(priv->sched_port, 0, ndev->mtu + ETH_HLEN); + mqnic_sched_port_channel_set_pkt_limit(priv->sched_port, 0, 0xFFFF); + mqnic_sched_port_channel_set_data_limit(priv->sched_port, 0, iface->tx_fifo_depth); + + mqnic_sched_port_channel_enable(priv->sched_port, 0); + + // enable scheduler + mqnic_sched_port_enable(priv->sched_port); + + netif_tx_start_all_queues(ndev); + netif_device_attach(ndev); + + if (mqnic_link_status_poll) { + priv->link_status = 0; + mod_timer(&priv->link_status_timer, + jiffies + msecs_to_jiffies(mqnic_link_status_poll)); + } else { + netif_carrier_on(ndev); + } + + mqnic_port_set_rx_ctrl(priv->port, MQNIC_PORT_RX_CTRL_EN); + + return 0; + +fail: + mqnic_stop_port(ndev); + return ret; +} + +void mqnic_stop_port(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_cq *cq; + struct radix_tree_iter iter; + void **slot; + + netdev_info(ndev, "%s on interface %d", __func__, priv->interface->index); + + if (mqnic_link_status_poll) + del_timer_sync(&priv->link_status_timer); + + mqnic_port_set_rx_ctrl(priv->port, 0); + + netif_tx_lock_bh(ndev); +// if (detach) +// netif_device_detach(ndev); + netif_tx_stop_all_queues(ndev); + netif_tx_unlock_bh(ndev); + + netif_carrier_off(ndev); + netif_tx_disable(ndev); + + spin_lock_bh(&priv->stats_lock); + mqnic_update_stats(ndev); + spin_unlock_bh(&priv->stats_lock); + + if (priv->sched_port) { + down_read(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_sched_port_queue_disable(priv->sched_port, q->index); + } + up_read(&priv->txq_table_sem); + + mqnic_sched_port_channel_disable(priv->sched_port, 0); + mqnic_sched_port_disable(priv->sched_port); + } + + // disable TX and RX queues + down_read(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_disable_tx_ring(q); + } + up_read(&priv->txq_table_sem); + + down_read(&priv->rxq_table_sem); + radix_tree_for_each_slot(slot, &priv->rxq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + mqnic_disable_rx_ring(q); + } + up_read(&priv->rxq_table_sem); + + msleep(20); + + mqnic_port_set_tx_ctrl(priv->port, 0); + + priv->port_up = false; + + // shut down NAPI and clean queues + down_write(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + cq = q->cq; + napi_disable(&cq->napi); + netif_napi_del(&cq->napi); + mqnic_close_tx_ring(q); + mqnic_destroy_tx_ring(q); + radix_tree_delete(&priv->txq_table, iter.index); + mqnic_close_cq(cq); + mqnic_destroy_cq(cq); + } + up_write(&priv->txq_table_sem); + + down_write(&priv->rxq_table_sem); + radix_tree_for_each_slot(slot, &priv->rxq_table, &iter, 0) { + struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + cq = q->cq; + napi_disable(&cq->napi); + netif_napi_del(&cq->napi); + mqnic_close_rx_ring(q); + mqnic_destroy_rx_ring(q); + radix_tree_delete(&priv->rxq_table, iter.index); + mqnic_close_cq(cq); + mqnic_destroy_cq(cq); + } + up_write(&priv->rxq_table_sem); + + // free scheduler port + if (priv->sched_port) + mqnic_interface_free_sched_port(priv->interface, priv->sched_port); + priv->sched_port = NULL; +} + +static int mqnic_open(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_dev *mdev = priv->mdev; + int ret = 0; + + mutex_lock(&mdev->state_lock); + + ret = mqnic_start_port(ndev); + + if (ret) + netdev_err(ndev, "Failed to start port on interface %d: %d", + priv->interface->index, ret); + + mutex_unlock(&mdev->state_lock); + return ret; +} + +static int mqnic_close(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_dev *mdev = priv->mdev; + int ret = 0; + + mutex_lock(&mdev->state_lock); + + mqnic_stop_port(ndev); + + mutex_unlock(&mdev->state_lock); + return ret; +} + +int mqnic_update_indir_table(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_if *iface = priv->interface; + struct mqnic_ring *q; + int k; + + mqnic_interface_set_rx_queue_map_rss_mask(iface, priv->port->index, 0xffffffff); + mqnic_interface_set_rx_queue_map_app_mask(iface, priv->port->index, 0); + + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) { + rcu_read_lock(); + q = radix_tree_lookup(&priv->rxq_table, priv->rx_queue_map_indir_table[k]); + rcu_read_unlock(); + + if (q) + mqnic_interface_set_rx_queue_map_indir_table(iface, priv->port->index, k, q->index); + } + + return 0; +} + +void mqnic_update_stats(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct radix_tree_iter iter; + void **slot; + unsigned long packets, bytes; + unsigned long dropped; + + if (unlikely(!priv->port_up)) + return; + + packets = 0; + bytes = 0; + dropped = 0; + down_read(&priv->rxq_table_sem); + radix_tree_for_each_slot(slot, &priv->rxq_table, &iter, 0) { + const struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + packets += READ_ONCE(q->packets); + bytes += READ_ONCE(q->bytes); + dropped += READ_ONCE(q->dropped_packets); + } + up_read(&priv->rxq_table_sem); + ndev->stats.rx_packets = packets; + ndev->stats.rx_bytes = bytes; + ndev->stats.rx_dropped = dropped; + + packets = 0; + bytes = 0; + dropped = 0; + down_read(&priv->txq_table_sem); + radix_tree_for_each_slot(slot, &priv->txq_table, &iter, 0) { + const struct mqnic_ring *q = (struct mqnic_ring *)*slot; + + packets += READ_ONCE(q->packets); + bytes += READ_ONCE(q->bytes); + dropped += READ_ONCE(q->dropped_packets); + } + up_read(&priv->txq_table_sem); + ndev->stats.tx_packets = packets; + ndev->stats.tx_bytes = bytes; + ndev->stats.tx_dropped = dropped; +} + +static void mqnic_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *stats) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + spin_lock_bh(&priv->stats_lock); + mqnic_update_stats(ndev); + netdev_stats_to_stats64(stats, &ndev->stats); + spin_unlock_bh(&priv->stats_lock); +} + +static int mqnic_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct hwtstamp_config hwts_config; + + if (copy_from_user(&hwts_config, ifr->ifr_data, sizeof(hwts_config))) + return -EFAULT; + + if (hwts_config.flags) + return -EINVAL; + + switch (hwts_config.tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + switch (hwts_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + hwts_config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + memcpy(&priv->hwts_config, &hwts_config, sizeof(hwts_config)); + + if (copy_to_user(ifr->ifr_data, &hwts_config, sizeof(hwts_config))) + return -EFAULT; + + return 0; +} + +static int mqnic_hwtstamp_get(struct net_device *ndev, struct ifreq *ifr) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + if (copy_to_user(ifr->ifr_data, &priv->hwts_config, sizeof(priv->hwts_config))) + return -EFAULT; + + return 0; +} + +static int mqnic_set_mac(struct net_device *ndev, void *addr) +{ + struct sockaddr *saddr = addr; + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + netif_addr_lock_bh(ndev); + + eth_hw_addr_set(ndev, saddr->sa_data); + + netif_addr_unlock_bh(ndev); + + return 0; +} + +static int mqnic_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_dev *mdev = priv->mdev; + + if (new_mtu < ndev->min_mtu || new_mtu > ndev->max_mtu) { + netdev_err(ndev, "Bad MTU: %d", new_mtu); + return -EPERM; + } + + netdev_info(ndev, "New MTU: %d", new_mtu); + + ndev->mtu = new_mtu; + + if (netif_running(ndev)) { + mutex_lock(&mdev->state_lock); + + mqnic_stop_port(ndev); + mqnic_start_port(ndev); + + mutex_unlock(&mdev->state_lock); + } + + return 0; +} + +static int mqnic_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return mqnic_hwtstamp_set(ndev, ifr); + case SIOCGHWTSTAMP: + return mqnic_hwtstamp_get(ndev, ifr); + default: + return -EOPNOTSUPP; + } +} + +static const struct net_device_ops mqnic_netdev_ops = { + .ndo_open = mqnic_open, + .ndo_stop = mqnic_close, + .ndo_start_xmit = mqnic_start_xmit, + .ndo_get_stats64 = mqnic_get_stats64, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = mqnic_set_mac, + .ndo_change_mtu = mqnic_change_mtu, + .ndo_eth_ioctl = mqnic_ioctl, +}; + +static void mqnic_link_status_timeout(struct timer_list *timer) +{ + struct mqnic_priv *priv = from_timer(priv, timer, link_status_timer); + unsigned int up = 1; + + if (!(mqnic_port_get_tx_ctrl(priv->port) & MQNIC_PORT_TX_CTRL_STATUS)) + up = 0; + if (!(mqnic_port_get_rx_ctrl(priv->port) & MQNIC_PORT_RX_CTRL_STATUS)) + up = 0; + + if (up) { + if (!priv->link_status) { + netif_carrier_on(priv->ndev); + priv->link_status = !priv->link_status; + } + } else { + if (priv->link_status) { + netif_carrier_off(priv->ndev); + priv->link_status = !priv->link_status; + } + } + + mod_timer(&priv->link_status_timer, jiffies + msecs_to_jiffies(mqnic_link_status_poll)); +} + +struct net_device *mqnic_create_netdev(struct mqnic_if *interface, struct mqnic_port *port) +{ + struct mqnic_dev *mdev = interface->mdev; + struct device *dev = interface->dev; + struct net_device *ndev; + struct mqnic_priv *priv; + int ret = 0; + int k; + u32 desc_block_size; + + ndev = alloc_etherdev_mqs(sizeof(*priv), mqnic_res_get_count(interface->txq_res), + mqnic_res_get_count(interface->rxq_res)); + if (!ndev) { + dev_err(dev, "Failed to allocate memory"); + return ERR_PTR(-ENOMEM); + } + + SET_NETDEV_DEV(ndev, dev); + + SET_NETDEV_DEVLINK_PORT(ndev, &port->dl_port); + + ndev->dev_port = port->phys_index; + + // init private data + priv = netdev_priv(ndev); + memset(priv, 0, sizeof(struct mqnic_priv)); + + spin_lock_init(&priv->stats_lock); + + priv->ndev = ndev; + priv->mdev = interface->mdev; + priv->dl_port = &port->dl_port; + priv->interface = interface; + priv->dev = dev; + priv->port = port; + priv->port_up = false; + priv->sched_port = NULL; + + // associate interface resources + priv->if_features = interface->if_features; + + priv->txq_count = min_t(u32, mqnic_res_get_count(interface->txq_res) / interface->port_count, 256); + priv->rxq_count = min_t(u32, mqnic_res_get_count(interface->rxq_res) / interface->port_count, num_online_cpus()); + + priv->tx_ring_size = roundup_pow_of_two(clamp_t(u32, mqnic_num_txq_entries, + MQNIC_MIN_TX_RING_SZ, + MQNIC_MAX_TX_RING_SZ)); + priv->rx_ring_size = roundup_pow_of_two(clamp_t(u32, mqnic_num_rxq_entries, + MQNIC_MIN_RX_RING_SZ, + MQNIC_MAX_RX_RING_SZ)); + + init_rwsem(&priv->txq_table_sem); + INIT_RADIX_TREE(&priv->txq_table, GFP_KERNEL); + + init_rwsem(&priv->rxq_table_sem); + INIT_RADIX_TREE(&priv->rxq_table, GFP_KERNEL); + + netif_set_real_num_tx_queues(ndev, priv->txq_count); + netif_set_real_num_rx_queues(ndev, priv->rxq_count); + + // set MAC + ndev->addr_len = ETH_ALEN; + + if (ndev->dev_port >= mdev->mac_count) { + dev_warn(dev, "Exhausted permanent MAC addresses; using random MAC"); + eth_hw_addr_random(ndev); + } else { + eth_hw_addr_set(ndev, mdev->mac_list[ndev->dev_port]); + + if (!is_valid_ether_addr(ndev->dev_addr)) { + dev_warn(dev, "Invalid MAC address in list; using random MAC"); + eth_hw_addr_random(ndev); + } + } + + priv->hwts_config.flags = 0; + priv->hwts_config.tx_type = HWTSTAMP_TX_OFF; + priv->hwts_config.rx_filter = HWTSTAMP_FILTER_NONE; + + desc_block_size = min_t(u32, interface->max_desc_block_size, 4); + + priv->rx_queue_map_indir_table_size = interface->rx_queue_map_indir_table_size; + priv->rx_queue_map_indir_table = kcalloc(priv->rx_queue_map_indir_table_size, sizeof(u32), GFP_KERNEL); + if (!priv->rx_queue_map_indir_table) { + ret = -ENOMEM; + goto fail; + } + + for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) + priv->rx_queue_map_indir_table[k] = k % priv->rxq_count; + + // entry points + ndev->netdev_ops = &mqnic_netdev_ops; + ndev->ethtool_ops = &mqnic_ethtool_ops; + + // set up features + ndev->hw_features = NETIF_F_SG; + + if (priv->if_features & MQNIC_IF_FEATURE_RX_CSUM) + ndev->hw_features |= NETIF_F_RXCSUM; + + if (priv->if_features & MQNIC_IF_FEATURE_TX_CSUM) + ndev->hw_features |= NETIF_F_HW_CSUM; + + ndev->features = ndev->hw_features | NETIF_F_HIGHDMA; + ndev->hw_features |= 0; + + ndev->min_mtu = ETH_MIN_MTU; + ndev->max_mtu = 1500; + + if (interface->max_tx_mtu && interface->max_rx_mtu) + ndev->max_mtu = min(interface->max_tx_mtu, interface->max_rx_mtu) - ETH_HLEN; + + netif_carrier_off(ndev); + if (mqnic_link_status_poll) + timer_setup(&priv->link_status_timer, mqnic_link_status_timeout, 0); + + ret = register_netdev(ndev); + if (ret) { + dev_err(dev, "netdev registration failed on interface %d: %d", + priv->interface->index, ret); + goto fail; + } + + priv->registered = 1; + + return ndev; + +fail: + mqnic_destroy_netdev(ndev); + return ERR_PTR(ret); +} + +void mqnic_destroy_netdev(struct net_device *ndev) +{ + struct mqnic_priv *priv = netdev_priv(ndev); + + if (priv->registered) + unregister_netdev(ndev); + + kfree(priv->rx_queue_map_indir_table); + + free_netdev(ndev); +} diff --git a/drivers/net/mqnic/mqnic_port.c b/drivers/net/mqnic/mqnic_port.c new file mode 100644 index 00000000000000..908d6cc267270e --- /dev/null +++ b/drivers/net/mqnic/mqnic_port.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +#include + +struct mqnic_port *mqnic_create_port(struct mqnic_if *interface, int index, + int phys_index, struct mqnic_reg_block *port_rb) +{ + struct device *dev = interface->dev; + struct devlink *devlink = priv_to_devlink(interface->mdev); + struct devlink_port_attrs attrs = {}; + struct mqnic_port *port; + struct mqnic_reg_block *rb; + u32 offset; + int ret = 0; + int k; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + attrs.phys.port_number = phys_index; + devlink_port_attrs_set(&port->dl_port, &attrs); + + ret = devlink_port_register(devlink, &port->dl_port, phys_index); + if (ret) { + kfree(port); + return ERR_PTR(ret); + } + + port->dev = dev; + port->interface = interface; + + port->index = index; + port->phys_index = phys_index; + + port->port_rb = port_rb; + + offset = ioread32(port_rb->regs + MQNIC_RB_SCHED_BLOCK_REG_OFFSET); + + port->rb_list = mqnic_enumerate_reg_block_list(interface->hw_addr, offset, interface->hw_regs_size - offset); + + if (!port->rb_list) { + ret = -EIO; + dev_err(dev, "Failed to enumerate blocks"); + goto fail; + } + + dev_info(dev, "Port-level register blocks:"); + for (rb = port->rb_list; rb->regs; rb++) + dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24, + (rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff); + + port->port_ctrl_rb = mqnic_find_reg_block(port->rb_list, MQNIC_RB_PORT_CTRL_TYPE, MQNIC_RB_PORT_CTRL_VER, 0); + + if (!port->port_ctrl_rb) { + ret = -EIO; + dev_err(dev, "Port control register block not found"); + goto fail; + } + + port->port_features = ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_FEATURES); + + dev_info(dev, "Port features: 0x%08x", port->port_features); + + mqnic_port_set_tx_ctrl(port, 0); + mqnic_port_set_rx_ctrl(port, 0); + mqnic_port_set_lfc_ctrl(port, interface->max_rx_mtu * 2); + + for (k = 0; k < 8; k++) + mqnic_port_set_pfc_ctrl(port, k, 0); + + dev_info(dev, "Port RX ctrl: 0x%08x", mqnic_port_get_rx_ctrl(port)); + dev_info(dev, "Port TX ctrl: 0x%08x", mqnic_port_get_tx_ctrl(port)); + + return port; + +fail: + mqnic_destroy_port(port); + return ERR_PTR(ret); +} + +void mqnic_destroy_port(struct mqnic_port *port) +{ + if (port->rb_list) + mqnic_free_reg_block_list(port->rb_list); + + devlink_port_unregister(&port->dl_port); + + kfree(port); +} + +u32 mqnic_port_get_tx_ctrl(struct mqnic_port *port) +{ + return ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_TX_CTRL); +} +EXPORT_SYMBOL(mqnic_port_get_tx_ctrl); + +void mqnic_port_set_tx_ctrl(struct mqnic_port *port, u32 val) +{ + iowrite32(val, port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_TX_CTRL); +} +EXPORT_SYMBOL(mqnic_port_set_tx_ctrl); + +u32 mqnic_port_get_rx_ctrl(struct mqnic_port *port) +{ + return ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_RX_CTRL); +} +EXPORT_SYMBOL(mqnic_port_get_rx_ctrl); + +void mqnic_port_set_rx_ctrl(struct mqnic_port *port, u32 val) +{ + iowrite32(val, port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_RX_CTRL); +} +EXPORT_SYMBOL(mqnic_port_set_rx_ctrl); + +u32 mqnic_port_get_fc_ctrl(struct mqnic_port *port) +{ + return ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_FC_CTRL); +} +EXPORT_SYMBOL(mqnic_port_get_fc_ctrl); + +void mqnic_port_set_fc_ctrl(struct mqnic_port *port, u32 val) +{ + iowrite32(val, port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_FC_CTRL); +} +EXPORT_SYMBOL(mqnic_port_set_fc_ctrl); + +u32 mqnic_port_get_lfc_ctrl(struct mqnic_port *port) +{ + return ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_LFC_CTRL); +} +EXPORT_SYMBOL(mqnic_port_get_lfc_ctrl); + +void mqnic_port_set_lfc_ctrl(struct mqnic_port *port, u32 val) +{ + iowrite32(val, port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_LFC_CTRL); +} +EXPORT_SYMBOL(mqnic_port_set_lfc_ctrl); + +u32 mqnic_port_get_pfc_ctrl(struct mqnic_port *port, int index) +{ + return ioread32(port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 + index * 4); +} +EXPORT_SYMBOL(mqnic_port_get_pfc_ctrl); + +void mqnic_port_set_pfc_ctrl(struct mqnic_port *port, int index, u32 val) +{ + iowrite32(val, port->port_ctrl_rb->regs + MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 + index * 4); +} +EXPORT_SYMBOL(mqnic_port_set_pfc_ctrl); diff --git a/drivers/net/mqnic/mqnic_ptp.c b/drivers/net/mqnic/mqnic_ptp.c new file mode 100644 index 00000000000000..73bc4fc88ba818 --- /dev/null +++ b/drivers/net/mqnic/mqnic_ptp.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" +#include + +ktime_t mqnic_read_cpl_ts(struct mqnic_dev *mdev, struct mqnic_ring *ring, + const struct mqnic_cpl *cpl) +{ + u64 ts_s = le16_to_cpu(cpl->ts_s); + u32 ts_ns = le32_to_cpu(cpl->ts_ns); + + if (unlikely(!ring->ts_valid || (ring->ts_s ^ ts_s) & 0xff00)) { + // seconds MSBs do not match, update cached timestamp + if (mdev->phc_rb) { + ring->ts_s = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_CUR_TOD_SEC_L); + ring->ts_s |= (u64)ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_CUR_TOD_SEC_H) << 32; + ring->ts_valid = 1; + } + } + + ts_s |= ring->ts_s & 0xffffffffffffff00; + + return ktime_set(ts_s, ts_ns); +} + +static int mqnic_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + + bool neg = false; + u64 nom_per_fns, adj; + + dev_dbg(mdev->dev, "%s: scaled_ppm: %ld", __func__, scaled_ppm); + + if (scaled_ppm < 0) { + neg = true; + scaled_ppm = -scaled_ppm; + } + + nom_per_fns = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_NOM_PERIOD_FNS); + nom_per_fns |= (u64)ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_NOM_PERIOD_NS) << 32; + + if (nom_per_fns == 0) + nom_per_fns = 0x4ULL << 32; + + adj = div_u64(((nom_per_fns >> 16) * scaled_ppm) + 500000, 1000000); + + if (neg) + adj = nom_per_fns - adj; + else + adj = nom_per_fns + adj; + + iowrite32(adj & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_PERIOD_FNS); + iowrite32(adj >> 32, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_PERIOD_NS); + + dev_dbg(mdev->dev, "%s adj: 0x%llx", __func__, adj); + + return 0; +} + +static int mqnic_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + + ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_FNS); + ts->tv_nsec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_NS); + ts->tv_sec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L); + ts->tv_sec |= (u64)ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H) << 32; + + return 0; +} + +static int mqnic_phc_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + + ptp_read_system_prets(sts); + ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_FNS); + ptp_read_system_postts(sts); + ts->tv_nsec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_NS); + ts->tv_sec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L); + ts->tv_sec |= (u64)ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H) << 32; + + return 0; +} + +static int mqnic_phc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + + iowrite32(ts->tv_nsec, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_NS); + iowrite32(ts->tv_sec & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_SEC_L); + iowrite32(ts->tv_sec >> 32, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_SEC_H); + + return 0; +} + +static int mqnic_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + struct timespec64 ts; + + dev_dbg(mdev->dev, "%s: delta: %lld", __func__, delta); + + if (delta > 536000000 || delta < -536000000) { + mqnic_phc_gettime(ptp, &ts); + ts = timespec64_add(ts, ns_to_timespec64(delta)); + mqnic_phc_settime(ptp, &ts); + } else { + iowrite32(delta & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_OFFSET_TOD_NS); + } + + return 0; +} + +static int mqnic_phc_perout(struct ptp_clock_info *ptp, int on, struct ptp_perout_request *perout) +{ + struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info); + struct mqnic_reg_block *rb; + + u64 start_sec, period_sec, width_sec; + u32 start_nsec, period_nsec, width_nsec; + + rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_PHC_PEROUT_TYPE, + MQNIC_RB_PHC_PEROUT_VER, perout->index); + + if (!rb) + return -EINVAL; + + if (!on) { + iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_CTRL); + + return 0; + } + + start_nsec = perout->start.nsec; + start_sec = start_nsec / NSEC_PER_SEC; + start_nsec -= start_sec * NSEC_PER_SEC; + start_sec += perout->start.sec; + + period_nsec = perout->period.nsec; + period_sec = period_nsec / NSEC_PER_SEC; + period_nsec -= period_sec * NSEC_PER_SEC; + period_sec += perout->period.sec; + + // set width to half of period + width_sec = period_sec >> 1; + width_nsec = (period_nsec + (period_sec & 1 ? NSEC_PER_SEC : 0)) >> 1; + + dev_info(mdev->dev, "%s: start: %lld.%09d", __func__, start_sec, start_nsec); + dev_info(mdev->dev, "%s: period: %lld.%09d", __func__, period_sec, period_nsec); + dev_info(mdev->dev, "%s: width: %lld.%09d", __func__, width_sec, width_nsec); + + iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_FNS); + iowrite32(start_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_NS); + iowrite32(start_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_SEC_L); + iowrite32(start_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_SEC_H); + + iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_FNS); + iowrite32(period_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_NS); + iowrite32(period_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_L); + iowrite32(period_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_H); + + iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_FNS); + iowrite32(width_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_NS); + iowrite32(width_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_L); + iowrite32(width_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_H); + + iowrite32(1, rb->regs + MQNIC_RB_PHC_PEROUT_REG_CTRL); + + return 0; +} + +static int mqnic_phc_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *request, int on) +{ + if (!request) + return -EINVAL; + + switch (request->type) { + case PTP_CLK_REQ_EXTTS: + return -EINVAL; + case PTP_CLK_REQ_PEROUT: + return mqnic_phc_perout(ptp, on, &request->perout); + case PTP_CLK_REQ_PPS: + return -EINVAL; + default: + return -EINVAL; + } +} + +static void mqnic_phc_set_from_system_clock(struct ptp_clock_info *ptp) +{ + struct timespec64 ts; + +#ifdef ktime_get_clocktai_ts64 + ktime_get_clocktai_ts64(&ts); +#else + ts = ktime_to_timespec64(ktime_get_clocktai()); +#endif + + mqnic_phc_settime(ptp, &ts); +} + +void mqnic_register_phc(struct mqnic_dev *mdev) +{ + int perout_ch_count = 0; + struct mqnic_reg_block *rb; + + if (!mdev->phc_rb) { + dev_warn(mdev->dev, "PTP clock not present"); + return; + } + + if (mdev->ptp_clock) { + dev_warn(mdev->dev, "PTP clock already registered"); + return; + } + + // count PTP period output channels + while ((rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_PHC_PEROUT_TYPE, + MQNIC_RB_PHC_PEROUT_VER, perout_ch_count))) { + perout_ch_count++; + } + + mdev->ptp_clock_info.owner = THIS_MODULE; + snprintf(mdev->ptp_clock_info.name, sizeof(mdev->ptp_clock_info.name), + "%s_ptp", mdev->name); + mdev->ptp_clock_info.max_adj = 100000000; + mdev->ptp_clock_info.n_alarm = 0; + mdev->ptp_clock_info.n_ext_ts = 0; + mdev->ptp_clock_info.n_per_out = perout_ch_count; + mdev->ptp_clock_info.n_pins = 0; + mdev->ptp_clock_info.pps = 0; + mdev->ptp_clock_info.adjfine = mqnic_phc_adjfine; + mdev->ptp_clock_info.adjtime = mqnic_phc_adjtime; + mdev->ptp_clock_info.gettime64 = mqnic_phc_gettime; + mdev->ptp_clock_info.gettimex64 = mqnic_phc_gettimex; + mdev->ptp_clock_info.settime64 = mqnic_phc_settime; + mdev->ptp_clock_info.enable = mqnic_phc_enable; + mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info, mdev->dev); + + if (IS_ERR(mdev->ptp_clock)) { + dev_err(mdev->dev, "%s: failed to register PHC (%ld)", __func__, PTR_ERR(mdev->ptp_clock)); + mdev->ptp_clock = NULL; + return; + } + + dev_info(mdev->dev, "registered PHC (index %d)", ptp_clock_index(mdev->ptp_clock)); + + mqnic_phc_set_from_system_clock(&mdev->ptp_clock_info); +} + +void mqnic_unregister_phc(struct mqnic_dev *mdev) +{ + if (mdev->ptp_clock) { + ptp_clock_unregister(mdev->ptp_clock); + mdev->ptp_clock = NULL; + dev_info(mdev->dev, "unregistered PHC"); + } +} diff --git a/drivers/net/mqnic/mqnic_reg_block.c b/drivers/net/mqnic/mqnic_reg_block.c new file mode 100644 index 00000000000000..e250088c747379 --- /dev/null +++ b/drivers/net/mqnic/mqnic_reg_block.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2021-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_reg_block *mqnic_enumerate_reg_block_list(u8 __iomem *base, size_t offset, size_t size) +{ + int max_count = 8; + struct mqnic_reg_block *reg_block_list = kzalloc(max_count * sizeof(struct mqnic_reg_block), GFP_KERNEL); + int count = 0; + int k; + + u8 __iomem *ptr; + + u32 rb_type; + u32 rb_version; + + if (!reg_block_list) + return NULL; + + while (1) { + reg_block_list[count].type = 0; + reg_block_list[count].version = 0; + reg_block_list[count].base = 0; + reg_block_list[count].regs = 0; + + if ((offset == 0 && count != 0) || offset >= size) + break; + + ptr = base + offset; + + for (k = 0; k < count; k++) { + if (ptr == reg_block_list[k].regs) { + pr_err("Register blocks form a loop"); + goto fail; + } + } + + rb_type = ioread32(ptr + MQNIC_RB_REG_TYPE); + rb_version = ioread32(ptr + MQNIC_RB_REG_VER); + offset = ioread32(ptr + MQNIC_RB_REG_NEXT_PTR); + + reg_block_list[count].type = rb_type; + reg_block_list[count].version = rb_version; + reg_block_list[count].base = base; + reg_block_list[count].regs = ptr; + + count++; + + if (count >= max_count) { + struct mqnic_reg_block *tmp; + + max_count += 4; + tmp = krealloc(reg_block_list, max_count * sizeof(struct mqnic_reg_block), GFP_KERNEL); + if (!tmp) + goto fail; + reg_block_list = tmp; + } + } + + return reg_block_list; +fail: + kfree(reg_block_list); + return NULL; +} +EXPORT_SYMBOL(mqnic_enumerate_reg_block_list); + +struct mqnic_reg_block *mqnic_find_reg_block(struct mqnic_reg_block *list, u32 type, u32 version, int index) +{ + struct mqnic_reg_block *rb = list; + + while (rb->regs) { + if (rb->type == type && (!version || rb->version == version)) { + if (index > 0) + index--; + else + return rb; + } + + rb++; + } + + return NULL; +} +EXPORT_SYMBOL(mqnic_find_reg_block); + +void mqnic_free_reg_block_list(struct mqnic_reg_block *list) +{ + kfree(list); +} +EXPORT_SYMBOL(mqnic_free_reg_block_list); diff --git a/drivers/net/mqnic/mqnic_res.c b/drivers/net/mqnic/mqnic_res.c new file mode 100644 index 00000000000000..0a8a70f41e3301 --- /dev/null +++ b/drivers/net/mqnic/mqnic_res.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_res *mqnic_create_res(unsigned int count, u8 __iomem *base, unsigned int stride) +{ + struct mqnic_res *res; + int ret; + + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + res->count = count; + res->base = base; + res->stride = stride; + + spin_lock_init(&res->lock); + + res->bmap = bitmap_zalloc(count, GFP_KERNEL); + if (!res) { + ret = -ENOMEM; + goto fail; + } + + return res; + +fail: + mqnic_destroy_res(res); + return ERR_PTR(ret); +} + +void mqnic_destroy_res(struct mqnic_res *res) +{ + if (!res) + return; + + bitmap_free(res->bmap); + + kfree(res); +} + +int mqnic_res_alloc(struct mqnic_res *res) +{ + int index; + + spin_lock(&res->lock); + index = bitmap_find_free_region(res->bmap, res->count, 0); + spin_unlock(&res->lock); + + return index; +} + +void mqnic_res_free(struct mqnic_res *res, int index) +{ + if (index < 0 || index >= res->count) + return; + + spin_lock(&res->lock); + bitmap_clear(res->bmap, index, 1); + spin_unlock(&res->lock); +} + +unsigned int mqnic_res_get_count(struct mqnic_res *res) +{ + return res->count; +} + +u8 __iomem *mqnic_res_get_addr(struct mqnic_res *res, int index) +{ + if (index < 0 || index >= res->count) + return NULL; + + return res->base + index * res->stride; +} diff --git a/drivers/net/mqnic/mqnic_rx.c b/drivers/net/mqnic/mqnic_rx.c new file mode 100644 index 00000000000000..b25341be255428 --- /dev/null +++ b/drivers/net/mqnic/mqnic_rx.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_ring *mqnic_create_rx_ring(struct mqnic_if *interface) +{ + struct mqnic_ring *ring; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + return ERR_PTR(-ENOMEM); + + ring->dev = interface->dev; + ring->interface = interface; + + ring->index = -1; + ring->enabled = 0; + + ring->hw_addr = NULL; + + ring->prod_ptr = 0; + ring->cons_ptr = 0; + + return ring; +} + +void mqnic_destroy_rx_ring(struct mqnic_ring *ring) +{ + mqnic_close_rx_ring(ring); + + kfree(ring); +} + +int mqnic_open_rx_ring(struct mqnic_ring *ring, struct mqnic_priv *priv, + struct mqnic_cq *cq, int size, int desc_block_size) +{ + int ret = 0; + + if (ring->enabled || ring->hw_addr || ring->buf || !priv || !cq) + return -EINVAL; + + ring->index = mqnic_res_alloc(ring->interface->rxq_res); + if (ring->index < 0) + return -ENOMEM; + + ring->log_desc_block_size = desc_block_size < 2 ? 0 : ilog2(desc_block_size - 1) + 1; + ring->desc_block_size = 1 << ring->log_desc_block_size; + + ring->size = roundup_pow_of_two(size); + ring->full_size = ring->size >> 1; + ring->size_mask = ring->size - 1; + ring->stride = roundup_pow_of_two(MQNIC_DESC_SIZE * ring->desc_block_size); + + ring->rx_info = kvcalloc(ring->size, sizeof(*ring->rx_info), GFP_KERNEL); + if (!ring->rx_info) { + ret = -ENOMEM; + goto fail; + } + + ring->buf_size = ring->size * ring->stride; + ring->buf = dma_alloc_coherent(ring->dev, ring->buf_size, &ring->buf_dma_addr, GFP_KERNEL); + if (!ring->buf) { + ret = -ENOMEM; + goto fail; + } + + ring->priv = priv; + ring->cq = cq; + cq->src_ring = ring; + cq->handler = mqnic_rx_irq; + + ring->hw_addr = mqnic_res_get_addr(ring->interface->rxq_res, ring->index); + + ring->prod_ptr = 0; + ring->cons_ptr = 0; + + // deactivate queue + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set base address + iowrite32((ring->buf_dma_addr & 0xfffff000), + ring->hw_addr + MQNIC_QUEUE_BASE_ADDR_VF_REG + 0); + iowrite32(((u64)ring->buf_dma_addr) >> 32, + ring->hw_addr + MQNIC_QUEUE_BASE_ADDR_VF_REG + 4); + // set size + iowrite32(MQNIC_QUEUE_CMD_SET_SIZE | ilog2(ring->size) | (ring->log_desc_block_size << 8), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set CQN + iowrite32(MQNIC_QUEUE_CMD_SET_CQN | ring->cq->cqn, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set pointers + iowrite32(MQNIC_QUEUE_CMD_SET_PROD_PTR | (ring->prod_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + iowrite32(MQNIC_QUEUE_CMD_SET_CONS_PTR | (ring->cons_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + + ret = mqnic_refill_rx_buffers(ring); + if (ret) { + netdev_err(priv->ndev, "failed to allocate RX buffer for RX queue index %d (of %u total) entry index %u (of %u total)", + ring->index, priv->rxq_count, ring->prod_ptr, ring->size); + if (ret == -ENOMEM) + netdev_err(priv->ndev, "machine might not have enough DMA-capable RAM; try to decrease number of RX channels (currently %u) and/or RX ring parameters (entries; currently %u) and/or module parameter \"num_rxq_entries\" (currently %u)", + priv->rxq_count, ring->size, mqnic_num_rxq_entries); + + goto fail; + } + + return 0; + +fail: + mqnic_close_rx_ring(ring); + return ret; +} + +void mqnic_close_rx_ring(struct mqnic_ring *ring) +{ + mqnic_disable_rx_ring(ring); + + if (ring->cq) { + ring->cq->src_ring = NULL; + ring->cq->handler = NULL; + } + + ring->priv = NULL; + ring->cq = NULL; + + ring->hw_addr = NULL; + + if (ring->buf) { + mqnic_free_rx_buf(ring); + + dma_free_coherent(ring->dev, ring->buf_size, ring->buf, ring->buf_dma_addr); + ring->buf = NULL; + ring->buf_dma_addr = 0; + } + + if (ring->rx_info) { + kvfree(ring->rx_info); + ring->rx_info = NULL; + } + + mqnic_res_free(ring->interface->rxq_res, ring->index); + ring->index = -1; +} + +int mqnic_enable_rx_ring(struct mqnic_ring *ring) +{ + if (!ring->hw_addr) + return -EINVAL; + + // enable queue + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 1, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + + ring->enabled = 1; + + return 0; +} + +void mqnic_disable_rx_ring(struct mqnic_ring *ring) +{ + // disable queue + if (ring->hw_addr) { + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + } + + ring->enabled = 0; +} + +bool mqnic_is_rx_ring_empty(const struct mqnic_ring *ring) +{ + return ring->prod_ptr == ring->cons_ptr; +} + +bool mqnic_is_rx_ring_full(const struct mqnic_ring *ring) +{ + return ring->prod_ptr - ring->cons_ptr >= ring->size; +} + +void mqnic_rx_read_cons_ptr(struct mqnic_ring *ring) +{ + ring->cons_ptr += ((ioread32(ring->hw_addr + MQNIC_QUEUE_PTR_REG) >> 16) - ring->cons_ptr) & MQNIC_QUEUE_PTR_MASK; +} + +void mqnic_rx_write_prod_ptr(struct mqnic_ring *ring) +{ + iowrite32(MQNIC_QUEUE_CMD_SET_PROD_PTR | (ring->prod_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); +} + +void mqnic_free_rx_desc(struct mqnic_ring *ring, int index) +{ + struct mqnic_rx_info *rx_info = &ring->rx_info[index]; + // struct page *page = rx_info->page; + + if (!rx_info->page) + return; + + dma_unmap_page(ring->dev, dma_unmap_addr(rx_info, dma_addr), + dma_unmap_len(rx_info, len), DMA_FROM_DEVICE); + rx_info->dma_addr = 0; + __free_pages(rx_info->page, rx_info->page_order); + rx_info->page = NULL; +} + +int mqnic_free_rx_buf(struct mqnic_ring *ring) +{ + u32 index; + int cnt = 0; + + while (!mqnic_is_rx_ring_empty(ring)) { + index = ring->cons_ptr & ring->size_mask; + mqnic_free_rx_desc(ring, index); + ring->cons_ptr++; + cnt++; + } + + return cnt; +} + +int mqnic_prepare_rx_desc(struct mqnic_ring *ring, int index) +{ + struct mqnic_rx_info *rx_info = &ring->rx_info[index]; + struct mqnic_desc *rx_desc = (struct mqnic_desc *)(ring->buf + index * ring->stride); + struct page *page = rx_info->page; + u32 page_order = ring->page_order; + u32 len = PAGE_SIZE << page_order; + dma_addr_t dma_addr; + + if (unlikely(page)) { + dev_err(ring->dev, "%s: skb not yet processed on interface %d", + __func__, ring->interface->index); + return -1; + } + + page = dev_alloc_pages(page_order); + if (unlikely(!page)) { + dev_err(ring->dev, "%s: failed to allocate memory on interface %d", + __func__, ring->interface->index); + return -ENOMEM; + } + + // map page + dma_addr = dma_map_page(ring->dev, page, 0, len, DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(ring->dev, dma_addr))) { + dev_err(ring->dev, "%s: DMA mapping failed on interface %d", + __func__, ring->interface->index); + __free_pages(page, page_order); + return -1; + } + + // write descriptor + rx_desc->len = cpu_to_le32(len); + rx_desc->addr = cpu_to_le64(dma_addr); + + // update rx_info + rx_info->page = page; + rx_info->page_order = page_order; + rx_info->page_offset = 0; + rx_info->dma_addr = dma_addr; + rx_info->len = len; + + return 0; +} + +int mqnic_refill_rx_buffers(struct mqnic_ring *ring) +{ + u32 missing = ring->size - (ring->prod_ptr - ring->cons_ptr); + int ret = 0; + + if (missing < 8) + return 0; + + for (; missing-- > 0;) { + ret = mqnic_prepare_rx_desc(ring, ring->prod_ptr & ring->size_mask); + if (ret) + break; + ring->prod_ptr++; + } + + // enqueue on NIC + dma_wmb(); + mqnic_rx_write_prod_ptr(ring); + + return ret; +} + +int mqnic_process_rx_cq(struct mqnic_cq *cq, int napi_budget) +{ + struct mqnic_if *interface = cq->interface; + struct device *dev = interface->dev; + struct mqnic_ring *rx_ring = cq->src_ring; + struct mqnic_priv *priv = rx_ring->priv; + struct mqnic_rx_info *rx_info; + struct mqnic_cpl *cpl; + struct sk_buff *skb; + struct page *page; + u32 cq_index; + u32 cq_cons_ptr; + u32 ring_index; + u32 ring_cons_ptr; + int done = 0; + int budget = napi_budget; + u32 len; + + if (unlikely(!priv || !priv->port_up)) + return done; + + // process completion queue + cq_cons_ptr = cq->cons_ptr; + cq_index = cq_cons_ptr & cq->size_mask; + + while (done < budget) { + cpl = (struct mqnic_cpl *)(cq->buf + cq_index * cq->stride); + + if (!!(cpl->phase & cpu_to_le32(0x80000000)) == !!(cq_cons_ptr & cq->size)) + break; + + dma_rmb(); + + ring_index = le16_to_cpu(cpl->index) & rx_ring->size_mask; + rx_info = &rx_ring->rx_info[ring_index]; + page = rx_info->page; + len = min_t(u32, le16_to_cpu(cpl->len), rx_info->len); + + if (len < ETH_HLEN) { + netdev_warn(priv->ndev, "%s: ring %d dropping short frame (length %d)", + __func__, rx_ring->index, len); + rx_ring->dropped_packets++; + goto rx_drop; + } + + if (unlikely(!page)) { + netdev_err(priv->ndev, "%s: ring %d null page at index %d", + __func__, rx_ring->index, ring_index); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_NONE, 16, 1, + cpl, MQNIC_CPL_SIZE, true); + break; + } + + skb = napi_get_frags(&cq->napi); + if (unlikely(!skb)) { + netdev_err(priv->ndev, "%s: ring %d failed to allocate skb", + __func__, rx_ring->index); + break; + } + + // RX hardware timestamp + if (interface->if_features & MQNIC_IF_FEATURE_PTP_TS) + skb_hwtstamps(skb)->hwtstamp = mqnic_read_cpl_ts(interface->mdev, rx_ring, cpl); + + skb_record_rx_queue(skb, rx_ring->index); + + // RX hardware checksum + if (priv->ndev->features & NETIF_F_RXCSUM) { + skb->csum = csum_unfold((__sum16) cpu_to_be16(le16_to_cpu(cpl->rx_csum))); + skb->ip_summed = CHECKSUM_COMPLETE; + } + + // unmap + dma_unmap_page(dev, dma_unmap_addr(rx_info, dma_addr), + dma_unmap_len(rx_info, len), DMA_FROM_DEVICE); + rx_info->dma_addr = 0; + + dma_sync_single_range_for_cpu(dev, rx_info->dma_addr, rx_info->page_offset, + rx_info->len, DMA_FROM_DEVICE); + + __skb_fill_page_desc(skb, 0, page, rx_info->page_offset, len); + rx_info->page = NULL; + + skb_shinfo(skb)->nr_frags = 1; + skb->len = len; + skb->data_len = len; + skb->truesize += rx_info->len; + + // hand off SKB + napi_gro_frags(&cq->napi); + + rx_ring->packets++; + rx_ring->bytes += le16_to_cpu(cpl->len); + +rx_drop: + done++; + + cq_cons_ptr++; + cq_index = cq_cons_ptr & cq->size_mask; + } + + // update CQ consumer pointer + cq->cons_ptr = cq_cons_ptr; + mqnic_cq_write_cons_ptr(cq); + + // process ring + ring_cons_ptr = READ_ONCE(rx_ring->cons_ptr); + ring_index = ring_cons_ptr & rx_ring->size_mask; + + while (ring_cons_ptr != rx_ring->prod_ptr) { + rx_info = &rx_ring->rx_info[ring_index]; + + if (rx_info->page) + break; + + ring_cons_ptr++; + ring_index = ring_cons_ptr & rx_ring->size_mask; + } + + // update consumer pointer + WRITE_ONCE(rx_ring->cons_ptr, ring_cons_ptr); + + // replenish buffers + mqnic_refill_rx_buffers(rx_ring); + + return done; +} + +void mqnic_rx_irq(struct mqnic_cq *cq) +{ + napi_schedule_irqoff(&cq->napi); +} + +int mqnic_poll_rx_cq(struct napi_struct *napi, int budget) +{ + struct mqnic_cq *cq = container_of(napi, struct mqnic_cq, napi); + int done; + + done = mqnic_process_rx_cq(cq, budget); + + if (done == budget) + return done; + + napi_complete(napi); + + mqnic_arm_cq(cq); + + return done; +} diff --git a/drivers/net/mqnic/mqnic_sched_block.c b/drivers/net/mqnic/mqnic_sched_block.c new file mode 100644 index 00000000000000..e60fac3bf330b1 --- /dev/null +++ b/drivers/net/mqnic/mqnic_sched_block.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_sched_block *mqnic_create_sched_block(struct mqnic_if *interface, + int index, + struct mqnic_reg_block *block_rb) +{ + struct device *dev = interface->dev; + struct mqnic_sched_block *block; + struct mqnic_reg_block *rb; + u32 offset; + int ret = 0; + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (!block) + return ERR_PTR(-ENOMEM); + + block->dev = dev; + block->interface = interface; + + block->index = index; + + block->block_rb = block_rb; + + offset = ioread32(block_rb->regs + MQNIC_RB_SCHED_BLOCK_REG_OFFSET); + + block->rb_list = mqnic_enumerate_reg_block_list(interface->hw_addr, offset, interface->hw_regs_size - offset); + + if (!block->rb_list) { + ret = -EIO; + dev_err(dev, "Failed to enumerate blocks"); + goto fail; + } + + dev_info(dev, "Scheduler block-level register blocks:"); + for (rb = block->rb_list; rb->regs; rb++) + dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24, + (rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff); + + block->sched_count = 0; + for (rb = block->rb_list; rb->regs; rb++) { + if (rb->type == MQNIC_RB_SCHED_RR_TYPE && rb->version == MQNIC_RB_SCHED_RR_VER) { + struct mqnic_sched *sched = mqnic_create_scheduler(block, + block->sched_count, rb); + + if (IS_ERR_OR_NULL(sched)) { + ret = PTR_ERR(sched); + goto fail; + } + + block->sched[block->sched_count] = sched; + block->sched_count++; + } + } + + dev_info(dev, "Scheduler count: %d", block->sched_count); + + return block; + +fail: + mqnic_destroy_sched_block(block); + return ERR_PTR(ret); +} + +void mqnic_destroy_sched_block(struct mqnic_sched_block *block) +{ + int k; + + mqnic_deactivate_sched_block(block); + + for (k = 0; k < ARRAY_SIZE(block->sched); k++) { + if (block->sched[k]) { + mqnic_destroy_scheduler(block->sched[k]); + block->sched[k] = NULL; + } + } + + if (block->rb_list) + mqnic_free_reg_block_list(block->rb_list); + + kfree(block); +} + +int mqnic_activate_sched_block(struct mqnic_sched_block *block) +{ + int k; + + // enable schedulers + for (k = 0; k < ARRAY_SIZE(block->sched); k++) + if (block->sched[k]) + mqnic_scheduler_enable(block->sched[k]); + + return 0; +} +EXPORT_SYMBOL(mqnic_activate_sched_block); + +void mqnic_deactivate_sched_block(struct mqnic_sched_block *block) +{ + int k; + + // disable schedulers + for (k = 0; k < ARRAY_SIZE(block->sched); k++) + if (block->sched[k]) + mqnic_scheduler_disable(block->sched[k]); +} +EXPORT_SYMBOL(mqnic_deactivate_sched_block); diff --git a/drivers/net/mqnic/mqnic_sched_port.c b/drivers/net/mqnic/mqnic_sched_port.c new file mode 100644 index 00000000000000..d2d661a166c59f --- /dev/null +++ b/drivers/net/mqnic/mqnic_sched_port.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2024 The Regents of the University of California + */ + +#include "mqnic.h" + +struct mqnic_sched_port *mqnic_create_sched_port(struct mqnic_sched *sched, int index) +{ + struct mqnic_sched_port *port; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + port->sched = sched; + + port->index = index; + + return port; +} + +void mqnic_destroy_sched_port(struct mqnic_sched_port *port) +{ + kfree(port); +} + +int mqnic_sched_port_enable(struct mqnic_sched_port *port) +{ + return mqnic_scheduler_enable(port->sched); +} +EXPORT_SYMBOL(mqnic_sched_port_enable); + +void mqnic_sched_port_disable(struct mqnic_sched_port *port) +{ + mqnic_scheduler_disable(port->sched); +} +EXPORT_SYMBOL(mqnic_sched_port_disable); + +int mqnic_sched_port_channel_enable(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_enable(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_enable); + +void mqnic_sched_port_channel_disable(struct mqnic_sched_port *port, int tc) +{ + mqnic_scheduler_channel_disable(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_disable); + +void mqnic_sched_port_channel_set_dest(struct mqnic_sched_port *port, int tc, int val) +{ + mqnic_scheduler_channel_set_dest(port->sched, port->index, tc, val); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_set_dest); + +int mqnic_sched_port_channel_get_dest(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_get_dest(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_get_dest); + +void mqnic_sched_port_channel_set_pkt_budget(struct mqnic_sched_port *port, int tc, int val) +{ + mqnic_scheduler_channel_set_pkt_budget(port->sched, port->index, tc, val); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_set_pkt_budget); + +int mqnic_sched_port_channel_get_pkt_budget(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_get_pkt_budget(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_get_pkt_budget); + +void mqnic_sched_port_channel_set_data_budget(struct mqnic_sched_port *port, int tc, int val) +{ + mqnic_scheduler_channel_set_data_budget(port->sched, port->index, tc, val); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_set_data_budget); + +int mqnic_sched_port_channel_get_data_budget(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_get_data_budget(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_get_data_budget); + +void mqnic_sched_port_channel_set_pkt_limit(struct mqnic_sched_port *port, int tc, int val) +{ + mqnic_scheduler_channel_set_pkt_limit(port->sched, port->index, tc, val); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_set_pkt_limit); + +int mqnic_sched_port_channel_get_pkt_limit(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_get_pkt_limit(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_get_pkt_limit); + +void mqnic_sched_port_channel_set_data_limit(struct mqnic_sched_port *port, int tc, int val) +{ + mqnic_scheduler_channel_set_data_limit(port->sched, port->index, tc, val); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_set_data_limit); + +int mqnic_sched_port_channel_get_data_limit(struct mqnic_sched_port *port, int tc) +{ + return mqnic_scheduler_channel_get_data_limit(port->sched, port->index, tc); +} +EXPORT_SYMBOL(mqnic_sched_port_channel_get_data_limit); + +int mqnic_sched_port_queue_enable(struct mqnic_sched_port *port, int queue) +{ + int ret = mqnic_scheduler_queue_enable(port->sched, queue); + + if (ret) + return ret; + + return mqnic_scheduler_queue_port_enable(port->sched, port->index, queue); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_enable); + +void mqnic_sched_port_queue_disable(struct mqnic_sched_port *port, int queue) +{ + mqnic_scheduler_queue_port_disable(port->sched, port->index, queue); + mqnic_scheduler_queue_disable(port->sched, queue); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_disable); + +void mqnic_sched_port_queue_set_pause(struct mqnic_sched_port *port, int queue, int val) +{ + mqnic_scheduler_queue_port_set_pause(port->sched, port->index, queue, val); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_set_pause); + +int mqnic_sched_port_queue_get_pause(struct mqnic_sched_port *port, int queue) +{ + return mqnic_scheduler_queue_port_get_pause(port->sched, port->index, queue); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_get_pause); + +void mqnic_sched_port_queue_set_tc(struct mqnic_sched_port *port, int queue, int val) +{ + mqnic_scheduler_queue_port_set_tc(port->sched, port->index, queue, val); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_set_tc); + +int mqnic_sched_port_queue_get_tc(struct mqnic_sched_port *port, int queue) +{ + return mqnic_scheduler_queue_port_get_tc(port->sched, port->index, queue); +} +EXPORT_SYMBOL(mqnic_sched_port_queue_get_tc); diff --git a/drivers/net/mqnic/mqnic_scheduler.c b/drivers/net/mqnic/mqnic_scheduler.c new file mode 100644 index 00000000000000..29a88008cac233 --- /dev/null +++ b/drivers/net/mqnic/mqnic_scheduler.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2021-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +static void _mqnic_scheduler_enable(struct mqnic_sched *sched) +{ + iowrite32(1, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CTRL); +} + +static void _mqnic_scheduler_disable(struct mqnic_sched *sched) +{ + iowrite32(0, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CTRL); +} + +struct mqnic_sched *mqnic_create_scheduler(struct mqnic_sched_block *block, + int index, struct mqnic_reg_block *rb) +{ + struct device *dev = block->dev; + struct mqnic_sched *sched; + u32 val; + int k; + + sched = kzalloc(sizeof(*sched), GFP_KERNEL); + if (!sched) + return ERR_PTR(-ENOMEM); + + sched->dev = dev; + sched->interface = block->interface; + sched->sched_block = block; + + sched->index = index; + + sched->rb = rb; + + sched->type = rb->type; + sched->offset = ioread32(rb->regs + MQNIC_RB_SCHED_RR_REG_OFFSET); + sched->queue_count = ioread32(rb->regs + MQNIC_RB_SCHED_RR_REG_QUEUE_COUNT); + sched->queue_stride = ioread32(rb->regs + MQNIC_RB_SCHED_RR_REG_QUEUE_STRIDE); + + sched->hw_addr = block->interface->hw_addr + sched->offset; + + val = ioread32(rb->regs + MQNIC_RB_SCHED_RR_REG_CFG); + sched->tc_count = val & 0xff; + sched->port_count = (val >> 8) & 0xff; + sched->channel_count = sched->tc_count * sched->port_count; + sched->fc_scale = 1 << ((val >> 16) & 0xff); + + sched->enable_count = 0; + _mqnic_scheduler_disable(sched); + + dev_info(dev, "Scheduler type: 0x%08x", sched->type); + dev_info(dev, "Scheduler offset: 0x%08x", sched->offset); + dev_info(dev, "Scheduler queue count: %d", sched->queue_count); + dev_info(dev, "Scheduler queue stride: %d", sched->queue_stride); + dev_info(dev, "Scheduler TC count: %d", sched->tc_count); + dev_info(dev, "Scheduler port count: %d", sched->port_count); + dev_info(dev, "Scheduler channel count: %d", sched->channel_count); + dev_info(dev, "Scheduler FC scale: %d", sched->fc_scale); + + INIT_LIST_HEAD(&sched->sched_port_list); + + for (k = 0; k < sched->port_count; k++) { + struct mqnic_sched_port *port = mqnic_create_sched_port(sched, k); + + list_add_tail(&port->list, &sched->sched_port_list); + mqnic_interface_register_sched_port(sched->interface, port); + } + + return sched; +} + +void mqnic_destroy_scheduler(struct mqnic_sched *sched) +{ + struct mqnic_sched_port *port, *port_safe; + + _mqnic_scheduler_disable(sched); + + list_for_each_entry_safe(port, port_safe, &sched->sched_port_list, list) { + mqnic_interface_unregister_sched_port(sched->interface, port); + list_del(&port->list); + mqnic_destroy_sched_port(port); + } + + kfree(sched); +} + +int mqnic_scheduler_enable(struct mqnic_sched *sched) +{ + if (sched->enable_count == 0) + _mqnic_scheduler_enable(sched); + + sched->enable_count++; + + return 0; +} +EXPORT_SYMBOL(mqnic_scheduler_enable); + +void mqnic_scheduler_disable(struct mqnic_sched *sched) +{ + sched->enable_count--; + + if (sched->enable_count == 0) + _mqnic_scheduler_disable(sched); +} +EXPORT_SYMBOL(mqnic_scheduler_disable); + +int mqnic_scheduler_channel_enable(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + iowrite32(1, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_CTRL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); + + return 0; +} +EXPORT_SYMBOL(mqnic_scheduler_channel_enable); + +void mqnic_scheduler_channel_disable(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + iowrite32(0, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_CTRL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_disable); + +void mqnic_scheduler_channel_set_dest(struct mqnic_sched *sched, int port, int tc, int val) +{ + int ch = sched->tc_count * port + tc; + + iowrite16(val, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC1_DEST + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_set_dest); + +int mqnic_scheduler_channel_get_dest(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + return ioread16(sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC1_DEST + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_get_dest); + +void mqnic_scheduler_channel_set_pkt_budget(struct mqnic_sched *sched, int port, int tc, int val) +{ + int ch = sched->tc_count * port + tc; + + iowrite16(val, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC1_PB + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_set_pkt_budget); + +int mqnic_scheduler_channel_get_pkt_budget(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + return ioread16(sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC1_PB + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_get_pkt_budget); + +void mqnic_scheduler_channel_set_data_budget(struct mqnic_sched *sched, int port, int tc, int val) +{ + int ch = sched->tc_count * port + tc; + + val = (val + sched->fc_scale - 1) / sched->fc_scale; + iowrite16(val, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC2_DB + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_set_data_budget); + +int mqnic_scheduler_channel_get_data_budget(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + return (int)ioread16(sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC2_DB + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE) * sched->fc_scale; +} +EXPORT_SYMBOL(mqnic_scheduler_channel_get_data_budget); + +void mqnic_scheduler_channel_set_pkt_limit(struct mqnic_sched *sched, int port, int tc, int val) +{ + int ch = sched->tc_count * port + tc; + + iowrite16(val, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC2_PL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_set_pkt_limit); + +int mqnic_scheduler_channel_get_pkt_limit(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + return ioread16(sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC2_PL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_get_pkt_limit); + +void mqnic_scheduler_channel_set_data_limit(struct mqnic_sched *sched, int port, int tc, int val) +{ + int ch = sched->tc_count * port + tc; + + val = (val + sched->fc_scale - 1) / sched->fc_scale; + iowrite32(val, sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC3_DL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE); +} +EXPORT_SYMBOL(mqnic_scheduler_channel_set_data_limit); + +int mqnic_scheduler_channel_get_data_limit(struct mqnic_sched *sched, int port, int tc) +{ + int ch = sched->tc_count * port + tc; + + return (int)ioread32(sched->rb->regs + MQNIC_RB_SCHED_RR_REG_CH0_FC3_DL + ch * MQNIC_RB_SCHED_RR_REG_CH_STRIDE) * sched->fc_scale; +} +EXPORT_SYMBOL(mqnic_scheduler_channel_get_data_limit); + +int mqnic_scheduler_queue_enable(struct mqnic_sched *sched, int queue) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_QUEUE_ENABLE | 1, sched->hw_addr + sched->queue_stride * queue); + + return 0; +} +EXPORT_SYMBOL(mqnic_scheduler_queue_enable); + +void mqnic_scheduler_queue_disable(struct mqnic_sched *sched, int queue) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_QUEUE_ENABLE | 0, sched->hw_addr + sched->queue_stride * queue); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_disable); + +void mqnic_scheduler_queue_set_pause(struct mqnic_sched *sched, int queue, int val) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_QUEUE_PAUSE | (val ? 1 : 0), sched->hw_addr + sched->queue_stride * queue); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_set_pause); + +int mqnic_scheduler_queue_get_pause(struct mqnic_sched *sched, int queue) +{ + return !!(ioread32(sched->hw_addr + sched->queue_stride * queue) & MQNIC_SCHED_RR_QUEUE_PAUSE); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_get_pause); + +int mqnic_scheduler_queue_port_enable(struct mqnic_sched *sched, int queue, int port) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_PORT_ENABLE | (port << 8) | 1, sched->hw_addr + sched->queue_stride * queue); + + return 0; +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_enable); + +void mqnic_scheduler_queue_port_disable(struct mqnic_sched *sched, int queue, int port) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_PORT_ENABLE | (port << 8) | 0, sched->hw_addr + sched->queue_stride * queue); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_disable); + +void mqnic_scheduler_queue_port_set_pause(struct mqnic_sched *sched, int queue, int port, int val) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_PORT_PAUSE | (port << 8) | (val ? 1 : 0), sched->hw_addr + sched->queue_stride * queue); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_set_pause); + +int mqnic_scheduler_queue_port_get_pause(struct mqnic_sched *sched, int queue, int port) +{ + return !!((ioread32(sched->hw_addr + sched->queue_stride * queue) >> port * 8) & MQNIC_SCHED_RR_PORT_PAUSE); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_get_pause); + +void mqnic_scheduler_queue_port_set_tc(struct mqnic_sched *sched, int queue, int port, int val) +{ + iowrite32(MQNIC_SCHED_RR_CMD_SET_PORT_TC | (port << 8) | (val & 0x7), sched->hw_addr + sched->queue_stride * queue); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_set_tc); + +int mqnic_scheduler_queue_port_get_tc(struct mqnic_sched *sched, int queue, int port) +{ + return !!((ioread32(sched->hw_addr + sched->queue_stride * queue) >> port * 8) & MQNIC_SCHED_RR_PORT_TC); +} +EXPORT_SYMBOL(mqnic_scheduler_queue_port_get_tc); diff --git a/drivers/net/mqnic/mqnic_stats.c b/drivers/net/mqnic/mqnic_stats.c new file mode 100644 index 00000000000000..dab84421d15001 --- /dev/null +++ b/drivers/net/mqnic/mqnic_stats.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2022-2023 The Regents of the University of California + */ + +#include "mqnic.h" + +void mqnic_stats_init(struct mqnic_dev *mdev) +{ + mdev->stats_rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_STATS_TYPE, MQNIC_RB_STATS_VER, 0); + + if (!mdev->stats_rb) + return; + + mdev->stats_offset = ioread32(mdev->stats_rb->regs + MQNIC_RB_STATS_REG_OFFSET); + mdev->stats_count = ioread32(mdev->stats_rb->regs + MQNIC_RB_STATS_REG_COUNT); + mdev->stats_stride = ioread32(mdev->stats_rb->regs + MQNIC_RB_STATS_REG_STRIDE); + mdev->stats_flags = ioread32(mdev->stats_rb->regs + MQNIC_RB_STATS_REG_FLAGS); +} + +u64 mqnic_stats_read(struct mqnic_dev *mdev, int index) +{ + u64 val; + + if (!mdev->stats_rb || index < 0 || index >= mdev->stats_count) + return 0; + + val = (u64)ioread32(mdev->hw_addr + mdev->stats_offset + index * 8 + 0); + val |= (u64)ioread32(mdev->hw_addr + mdev->stats_offset + index * 8 + 4) << 32; + + return val; +} +EXPORT_SYMBOL(mqnic_stats_read); diff --git a/drivers/net/mqnic/mqnic_tx.c b/drivers/net/mqnic/mqnic_tx.c new file mode 100644 index 00000000000000..fa3cfd29194846 --- /dev/null +++ b/drivers/net/mqnic/mqnic_tx.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright (c) 2019-2023 The Regents of the University of California + */ + +#include +#include "mqnic.h" + +struct mqnic_ring *mqnic_create_tx_ring(struct mqnic_if *interface) +{ + struct mqnic_ring *ring; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + return ERR_PTR(-ENOMEM); + + ring->dev = interface->dev; + ring->interface = interface; + + ring->index = -1; + ring->enabled = 0; + + ring->hw_addr = NULL; + + ring->prod_ptr = 0; + ring->cons_ptr = 0; + + return ring; +} + +void mqnic_destroy_tx_ring(struct mqnic_ring *ring) +{ + mqnic_close_tx_ring(ring); + + kfree(ring); +} + +int mqnic_open_tx_ring(struct mqnic_ring *ring, struct mqnic_priv *priv, + struct mqnic_cq *cq, int size, int desc_block_size) +{ + int ret = 0; + + if (ring->enabled || ring->hw_addr || ring->buf || !priv || !cq) + return -EINVAL; + + ring->index = mqnic_res_alloc(ring->interface->txq_res); + if (ring->index < 0) + return -ENOMEM; + + ring->log_desc_block_size = desc_block_size < 2 ? 0 : ilog2(desc_block_size - 1) + 1; + ring->desc_block_size = 1 << ring->log_desc_block_size; + + ring->size = roundup_pow_of_two(size); + ring->full_size = ring->size >> 1; + ring->size_mask = ring->size - 1; + ring->stride = roundup_pow_of_two(MQNIC_DESC_SIZE * ring->desc_block_size); + + ring->tx_info = kvcalloc(ring->size, sizeof(*ring->tx_info), GFP_KERNEL); + if (!ring->tx_info) { + ret = -ENOMEM; + goto fail; + } + + ring->buf_size = ring->size * ring->stride; + ring->buf = dma_alloc_coherent(ring->dev, ring->buf_size, &ring->buf_dma_addr, GFP_KERNEL); + if (!ring->buf) { + ret = -ENOMEM; + goto fail; + } + + ring->priv = priv; + ring->cq = cq; + cq->src_ring = ring; + cq->handler = mqnic_tx_irq; + + ring->hw_addr = mqnic_res_get_addr(ring->interface->txq_res, ring->index); + + ring->prod_ptr = 0; + ring->cons_ptr = 0; + + // deactivate queue + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set base address + iowrite32((ring->buf_dma_addr & 0xfffff000), + ring->hw_addr + MQNIC_QUEUE_BASE_ADDR_VF_REG + 0); + iowrite32(((u64)ring->buf_dma_addr) >> 32, + ring->hw_addr + MQNIC_QUEUE_BASE_ADDR_VF_REG + 4); + // set size + iowrite32(MQNIC_QUEUE_CMD_SET_SIZE | ilog2(ring->size) | (ring->log_desc_block_size << 8), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set CQN + iowrite32(MQNIC_QUEUE_CMD_SET_CQN | ring->cq->cqn, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + // set pointers + iowrite32(MQNIC_QUEUE_CMD_SET_PROD_PTR | (ring->prod_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + iowrite32(MQNIC_QUEUE_CMD_SET_CONS_PTR | (ring->cons_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + + return 0; + +fail: + mqnic_close_tx_ring(ring); + return ret; +} + +void mqnic_close_tx_ring(struct mqnic_ring *ring) +{ + mqnic_disable_tx_ring(ring); + + if (ring->cq) { + ring->cq->src_ring = NULL; + ring->cq->handler = NULL; + } + + ring->priv = NULL; + ring->cq = NULL; + + ring->hw_addr = NULL; + + if (ring->buf) { + mqnic_free_tx_buf(ring); + + dma_free_coherent(ring->dev, ring->buf_size, ring->buf, ring->buf_dma_addr); + ring->buf = NULL; + ring->buf_dma_addr = 0; + } + + if (ring->tx_info) { + kvfree(ring->tx_info); + ring->tx_info = NULL; + } + + mqnic_res_free(ring->interface->txq_res, ring->index); + ring->index = -1; +} + +int mqnic_enable_tx_ring(struct mqnic_ring *ring) +{ + if (!ring->hw_addr) + return -EINVAL; + + // enable queue + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 1, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + + ring->enabled = 1; + + return 0; +} + +void mqnic_disable_tx_ring(struct mqnic_ring *ring) +{ + // disable queue + if (ring->hw_addr) { + iowrite32(MQNIC_QUEUE_CMD_SET_ENABLE | 0, + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); + } + + ring->enabled = 0; +} + +bool mqnic_is_tx_ring_empty(const struct mqnic_ring *ring) +{ + return ring->prod_ptr == ring->cons_ptr; +} + +bool mqnic_is_tx_ring_full(const struct mqnic_ring *ring) +{ + return ring->prod_ptr - ring->cons_ptr >= ring->full_size; +} + +void mqnic_tx_read_cons_ptr(struct mqnic_ring *ring) +{ + ring->cons_ptr += ((ioread32(ring->hw_addr + MQNIC_QUEUE_PTR_REG) >> 16) - ring->cons_ptr) & MQNIC_QUEUE_PTR_MASK; +} + +void mqnic_tx_write_prod_ptr(struct mqnic_ring *ring) +{ + iowrite32(MQNIC_QUEUE_CMD_SET_PROD_PTR | (ring->prod_ptr & MQNIC_QUEUE_PTR_MASK), + ring->hw_addr + MQNIC_QUEUE_CTRL_STATUS_REG); +} + +void mqnic_free_tx_desc(struct mqnic_ring *ring, int index, int napi_budget) +{ + struct mqnic_tx_info *tx_info = &ring->tx_info[index]; + struct sk_buff *skb = tx_info->skb; + u32 i; + + prefetchw(&skb->users); + + dma_unmap_single(ring->dev, dma_unmap_addr(tx_info, dma_addr), + dma_unmap_len(tx_info, len), DMA_TO_DEVICE); + dma_unmap_addr_set(tx_info, dma_addr, 0); + + // unmap frags + for (i = 0; i < tx_info->frag_count; i++) + dma_unmap_page(ring->dev, tx_info->frags[i].dma_addr, + tx_info->frags[i].len, DMA_TO_DEVICE); + + napi_consume_skb(skb, napi_budget); + tx_info->skb = NULL; +} + +int mqnic_free_tx_buf(struct mqnic_ring *ring) +{ + u32 index; + int cnt = 0; + + while (!mqnic_is_tx_ring_empty(ring)) { + index = ring->cons_ptr & ring->size_mask; + mqnic_free_tx_desc(ring, index, 0); + ring->cons_ptr++; + cnt++; + } + + return cnt; +} + +int mqnic_process_tx_cq(struct mqnic_cq *cq, int napi_budget) +{ + struct mqnic_if *interface = cq->interface; + struct mqnic_ring *tx_ring = cq->src_ring; + struct mqnic_priv *priv = tx_ring->priv; + struct mqnic_tx_info *tx_info; + struct mqnic_cpl *cpl; + struct skb_shared_hwtstamps hwts; + u32 cq_index; + u32 cq_cons_ptr; + u32 ring_index; + u32 ring_cons_ptr; + u32 packets = 0; + u32 bytes = 0; + int done = 0; + int budget = napi_budget; + + if (unlikely(!priv || !priv->port_up)) + return done; + + // prefetch for BQL + netdev_txq_bql_complete_prefetchw(tx_ring->tx_queue); + + // process completion queue + cq_cons_ptr = cq->cons_ptr; + cq_index = cq_cons_ptr & cq->size_mask; + + while (done < budget) { + cpl = (struct mqnic_cpl *)(cq->buf + cq_index * cq->stride); + + if (!!(cpl->phase & cpu_to_le32(0x80000000)) == !!(cq_cons_ptr & cq->size)) + break; + + dma_rmb(); + + ring_index = le16_to_cpu(cpl->index) & tx_ring->size_mask; + tx_info = &tx_ring->tx_info[ring_index]; + + // TX hardware timestamp + if (unlikely(tx_info->ts_requested)) { + netdev_dbg(priv->ndev, "%s: TX TS requested", __func__); + hwts.hwtstamp = mqnic_read_cpl_ts(interface->mdev, tx_ring, cpl); + skb_tstamp_tx(tx_info->skb, &hwts); + } + // free TX descriptor + mqnic_free_tx_desc(tx_ring, ring_index, napi_budget); + + packets++; + bytes += le16_to_cpu(cpl->len); + + done++; + + cq_cons_ptr++; + cq_index = cq_cons_ptr & cq->size_mask; + } + + // update CQ consumer pointer + cq->cons_ptr = cq_cons_ptr; + mqnic_cq_write_cons_ptr(cq); + + // process ring + ring_cons_ptr = READ_ONCE(tx_ring->cons_ptr); + ring_index = ring_cons_ptr & tx_ring->size_mask; + + while (ring_cons_ptr != tx_ring->prod_ptr) { + tx_info = &tx_ring->tx_info[ring_index]; + + if (tx_info->skb) + break; + + ring_cons_ptr++; + ring_index = ring_cons_ptr & tx_ring->size_mask; + } + + // update consumer pointer + WRITE_ONCE(tx_ring->cons_ptr, ring_cons_ptr); + + // BQL + //netdev_tx_completed_queue(tx_ring->tx_queue, packets, bytes); + + // wake queue if it is stopped + if (netif_tx_queue_stopped(tx_ring->tx_queue) && !mqnic_is_tx_ring_full(tx_ring)) + netif_tx_wake_queue(tx_ring->tx_queue); + + return done; +} + +void mqnic_tx_irq(struct mqnic_cq *cq) +{ + napi_schedule_irqoff(&cq->napi); +} + +int mqnic_poll_tx_cq(struct napi_struct *napi, int budget) +{ + struct mqnic_cq *cq = container_of(napi, struct mqnic_cq, napi); + int done; + + done = mqnic_process_tx_cq(cq, budget); + + if (done == budget) + return done; + + napi_complete(napi); + + mqnic_arm_cq(cq); + + return done; +} + +static bool mqnic_map_skb(struct mqnic_ring *ring, struct mqnic_tx_info *tx_info, + struct mqnic_desc *tx_desc, struct sk_buff *skb) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + const skb_frag_t *frag; + u32 i; + u32 len; + dma_addr_t dma_addr; + + // update tx_info + tx_info->skb = skb; + tx_info->frag_count = 0; + + for (i = 0; i < shinfo->nr_frags; i++) { + frag = &shinfo->frags[i]; + len = skb_frag_size(frag); + dma_addr = skb_frag_dma_map(ring->dev, frag, 0, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(ring->dev, dma_addr))) + // mapping failed + goto map_error; + + // write descriptor + tx_desc[i + 1].len = cpu_to_le32(len); + tx_desc[i + 1].addr = cpu_to_le64(dma_addr); + + // update tx_info + tx_info->frag_count = i + 1; + tx_info->frags[i].len = len; + tx_info->frags[i].dma_addr = dma_addr; + } + + for (i = tx_info->frag_count; i < ring->desc_block_size - 1; i++) { + tx_desc[i + 1].len = 0; + tx_desc[i + 1].addr = 0; + } + + // map skb + len = skb_headlen(skb); + dma_addr = dma_map_single(ring->dev, skb->data, len, DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(ring->dev, dma_addr))) + // mapping failed + goto map_error; + + // write descriptor + tx_desc[0].len = cpu_to_le32(len); + tx_desc[0].addr = cpu_to_le64(dma_addr); + + // update tx_info + dma_unmap_addr_set(tx_info, dma_addr, dma_addr); + dma_unmap_len_set(tx_info, len, len); + + return true; + +map_error: + dev_err(ring->dev, "%s: DMA mapping failed", __func__); + + // unmap frags + for (i = 0; i < tx_info->frag_count; i++) + dma_unmap_page(ring->dev, tx_info->frags[i].dma_addr, + tx_info->frags[i].len, DMA_TO_DEVICE); + + // update tx_info + tx_info->skb = NULL; + tx_info->frag_count = 0; + + return false; +} + +netdev_tx_t mqnic_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + struct mqnic_priv *priv = netdev_priv(ndev); + struct mqnic_ring *ring; + struct mqnic_tx_info *tx_info; + struct mqnic_desc *tx_desc; + int ring_index; + u32 index; + bool stop_queue; + u32 cons_ptr; + + if (unlikely(!priv->port_up)) + goto tx_drop; + + ring_index = skb_get_queue_mapping(skb); + + rcu_read_lock(); + ring = radix_tree_lookup(&priv->txq_table, ring_index); + rcu_read_unlock(); + + if (unlikely(!ring)) + // unknown TX queue + goto tx_drop; + + if (skb->len < ETH_HLEN) { + netdev_warn(priv->ndev, "%s: ring %d dropping short frame (length %d)", + __func__, ring->index, skb->len); + ring->dropped_packets++; + goto tx_drop_count; + } + + cons_ptr = READ_ONCE(ring->cons_ptr); + + // prefetch for BQL + netdev_txq_bql_enqueue_prefetchw(ring->tx_queue); + + index = ring->prod_ptr & ring->size_mask; + + tx_desc = (struct mqnic_desc *)(ring->buf + index * ring->stride); + + tx_info = &ring->tx_info[index]; + + // TX hardware timestamp + tx_info->ts_requested = 0; + if (unlikely(priv->if_features & MQNIC_IF_FEATURE_PTP_TS && shinfo->tx_flags & SKBTX_HW_TSTAMP)) { + netdev_dbg(ndev, "%s: TX TS requested", __func__); + shinfo->tx_flags |= SKBTX_IN_PROGRESS; + tx_info->ts_requested = 1; + } + + // TX hardware checksum + if (skb->ip_summed == CHECKSUM_PARTIAL) { + unsigned int csum_start = skb_checksum_start_offset(skb); + unsigned int csum_offset = skb->csum_offset; + + if (csum_start > 255 || csum_offset > 127) { + netdev_info(ndev, "%s: Hardware checksum fallback start %d offset %d", + __func__, csum_start, csum_offset); + + // offset out of range, fall back on software checksum + if (skb_checksum_help(skb)) { + // software checksumming failed + goto tx_drop_count; + } + tx_desc->tx_csum_cmd = 0; + } else { + tx_desc->tx_csum_cmd = cpu_to_le16(0x8000 | (csum_offset << 8) | (csum_start)); + } + } else { + tx_desc->tx_csum_cmd = 0; + } + + if (shinfo->nr_frags > ring->desc_block_size - 1 || (skb->data_len && skb->data_len < 32)) { + // too many frags or very short data portion; linearize + if (skb_linearize(skb)) + goto tx_drop_count; + } + + // map skb + if (!mqnic_map_skb(ring, tx_info, tx_desc, skb)) + // map failed + goto tx_drop_count; + + // count packet + ring->packets++; + ring->bytes += skb->len; + + // enqueue + ring->prod_ptr++; + + skb_tx_timestamp(skb); + + stop_queue = mqnic_is_tx_ring_full(ring); + if (unlikely(stop_queue)) { + netdev_dbg(ndev, "%s: TX ring %d full", __func__, ring_index); + netif_tx_stop_queue(ring->tx_queue); + } + + // BQL + //netdev_tx_sent_queue(ring->tx_queue, tx_info->len); + //__netdev_tx_sent_queue(ring->tx_queue, tx_info->len, skb->xmit_more); + + // enqueue on NIC + if (unlikely(!netdev_xmit_more() || stop_queue)) { + dma_wmb(); + mqnic_tx_write_prod_ptr(ring); + } + + // check if queue restarted + if (unlikely(stop_queue)) { + // Ensure consumer pointer is up-to-date before checking ring full status + smp_rmb(); + + cons_ptr = READ_ONCE(ring->cons_ptr); + + if (unlikely(!mqnic_is_tx_ring_full(ring))) + netif_tx_wake_queue(ring->tx_queue); + } + + return NETDEV_TX_OK; + +tx_drop_count: + ring->dropped_packets++; +tx_drop: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} From e84f0e48c483bb43df389764e837c915b652b37f Mon Sep 17 00:00:00 2001 From: Eliza Balas Date: Thu, 31 Jul 2025 10:58:54 +0300 Subject: [PATCH 2/4] dt-bindings: net: corundum,mqnic: Add device-tree binding for Corundum MQNIC Add YAML-based devicetree binding documentation for the Corundum mqnic FPGA-based multi-queue network interface controller. Signed-off-by: Eliza Balas --- .../bindings/net/corundum,mqnic.yaml | 111 ++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 113 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/corundum,mqnic.yaml diff --git a/Documentation/devicetree/bindings/net/corundum,mqnic.yaml b/Documentation/devicetree/bindings/net/corundum,mqnic.yaml new file mode 100644 index 00000000000000..9fe98496cbfa97 --- /dev/null +++ b/Documentation/devicetree/bindings/net/corundum,mqnic.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: BSD-2-Clause-Views +# Copyright (c) 2019-2024 The Regents of the University of California +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/net/corundum,mqnic.yaml#" +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Corundum mqnic Ethernet controller + +maintainers: + - Alex Forencich + +description: | + The Corundum mqnic is an open-source multi-queue network interface controller + for FPGA platforms. + +properties: + compatible: + enum: + - corundum,mqnic + + reg: + description: > + Address and length of the "control" register set. Optionally a second and + third range, the "app" and "ram" register sets may be added. + + reg-names: + description: > + Names for each register region in 'reg'. + + interrupts: + minItems: 1 + description: > + Should contain mqnic interrupt(s) + + reset-gpios: + $ref: /schemas/gpio/gpio.yaml# + description: > + GPIO used to reset the device. This property is optional, but if present, + the RESET GPIO controller will be used to reset the device. + + resets: + $ref: /schemas/types.yaml#/definitions/phandle + description: > + Phandle to a reset controller node. This property is optional, but if + present, the device will be reset using the reset controller. + + nvmem-cells: + description: > + Phandle of nvmem cell containing the base MAC address + + nvmem-cell-names: + const: mac-address + description: > + Should be "mac-address" + + mac-address-increment-byte: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + Index of base MAC address byte to increment (default: 0x5) + + mac-address-increment: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + Number to add to the chosen base MAC address byte (default: 0x0) + + mac-address-local: + type: boolean + description: > + Boolean, mark derived MAC addresses as "locally administrated" + (default: false) + + module-eeproms: + $ref: /schemas/types.yaml#/definitions/phandle + description: > + Array of phandles to SFP module EEPROM node(s) + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + mqnic0: ethernet@a0000000 { + compatible = "corundum,mqnic"; + reg = <0x0 0xa0000000 0x0 0x1000000>, + <0x0 0xb0000000 0x0 0x1000000>; + reg-names = "csr", "app"; + interrupt-parent = <&gic>; + interrupts = <0x0 0x59 0x1>; + + nvmem-cells = <&macaddress>; + nvmem-cell-names = "mac-address"; + + mac-address-increment = <0x1>; + mac-address-local; + + module-eeproms = <&module_eeprom_sfp0>; + }; + + eeprom { + #address-cells = <1>; + #size-cells = <1>; + + macaddress: macaddress@20 { + reg = <0x20 0x06>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index b320a39de7fe40..ab1dac3914a744 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -322,6 +322,8 @@ patternProperties: description: Chengdu Corpro Technology Co., Ltd. "^cortina,.*": description: Cortina Systems, Inc. + "^corundum,.*": + description: Corundum Project (https://github.com/ucsdsysnet/corundum) "^cosmic,.*": description: Cosmic Circuits "^crane,.*": From a3d07b5d8de6ffe30ecc7abe4b02f93b49b90e72 Mon Sep 17 00:00:00 2001 From: Eliza Balas Date: Thu, 28 Nov 2024 14:17:11 +0200 Subject: [PATCH 3/4] arch: microblaze: configs: adi_mb_defconfig: Add corundum configs to the list Enable Corundum mqnic and dependencies: RESET_GPIO I2C_ALGOBIT RTC_CLASS Signed-off-by: Eliza Balas --- arch/microblaze/configs/adi_mb_defconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/microblaze/configs/adi_mb_defconfig b/arch/microblaze/configs/adi_mb_defconfig index da698a6f7e26d4..a92494e4a03ca1 100644 --- a/arch/microblaze/configs/adi_mb_defconfig +++ b/arch/microblaze/configs/adi_mb_defconfig @@ -115,6 +115,7 @@ CONFIG_XILINX_LL_TEMAC=y CONFIG_MARVELL_PHY=y CONFIG_DP83867_PHY=y CONFIG_XILINX_PHY=y +CONFIG_CORUNDUM_MQNIC=y # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y @@ -127,6 +128,7 @@ CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MUX=y CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_HELPER_AUTO is not set CONFIG_I2C_XILINX=y CONFIG_SPI=y @@ -139,12 +141,15 @@ CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_GPIO=y # CONFIG_HWMON is not set CONFIG_WATCHDOG=y CONFIG_XILINX_WATCHDOG=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y # CONFIG_USB_SUPPORT is not set +CONFIG_RTC_CLASS=y CONFIG_DMADEVICES=y CONFIG_AXI_DMAC=y CONFIG_XILINX_DMA=y From bae6c1bad42643a8073549959ccf4cc113470479 Mon Sep 17 00:00:00 2001 From: Eliza Balas Date: Tue, 8 Jul 2025 11:29:39 +0300 Subject: [PATCH 4/4] arch: microblaze: boot: dts: Add VCU118 Corundum support This patch adds an example device tree for VCU118 / ad9081_fmca_ebz project with Corundum (MQNIC) support. Signed-off-by: Eliza Balas --- .../boot/dts/vcu118_ad9081_m8_l4_corundum.dts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 arch/microblaze/boot/dts/vcu118_ad9081_m8_l4_corundum.dts diff --git a/arch/microblaze/boot/dts/vcu118_ad9081_m8_l4_corundum.dts b/arch/microblaze/boot/dts/vcu118_ad9081_m8_l4_corundum.dts new file mode 100644 index 00000000000000..2558d96a5a5b6b --- /dev/null +++ b/arch/microblaze/boot/dts/vcu118_ad9081_m8_l4_corundum.dts @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD9081-FMC-EBZ + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-mxfe/ad9081 + * https://wiki.analog.com/resources/eval/user-guides/ad9081_fmca_ebz/ad9081_fmca_ebz_hdl + * + * hdl_project: + * board_revision: <> + * + * Copyright (C) 2019-2025 Analog Devices Inc. + */ + +#include "vcu118_ad9081_m8_l4.dts" + +/ { + model = "Analog Devices AD9081-FMCA-EBZ @Xilinx/vcu118 Corundum support"; +}; + +&amba_pl { + corundum_reset: gpio@52000000 { + #gpio-cells = <2>; + clock-frequency = <100000000>; + clock-names = "s_axi_aclk"; + clocks = <&clk_bus_0>; + compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a"; + gpio-controller ; + reg = <0x52000000 0x1000>; + xlnx,all-inputs = <0x0>; + xlnx,all-inputs-2 = <0x0>; + xlnx,all-outputs = <0x1>; + xlnx,all-outputs-2 = <0x0>; + xlnx,dout-default = <0x00000000>; + xlnx,dout-default-2 = <0x00000000>; + xlnx,gpio-width = <0x1>; + xlnx,gpio2-width = <0x20>; + xlnx,interrupt-present = <0x0>; + xlnx,is-dual = <0x0>; + xlnx,tri-default = <0xFFFFFFFF>; + xlnx,tri-default-2 = <0xFFFFFFFF>; + }; + corundum: corundum_eth100g@50000000 { + compatible = "corundum,mqnic"; + reg = <0x50000000 0x1000000>; + reg-names = "csr"; + local-mac-address = [00 0a 35 00 01 02]; + interrupt-names = "irq"; + interrupt-parent = <&axi_intc>; + interrupts = <5 2>; + reset-gpios = <&corundum_reset 0 GPIO_ACTIVE_HIGH>; + }; +};