Add basic driver framework for the Alibaba Elastic Ethernet Adapter(EEA). This commit creates the netdevice after PCI probe, and initializes the admin queue to send commands to the device. Reviewed-by: Dust Li Reviewed-by: Philo Lu Signed-off-by: Wen Gu Signed-off-by: Xuan Zhuo --- drivers/net/ethernet/alibaba/eea/Makefile | 6 +- drivers/net/ethernet/alibaba/eea/eea_adminq.c | 482 ++++++++++++++++++ drivers/net/ethernet/alibaba/eea/eea_adminq.h | 74 +++ drivers/net/ethernet/alibaba/eea/eea_net.c | 219 ++++++++ drivers/net/ethernet/alibaba/eea/eea_net.h | 136 +++++ drivers/net/ethernet/alibaba/eea/eea_pci.c | 29 +- drivers/net/ethernet/alibaba/eea/eea_pci.h | 3 + 7 files changed, 946 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/alibaba/eea/eea_adminq.c create mode 100644 drivers/net/ethernet/alibaba/eea/eea_adminq.h create mode 100644 drivers/net/ethernet/alibaba/eea/eea_net.c create mode 100644 drivers/net/ethernet/alibaba/eea/eea_net.h diff --git a/drivers/net/ethernet/alibaba/eea/Makefile b/drivers/net/ethernet/alibaba/eea/Makefile index e5e4007810a6..91f318e8e046 100644 --- a/drivers/net/ethernet/alibaba/eea/Makefile +++ b/drivers/net/ethernet/alibaba/eea/Makefile @@ -1,4 +1,6 @@ obj-$(CONFIG_EEA) += eea.o -eea-y := eea_ring.o \ - eea_pci.o +eea-y := eea_ring.o \ + eea_net.o \ + eea_pci.o \ + eea_adminq.o diff --git a/drivers/net/ethernet/alibaba/eea/eea_adminq.c b/drivers/net/ethernet/alibaba/eea/eea_adminq.c new file mode 100644 index 000000000000..c36714a932eb --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_adminq.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +#define EEA_AQ_CMD_CFG_QUERY ((0 << 8) | 0) + +#define EEA_AQ_CMD_QUEUE_CREATE ((1 << 8) | 0) +#define EEA_AQ_CMD_QUEUE_DESTROY_ALL ((1 << 8) | 1) + +#define EEA_AQ_CMD_HOST_INFO ((2 << 8) | 0) + +#define EEA_AQ_CMD_DEV_STATUS ((3 << 8) | 0) + +#define EEA_RING_DESC_F_AQ_PHASE (BIT(15) | BIT(7)) + +#define EEA_QUEUE_FLAGS_HW_SPLIT_HDR BIT(0) +#define EEA_QUEUE_FLAGS_SQCQ BIT(1) +#define EEA_QUEUE_FLAGS_HWTS BIT(2) + +struct eea_aq_create { + __le32 flags; + /* queue index. + * rx: 0 == qidx % 2 + * tx: 1 == qidx % 2 + */ + __le16 qidx; + /* the depth of the queue */ + __le16 depth; + /* 0: without SPLIT HDR + * 1: 128B + * 2: 256B + * 3: 512B + */ + u8 hdr_buf_size; + u8 sq_desc_size; + u8 cq_desc_size; + u8 reserve0; + /* The vector for the irq. rx,tx share the same vector */ + __le16 msix_vector; + __le16 reserve; + /* sq ring cfg. */ + __le32 sq_addr_low; + __le32 sq_addr_high; + /* cq ring cfg. Just valid when flags include EEA_QUEUE_FLAGS_SQCQ. */ + __le32 cq_addr_low; + __le32 cq_addr_high; +}; + +struct eea_aq_queue_drv_status { + __le16 qidx; + + __le16 sq_head; + __le16 cq_head; + __le16 reserved; +}; + +#define EEA_OS_DISTRO 0 +#define EEA_DRV_TYPE 0 +#define EEA_OS_LINUX 1 +#define EEA_SPEC_VER_MAJOR 1 +#define EEA_SPEC_VER_MINOR 0 + +struct eea_aq_host_info_cfg { + __le16 os_type; + __le16 os_dist; + __le16 drv_type; + + __le16 kern_ver_major; + __le16 kern_ver_minor; + __le16 kern_ver_sub_minor; + + __le16 drv_ver_major; + __le16 drv_ver_minor; + __le16 drv_ver_sub_minor; + + __le16 spec_ver_major; + __le16 spec_ver_minor; + __le16 pci_bdf; + __le32 pci_domain; + + u8 os_ver_str[64]; + u8 isa_str[64]; +}; + +#define EEA_HINFO_MAX_REP_LEN 1024 +#define EEA_HINFO_REP_REJECT 2 + +struct eea_aq_host_info_rep { + u8 op_code; + u8 has_reply; + u8 reply_str[EEA_HINFO_MAX_REP_LEN]; +}; + +static struct eea_ring *qid_to_ering(struct eea_net *enet, u32 qid) +{ + struct eea_ring *ering; + + if (qid % 2 == 0) + ering = enet->rx[qid / 2]->ering; + else + ering = enet->tx[qid / 2].ering; + + return ering; +} + +#define EEA_AQ_TIMEOUT_US (60 * 1000 * 1000) + +/* Operations are fully serialized under the protection of a lock + * (e.g., rtlock) + * + * If the hardware fails to complete the command correctly, a device reset will + * be triggered. + */ +static int eea_adminq_submit(struct eea_net *enet, u16 cmd, + dma_addr_t req_addr, dma_addr_t res_addr, + u32 req_size, u32 res_size) +{ + struct eea_aq_cdesc *cdesc; + struct eea_aq_desc *desc; + int ret; + + desc = ering_aq_alloc_desc(enet->adminq.ring); + + desc->classid = cmd >> 8; + desc->command = cmd & 0xff; + + desc->data_addr = cpu_to_le64(req_addr); + desc->data_len = cpu_to_le32(req_size); + + desc->reply_addr = cpu_to_le64(res_addr); + desc->reply_len = cpu_to_le32(res_size); + + /* for update flags */ + dma_wmb(); + + desc->flags = cpu_to_le16(enet->adminq.phase); + + ering_sq_commit_desc(enet->adminq.ring); + + ering_kick(enet->adminq.ring); + + ++enet->adminq.num; + + if ((enet->adminq.num % enet->adminq.ring->num) == 0) + enet->adminq.phase ^= EEA_RING_DESC_F_AQ_PHASE; + + ret = read_poll_timeout(ering_cq_get_desc, cdesc, cdesc, 10, + EEA_AQ_TIMEOUT_US, false, enet->adminq.ring); + if (ret) { + netdev_err(enet->netdev, + "adminq exec timeout. cmd: %d reset device.\n", + cmd); + /* The device must be reset before unmapping buffers to avoid + * potential DMA writes after the memory is freed. + */ + eea_device_reset(enet->edev); + enet->adminq.broken = true; + return ret; + } + + /* Returns 0 on success, or a negative error code on failure. */ + ret = le32_to_cpu(cdesc->status); + + ering_cq_ack_desc(enet->adminq.ring, 1); + + if (ret) + netdev_err(enet->netdev, + "adminq exec failed. cmd: %d ret %d\n", cmd, ret); + + return ret; +} + +static int eea_adminq_exec(struct eea_net *enet, u16 cmd, + void *req, u32 req_size, void *res, u32 res_size) +{ + dma_addr_t req_addr = 0, res_addr = 0; + struct device *dma; + int ret; + + if (enet->adminq.broken) + return -EIO; + + dma = enet->edev->dma_dev; + + if (req) { + req_addr = dma_map_single(dma, req, req_size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dma, req_addr))) + return -ENOMEM; + } + + if (res) { + res_addr = dma_map_single(dma, res, res_size, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dma, res_addr))) { + ret = -ENOMEM; + goto err_unmap_req; + } + } + + ret = eea_adminq_submit(enet, cmd, req_addr, res_addr, + req_size, res_size); + if (res) + dma_unmap_single(dma, res_addr, res_size, DMA_FROM_DEVICE); + +err_unmap_req: + if (req) + dma_unmap_single(dma, req_addr, req_size, DMA_TO_DEVICE); + + return ret; +} + +void eea_destroy_adminq(struct eea_net *enet) +{ + struct eea_aq *aq; + + aq = &enet->adminq; + + if (aq->ring) { + ering_free(aq->ring); + aq->ring = NULL; + aq->phase = 0; + } + + kfree(aq->q_req_buf); + kfree(aq->q_res_buf); + + aq->q_req_buf = NULL; + aq->q_res_buf = NULL; +} + +int eea_create_adminq(struct eea_net *enet, u32 qid) +{ + u32 db_size, q_size, num; + struct eea_ring *ering; + struct eea_aq *aq; + int err = -ENOMEM; + + num = enet->edev->rx_num + enet->edev->tx_num; + aq = &enet->adminq; + + ering = ering_alloc(qid, 64, enet->edev, sizeof(struct eea_aq_desc), + sizeof(struct eea_aq_cdesc), "adminq"); + if (!ering) + return -ENOMEM; + + aq->ring = ering; + + err = eea_pci_active_aq(ering, qid / 2 + 1); + if (err) + goto err; + + aq->phase = BIT(7); + aq->num = 0; + + q_size = sizeof(*aq->q_req_buf) * num; + db_size = sizeof(*aq->q_res_buf) * num; + + aq->q_req_size = q_size; + aq->q_res_size = db_size; + + aq->q_req_buf = kzalloc(q_size, GFP_KERNEL); + if (!aq->q_req_buf) + goto err; + + aq->q_res_buf = kzalloc(db_size, GFP_KERNEL); + if (!aq->q_res_buf) + goto err; + + err = eea_pci_set_aq_up(enet->edev); + if (err) + goto err; + + aq->broken = false; + + return 0; + +err: + /* For the adminq, we can safely free the ring before setting it up. */ + eea_destroy_adminq(enet); + return err; +} + +int eea_adminq_query_cfg(struct eea_net *enet, struct eea_aq_cfg *cfg) +{ + return eea_adminq_exec(enet, EEA_AQ_CMD_CFG_QUERY, NULL, 0, cfg, + sizeof(*cfg)); +} + +static void qcfg_fill(struct eea_aq_create *qcfg, struct eea_ring *ering, + u32 flags) +{ + qcfg->flags = cpu_to_le32(flags); + qcfg->qidx = cpu_to_le16(ering->index); + qcfg->depth = cpu_to_le16(ering->num); + + qcfg->hdr_buf_size = flags & EEA_QUEUE_FLAGS_HW_SPLIT_HDR ? 1 : 0; + qcfg->sq_desc_size = ering->sq.desc_size; + qcfg->cq_desc_size = ering->cq.desc_size; + qcfg->msix_vector = cpu_to_le16(ering->msix_vec); + + qcfg->sq_addr_low = cpu_to_le32(lower_32_bits(ering->sq.dma_addr)); + qcfg->sq_addr_high = cpu_to_le32(upper_32_bits(ering->sq.dma_addr)); + + qcfg->cq_addr_low = cpu_to_le32(lower_32_bits(ering->cq.dma_addr)); + qcfg->cq_addr_high = cpu_to_le32(upper_32_bits(ering->cq.dma_addr)); +} + +int eea_adminq_create_q(struct eea_net *enet, u32 num, u32 flags) +{ + int i, db_size, q_size, err = -ENOMEM; + struct eea_net_cfg *cfg; + struct eea_ring *ering; + struct eea_aq *aq; + + cfg = &enet->cfg; + aq = &enet->adminq; + + if (cfg->split_hdr) + flags |= EEA_QUEUE_FLAGS_HW_SPLIT_HDR; + + flags |= EEA_QUEUE_FLAGS_SQCQ; + flags |= EEA_QUEUE_FLAGS_HWTS; + + q_size = sizeof(*aq->q_req_buf) * num; + db_size = sizeof(*aq->q_res_buf) * num; + + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + qcfg_fill(aq->q_req_buf + i, ering, flags); + } + + err = eea_adminq_exec(enet, EEA_AQ_CMD_QUEUE_CREATE, + aq->q_req_buf, q_size, aq->q_res_buf, db_size); + if (err) + return err; + + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + ering->db = eea_pci_db_addr(ering->edev, + le32_to_cpu(aq->q_res_buf[i])); + if (!ering->db) { + netdev_err(enet->netdev, "invalid db off %u\n", + le32_to_cpu(aq->q_res_buf[i])); + goto err; + } + } + + return err; + +err: + eea_adminq_destroy_all_q(enet); + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + ering->db = NULL; + } + + return -EIO; +} + +int eea_adminq_destroy_all_q(struct eea_net *enet) +{ + return eea_adminq_exec(enet, EEA_AQ_CMD_QUEUE_DESTROY_ALL, NULL, 0, + NULL, 0); +} + +/* The caller must ensure that both the 'rt' and 'tx' arrays are valid. */ +struct eea_aq_dev_status *eea_adminq_dev_status(struct eea_net *enet) +{ + struct eea_aq_queue_drv_status *drv_status; + struct eea_aq_dev_status *dev_status; + int err, i, io_num, size, q_num; + struct eea_ring *ering; + void *rep, *req; + + q_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num + 1; + io_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num; + + req = kcalloc(q_num, sizeof(struct eea_aq_queue_drv_status), + GFP_KERNEL); + if (!req) + return NULL; + + size = struct_size(dev_status, q_status, q_num); + + rep = kzalloc(size, GFP_KERNEL); + if (!rep) { + kfree(req); + return NULL; + } + + drv_status = req; + for (i = 0; i < io_num; ++i, ++drv_status) { + ering = qid_to_ering(enet, i); + drv_status->qidx = cpu_to_le16(i); + drv_status->cq_head = cpu_to_le16(ering->cq.head); + drv_status->sq_head = cpu_to_le16(ering->sq.head); + } + + drv_status->qidx = cpu_to_le16(i); + drv_status->cq_head = cpu_to_le16(enet->adminq.ring->cq.head); + drv_status->sq_head = cpu_to_le16(enet->adminq.ring->sq.head); + + err = eea_adminq_exec(enet, EEA_AQ_CMD_DEV_STATUS, req, + q_num * sizeof(struct eea_aq_queue_drv_status), + rep, size); + kfree(req); + if (err) { + kfree(rep); + return NULL; + } + + return rep; +} + +int eea_adminq_config_host_info(struct eea_net *enet) +{ + struct device *dev = enet->edev->dma_dev; + struct eea_aq_host_info_cfg *cfg; + struct eea_aq_host_info_rep *rep; + int rc = -ENOMEM; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return rc; + + rep = kzalloc(sizeof(*rep), GFP_KERNEL); + if (!rep) + goto err_free_cfg; + + cfg->os_type = cpu_to_le16(EEA_OS_LINUX); + cfg->os_dist = cpu_to_le16(EEA_OS_DISTRO); + cfg->drv_type = cpu_to_le16(EEA_DRV_TYPE); + + cfg->kern_ver_major = cpu_to_le16(LINUX_VERSION_MAJOR); + cfg->kern_ver_minor = cpu_to_le16(LINUX_VERSION_PATCHLEVEL); + cfg->kern_ver_sub_minor = cpu_to_le16(LINUX_VERSION_SUBLEVEL); + + cfg->drv_ver_major = cpu_to_le16(EEA_VER_MAJOR); + cfg->drv_ver_minor = cpu_to_le16(EEA_VER_MINOR); + cfg->drv_ver_sub_minor = cpu_to_le16(EEA_VER_SUB_MINOR); + + cfg->spec_ver_major = cpu_to_le16(EEA_SPEC_VER_MAJOR); + cfg->spec_ver_minor = cpu_to_le16(EEA_SPEC_VER_MINOR); + + cfg->pci_bdf = cpu_to_le16(eea_pci_dev_id(enet->edev)); + cfg->pci_domain = cpu_to_le32(eea_pci_domain_nr(enet->edev)); + + strscpy(cfg->os_ver_str, utsname()->release, sizeof(cfg->os_ver_str)); + strscpy(cfg->isa_str, utsname()->machine, sizeof(cfg->isa_str)); + + rc = eea_adminq_exec(enet, EEA_AQ_CMD_HOST_INFO, + cfg, sizeof(*cfg), rep, sizeof(*rep)); + + if (!rc) { + if (rep->op_code == EEA_HINFO_REP_REJECT) { + dev_err(dev, "Device has refused the initialization due to provided host information\n"); + rc = -ENODEV; + } + if (rep->has_reply) { + rep->reply_str[EEA_HINFO_MAX_REP_LEN - 1] = '\0'; + dev_warn(dev, "Device replied: %s\n", + rep->reply_str); + } + } + + kfree(rep); +err_free_cfg: + kfree(cfg); + return rc; +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_adminq.h b/drivers/net/ethernet/alibaba/eea/eea_adminq.h new file mode 100644 index 000000000000..816059193b3b --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_adminq.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_ADMINQ_H__ +#define __EEA_ADMINQ_H__ + +struct eea_aq_cfg { + __le32 rx_depth_max; + __le32 rx_depth_def; + + __le32 tx_depth_max; + __le32 tx_depth_def; + + __le32 max_tso_size; + __le32 max_tso_segs; + + u8 mac[ETH_ALEN]; + __le16 status; + + __le16 mtu; + __le16 reserved0; + __le16 reserved1; + u8 reserved2; + u8 reserved3; + + __le16 reserved4; + __le16 reserved5; + __le16 reserved6; +}; + +struct eea_aq_queue_status { + __le16 qidx; +#define EEA_QUEUE_STATUS_OK 0 +#define EEA_QUEUE_STATUS_NEED_RESET 1 + __le16 status; +}; + +struct eea_aq_dev_status { +#define EEA_LINK_DOWN_STATUS 0 +#define EEA_LINK_UP_STATUS 1 + __le16 link_status; + __le16 reserved; + + struct eea_aq_queue_status q_status[]; +}; + +struct eea_aq { + struct eea_ring *ring; + u32 num; + bool broken; + u16 phase; + + u32 q_req_size; + u32 q_res_size; + struct eea_aq_create *q_req_buf; + __le32 *q_res_buf; +}; + +struct eea_net; + +int eea_create_adminq(struct eea_net *enet, u32 qid); +void eea_destroy_adminq(struct eea_net *enet); + +int eea_adminq_query_cfg(struct eea_net *enet, struct eea_aq_cfg *cfg); + +int eea_adminq_create_q(struct eea_net *enet, u32 num, u32 flags); +int eea_adminq_destroy_all_q(struct eea_net *enet); +struct eea_aq_dev_status *eea_adminq_dev_status(struct eea_net *enet); +int eea_adminq_config_host_info(struct eea_net *enet); +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_net.c b/drivers/net/ethernet/alibaba/eea/eea_net.c new file mode 100644 index 000000000000..ff43b90a6a68 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_net.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include +#include +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +#define EEA_SPLIT_HDR_SIZE ALIGN(128, L1_CACHE_BYTES) +#define EEA_NET_IO_RING_DEPTH_MAX (32 * 1024) +#define EEA_NET_IO_RING_DEPTH_MIN 128 + +static int eea_update_cfg(struct eea_net *enet, + struct eea_device *edev, + struct eea_aq_cfg *hwcfg) +{ + enet->cfg_hw.rx_ring_depth = le32_to_cpu(hwcfg->rx_depth_max); + enet->cfg_hw.tx_ring_depth = le32_to_cpu(hwcfg->tx_depth_max); + + enet->cfg_hw.rx_ring_num = edev->rx_num; + enet->cfg_hw.tx_ring_num = edev->tx_num; + + enet->cfg.rx_ring_depth = le32_to_cpu(hwcfg->rx_depth_def); + enet->cfg.tx_ring_depth = le32_to_cpu(hwcfg->tx_depth_def); + + enet->cfg.rx_ring_num = edev->rx_num; + enet->cfg.tx_ring_num = edev->tx_num; + + enet->cfg_hw.split_hdr = EEA_SPLIT_HDR_SIZE; + + /* Now, we assert that the rx ring num is equal to the tx ring num. */ + if (enet->cfg_hw.rx_ring_num != enet->cfg_hw.tx_ring_num) { + dev_err(edev->dma_dev, "The device gave us invalid rx,tx ring num. %d %d\n", + enet->cfg.rx_ring_num, + enet->cfg.tx_ring_num); + return -EINVAL; + } + + if (enet->cfg_hw.rx_ring_depth > EEA_NET_IO_RING_DEPTH_MAX || + enet->cfg_hw.rx_ring_depth < EEA_NET_IO_RING_DEPTH_MIN) { + dev_err(edev->dma_dev, "The device gave us an invalid rx depth. %d\n", + enet->cfg_hw.rx_ring_depth); + return -EINVAL; + } + + if (enet->cfg_hw.tx_ring_depth > EEA_NET_IO_RING_DEPTH_MAX || + enet->cfg_hw.tx_ring_depth < EEA_NET_IO_RING_DEPTH_MIN) { + dev_err(edev->dma_dev, "The device gave us an invalid tx depth. %d\n", + enet->cfg_hw.tx_ring_depth); + return -EINVAL; + } + + return 0; +} + +static int eea_netdev_init_features(struct net_device *netdev, + struct eea_net *enet, + struct eea_device *edev) +{ + struct eea_aq_cfg *cfg; + int err; + u32 mtu; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + err = eea_adminq_query_cfg(enet, cfg); + if (err) + goto err_free; + + mtu = le16_to_cpu(cfg->mtu); + if (mtu < ETH_MIN_MTU) { + dev_err(edev->dma_dev, "The device gave us an invalid MTU. Here we can only exit the initialization. %u < %u\n", + mtu, ETH_MIN_MTU); + err = -EINVAL; + goto err_free; + } + + err = eea_update_cfg(enet, edev, cfg); + if (err) + goto err_free; + + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + + netdev->hw_features |= NETIF_F_HW_CSUM; + netdev->hw_features |= NETIF_F_GRO_HW; + netdev->hw_features |= NETIF_F_SG; + netdev->hw_features |= NETIF_F_TSO; + netdev->hw_features |= NETIF_F_TSO_ECN; + netdev->hw_features |= NETIF_F_TSO6; + netdev->hw_features |= NETIF_F_GSO_UDP_L4; + + netdev->features |= NETIF_F_HIGHDMA; + netdev->features |= NETIF_F_HW_CSUM; + netdev->features |= NETIF_F_SG; + netdev->features |= NETIF_F_GSO_ROBUST; + netdev->features |= netdev->hw_features & NETIF_F_ALL_TSO; + netdev->features |= NETIF_F_RXCSUM; + netdev->features |= NETIF_F_GRO_HW; + + netdev->vlan_features = netdev->features; + + if (!is_valid_ether_addr(cfg->mac)) { + dev_err(edev->dma_dev, "The device gave invalid mac %pM\n", + cfg->mac); + err = -EINVAL; + goto err_free; + } + + eth_hw_addr_set(netdev, cfg->mac); + + enet->speed = SPEED_UNKNOWN; + enet->duplex = DUPLEX_UNKNOWN; + + netdev->min_mtu = ETH_MIN_MTU; + + netdev->mtu = mtu; + + /* If jumbo frames are already enabled, then the returned MTU will be a + * jumbo MTU, and the driver will automatically enable jumbo frame + * support by default. + */ + netdev->max_mtu = mtu; + +err_free: + kfree(cfg); + return err; +} + +static const struct net_device_ops eea_netdev = { + .ndo_validate_addr = eth_validate_addr, + .ndo_features_check = passthru_features_check, +}; + +static struct eea_net *eea_netdev_alloc(struct eea_device *edev, u32 pairs) +{ + struct net_device *netdev; + struct eea_net *enet; + + netdev = alloc_etherdev_mq(sizeof(struct eea_net), pairs); + if (!netdev) { + dev_err(edev->dma_dev, + "alloc_etherdev_mq failed with pairs %d\n", pairs); + return NULL; + } + + netdev->netdev_ops = &eea_netdev; + SET_NETDEV_DEV(netdev, edev->dma_dev); + + enet = netdev_priv(netdev); + enet->netdev = netdev; + enet->edev = edev; + edev->enet = enet; + + return enet; +} + +int eea_net_probe(struct eea_device *edev) +{ + struct eea_net *enet; + int err = -ENOMEM; + + enet = eea_netdev_alloc(edev, edev->rx_num); + if (!enet) + return -ENOMEM; + + err = eea_create_adminq(enet, edev->rx_num + edev->tx_num); + if (err) + goto err_free_netdev; + + err = eea_adminq_config_host_info(enet); + if (err) + goto err_reset_dev; + + err = eea_netdev_init_features(enet->netdev, enet, edev); + if (err) + goto err_reset_dev; + + netdev_dbg(enet->netdev, "eea probe success.\n"); + + return 0; + +err_reset_dev: + eea_device_reset(edev); + eea_destroy_adminq(enet); + +err_free_netdev: + free_netdev(enet->netdev); + return err; +} + +void eea_net_remove(struct eea_device *edev) +{ + struct net_device *netdev; + struct eea_net *enet; + + enet = edev->enet; + netdev = enet->netdev; + + netdev_dbg(enet->netdev, "eea removed.\n"); + + eea_device_reset(edev); + + eea_destroy_adminq(enet); + + free_netdev(netdev); +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_net.h b/drivers/net/ethernet/alibaba/eea/eea_net.h new file mode 100644 index 000000000000..239312456c5b --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_net.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_NET_H__ +#define __EEA_NET_H__ + +#include +#include + +#include "eea_adminq.h" +#include "eea_ring.h" + +#define EEA_VER_MAJOR 1 +#define EEA_VER_MINOR 0 +#define EEA_VER_SUB_MINOR 0 + +struct eea_net_tx { + struct eea_net *enet; + + struct eea_ring *ering; + + struct eea_tx_meta *meta; + struct eea_tx_meta *free; + + struct device *dma_dev; + + u32 index; + + char name[16]; +}; + +struct eea_rx_meta { + struct eea_rx_meta *next; + + struct page *page; + dma_addr_t dma; + u32 offset; + u32 frags; + + struct page *hdr_page; + void *hdr_addr; + dma_addr_t hdr_dma; + + u32 id; + + u32 truesize; + u32 headroom; + u32 tailroom; + + u32 len; +}; + +struct eea_net_rx_pkt_ctx { + u16 idx; + + bool data_valid; + bool do_drop; + + struct sk_buff *head_skb; +}; + +struct eea_net_rx { + struct eea_net *enet; + + struct eea_ring *ering; + + struct eea_rx_meta *meta; + struct eea_rx_meta *free; + + struct device *dma_dev; + + u32 index; + + u32 flags; + + u32 headroom; + + struct napi_struct *napi; + + char name[16]; + + struct eea_net_rx_pkt_ctx pkt; + + struct page_pool *pp; +}; + +struct eea_net_cfg { + u32 rx_ring_depth; + u32 tx_ring_depth; + u32 rx_ring_num; + u32 tx_ring_num; + + u8 rx_sq_desc_size; + u8 rx_cq_desc_size; + u8 tx_sq_desc_size; + u8 tx_cq_desc_size; + + u32 split_hdr; +}; + +enum { + EEA_LINK_ERR_NONE, + EEA_LINK_ERR_HA_RESET_DEV, + EEA_LINK_ERR_LINK_DOWN, +}; + +struct eea_net { + struct eea_device *edev; + struct net_device *netdev; + + struct eea_aq adminq; + + struct eea_net_tx *tx; + struct eea_net_rx **rx; + + struct eea_net_cfg cfg; + struct eea_net_cfg cfg_hw; + + u32 link_err; + + bool started; + + u8 duplex; + u32 speed; + + u64 hw_ts_offset; +}; + +int eea_net_probe(struct eea_device *edev); +void eea_net_remove(struct eea_device *edev); + +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_pci.c b/drivers/net/ethernet/alibaba/eea/eea_pci.c index 0b165f14a162..7258271d3260 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_pci.c +++ b/drivers/net/ethernet/alibaba/eea/eea_pci.c @@ -8,6 +8,7 @@ #include #include +#include "eea_net.h" #include "eea_pci.h" #define EEA_PCI_DB_OFFSET 4096 @@ -64,7 +65,9 @@ struct eea_pci_device { ((void __iomem *)((reg) + offsetof(struct eea_pci_cfg, item))) #define cfg_write8(reg, item, val) iowrite8(val, cfg_pointer(reg, item)) +#define cfg_write16(reg, item, val) iowrite16(val, cfg_pointer(reg, item)) #define cfg_write32(reg, item, val) iowrite32(val, cfg_pointer(reg, item)) +#define cfg_write64(reg, item, val) iowrite64_lo_hi(val, cfg_pointer(reg, item)) #define cfg_read8(reg, item) ioread8(cfg_pointer(reg, item)) #define cfg_read32(reg, item) ioread32(cfg_pointer(reg, item)) @@ -309,6 +312,25 @@ void __iomem *eea_pci_db_addr(struct eea_device *edev, u32 off) return edev->ep_dev->db_base + off; } +int eea_pci_active_aq(struct eea_ring *ering, int msix_vec) +{ + struct eea_pci_device *ep_dev = ering->edev->ep_dev; + + cfg_write16(ep_dev->reg, aq_size, ering->num); + cfg_write16(ep_dev->reg, aq_msix_vector, msix_vec); + + cfg_write64(ep_dev->reg, aq_sq_addr, ering->sq.dma_addr); + cfg_write64(ep_dev->reg, aq_cq_addr, ering->cq.dma_addr); + + ering->db = eea_pci_db_addr(ering->edev, + cfg_read32(ep_dev->reg, aq_db_off)); + + if (!ering->db) + return -EIO; + + return 0; +} + u64 eea_pci_device_ts(struct eea_device *edev) { struct eea_pci_device *ep_dev = edev->ep_dev; @@ -330,7 +352,9 @@ static int eea_init_device(struct eea_device *edev) if (err) goto err; - /* do net device probe ... */ + err = eea_net_probe(edev); + if (err) + goto err; return 0; err: @@ -364,6 +388,9 @@ static void __eea_pci_remove(struct pci_dev *pci_dev) { struct eea_pci_device *ep_dev = pci_get_drvdata(pci_dev); struct device *dev = get_device(&ep_dev->pci_dev->dev); + struct eea_device *edev = &ep_dev->edev; + + eea_net_remove(edev); pci_disable_sriov(pci_dev); diff --git a/drivers/net/ethernet/alibaba/eea/eea_pci.h b/drivers/net/ethernet/alibaba/eea/eea_pci.h index be4e75b4ed2f..d0094c419f59 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_pci.h +++ b/drivers/net/ethernet/alibaba/eea/eea_pci.h @@ -10,6 +10,8 @@ #include +#include "eea_ring.h" + struct eea_pci_cap { __u8 cap_vndr; __u8 cap_next; @@ -43,6 +45,7 @@ u16 eea_pci_dev_id(struct eea_device *edev); int eea_device_reset(struct eea_device *dev); int eea_pci_set_aq_up(struct eea_device *dev); +int eea_pci_active_aq(struct eea_ring *ering, int msix_vec); u64 eea_pci_device_ts(struct eea_device *edev); -- 2.32.0.3.g01195cf9f