Add device pci init implementation,
to obtain PCI capability and read configuration, etc.

Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
 drivers/net/zxdh/meson.build   |   1 +
 drivers/net/zxdh/zxdh_ethdev.c |  44 +++++
 drivers/net/zxdh/zxdh_ethdev.h |  18 ++-
 drivers/net/zxdh/zxdh_pci.c    | 283 +++++++++++++++++++++++++++++++++
 drivers/net/zxdh/zxdh_pci.h    | 138 ++++++++++++++++
 5 files changed, 483 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/zxdh/zxdh_pci.c
 create mode 100644 drivers/net/zxdh/zxdh_pci.h

diff --git a/drivers/net/zxdh/meson.build b/drivers/net/zxdh/meson.build
index 932fb1c835..7db4e7bc71 100644
--- a/drivers/net/zxdh/meson.build
+++ b/drivers/net/zxdh/meson.build
@@ -15,4 +15,5 @@ endif
 
 sources = files(
         'zxdh_ethdev.c',
+        'zxdh_pci.c',
 )
diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c
index b64dddc91e..ae20e00317 100644
--- a/drivers/net/zxdh/zxdh_ethdev.c
+++ b/drivers/net/zxdh/zxdh_ethdev.c
@@ -8,6 +8,41 @@
 
 #include "zxdh_ethdev.h"
 #include "zxdh_logs.h"
+#include "zxdh_pci.h"
+
+struct zxdh_hw_internal zxdh_hw_internal[RTE_MAX_ETHPORTS];
+
+static int32_t
+zxdh_init_device(struct rte_eth_dev *eth_dev)
+{
+    struct zxdh_hw *hw = eth_dev->data->dev_private;
+    struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
+    int ret = 0;
+
+    ret = zxdh_read_pci_caps(pci_dev, hw);
+    if (ret) {
+        PMD_DRV_LOG(ERR, "port 0x%x pci caps read failed .", hw->port_id);
+        goto err;
+    }
+
+    zxdh_hw_internal[hw->port_id].zxdh_vtpci_ops = &zxdh_dev_pci_ops;
+    zxdh_pci_reset(hw);
+    zxdh_get_pci_dev_config(hw);
+
+    rte_ether_addr_copy((struct rte_ether_addr *)hw->mac_addr, &eth_dev->data->mac_addrs[0]);
+
+    /* If host does not support both status and MSI-X then disable LSC */
+    if (vtpci_with_feature(hw, ZXDH_NET_F_STATUS) && hw->use_msix != ZXDH_MSIX_NONE)
+        eth_dev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;
+    else
+        eth_dev->data->dev_flags &= ~RTE_ETH_DEV_INTR_LSC;
+
+    return 0;
+
+err:
+    PMD_DRV_LOG(ERR, "port %d init device failed", eth_dev->data->port_id);
+    return ret;
+}
 
 static int
 zxdh_eth_dev_init(struct rte_eth_dev *eth_dev)
@@ -46,6 +81,15 @@ zxdh_eth_dev_init(struct rte_eth_dev *eth_dev)
         hw->is_pf = 1;
     }
 
+    ret = zxdh_init_device(eth_dev);
+    if (ret < 0)
+        goto err_zxdh_init;
+
+    return ret;
+
+err_zxdh_init:
+    rte_free(eth_dev->data->mac_addrs);
+    eth_dev->data->mac_addrs = NULL;
     return ret;
 }
 
diff --git a/drivers/net/zxdh/zxdh_ethdev.h b/drivers/net/zxdh/zxdh_ethdev.h
index a11e3624a9..a22ac15065 100644
--- a/drivers/net/zxdh/zxdh_ethdev.h
+++ b/drivers/net/zxdh/zxdh_ethdev.h
@@ -5,6 +5,7 @@
 #ifndef ZXDH_ETHDEV_H
 #define ZXDH_ETHDEV_H
 
+#include <rte_ether.h>
 #include "ethdev_driver.h"
 
 #ifdef __cplusplus
@@ -24,15 +25,30 @@ extern "C" {
 #define ZXDH_MAX_MAC_ADDRS        (ZXDH_MAX_UC_MAC_ADDRS + ZXDH_MAX_MC_MAC_ADDRS)
 
 #define ZXDH_NUM_BARS             2
+#define ZXDH_RX_QUEUES_MAX        128U
+#define ZXDH_TX_QUEUES_MAX        128U
 
 struct zxdh_hw {
     struct rte_eth_dev *eth_dev;
-    uint64_t bar_addr[ZXDH_NUM_BARS];
+    struct zxdh_pci_common_cfg *common_cfg;
+    struct zxdh_net_config *dev_cfg;
 
+    uint64_t bar_addr[ZXDH_NUM_BARS];
+    uint64_t host_features;
+    uint64_t guest_features;
+    uint32_t max_queue_pairs;
     uint32_t speed;
+    uint32_t notify_off_multiplier;
+    uint16_t *notify_base;
+    uint16_t pcie_id;
     uint16_t device_id;
     uint16_t port_id;
 
+    uint8_t *isr;
+    uint8_t weak_barriers;
+    uint8_t use_msix;
+    uint8_t mac_addr[RTE_ETHER_ADDR_LEN];
+
     uint8_t duplex;
     uint8_t is_pf;
 };
diff --git a/drivers/net/zxdh/zxdh_pci.c b/drivers/net/zxdh/zxdh_pci.c
new file mode 100644
index 0000000000..68785aa03e
--- /dev/null
+++ b/drivers/net/zxdh/zxdh_pci.c
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2024 ZTE Corporation
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <rte_io.h>
+#include <rte_bus.h>
+#include <rte_pci.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+
+#include "zxdh_ethdev.h"
+#include "zxdh_pci.h"
+#include "zxdh_logs.h"
+
+#define ZXDH_PMD_DEFAULT_GUEST_FEATURES   \
+        (1ULL << ZXDH_NET_F_MRG_RXBUF | \
+        1ULL << ZXDH_NET_F_STATUS    | \
+        1ULL << ZXDH_NET_F_MQ        | \
+        1ULL << ZXDH_F_ANY_LAYOUT    | \
+        1ULL << ZXDH_F_VERSION_1     | \
+        1ULL << ZXDH_F_RING_PACKED   | \
+        1ULL << ZXDH_F_IN_ORDER      | \
+        1ULL << ZXDH_F_NOTIFICATION_DATA | \
+        1ULL << ZXDH_NET_F_MAC)
+
+static void
+zxdh_read_dev_config(struct zxdh_hw *hw, size_t offset,
+        void *dst, int32_t length)
+{
+    int32_t i       = 0;
+    uint8_t *p      = NULL;
+    uint8_t old_gen = 0;
+    uint8_t new_gen = 0;
+
+    do {
+        old_gen = rte_read8(&hw->common_cfg->config_generation);
+
+        p = dst;
+        for (i = 0;  i < length; i++)
+            *p++ = rte_read8((uint8_t *)hw->dev_cfg + offset + i);
+
+        new_gen = rte_read8(&hw->common_cfg->config_generation);
+    } while (old_gen != new_gen);
+}
+
+static void
+zxdh_write_dev_config(struct zxdh_hw *hw, size_t offset,
+        const void *src, int32_t length)
+{
+    int32_t i = 0;
+    const uint8_t *p = src;
+
+    for (i = 0;  i < length; i++)
+        rte_write8((*p++), (((uint8_t *)hw->dev_cfg) + offset + i));
+}
+
+static uint8_t
+zxdh_get_status(struct zxdh_hw *hw)
+{
+    return rte_read8(&hw->common_cfg->device_status);
+}
+
+static void
+zxdh_set_status(struct zxdh_hw *hw, uint8_t status)
+{
+    rte_write8(status, &hw->common_cfg->device_status);
+}
+
+static uint64_t
+zxdh_get_features(struct zxdh_hw *hw)
+{
+    uint32_t features_lo = 0;
+    uint32_t features_hi = 0;
+
+    rte_write32(0, &hw->common_cfg->device_feature_select);
+    features_lo = rte_read32(&hw->common_cfg->device_feature);
+
+    rte_write32(1, &hw->common_cfg->device_feature_select);
+    features_hi = rte_read32(&hw->common_cfg->device_feature);
+
+    return ((uint64_t)features_hi << 32) | features_lo;
+}
+
+static void
+zxdh_set_features(struct zxdh_hw *hw, uint64_t features)
+{
+    rte_write32(0, &hw->common_cfg->guest_feature_select);
+    rte_write32(features & ((1ULL << 32) - 1), &hw->common_cfg->guest_feature);
+    rte_write32(1, &hw->common_cfg->guest_feature_select);
+    rte_write32(features >> 32, &hw->common_cfg->guest_feature);
+}
+
+const struct zxdh_pci_ops zxdh_dev_pci_ops = {
+    .read_dev_cfg   = zxdh_read_dev_config,
+    .write_dev_cfg  = zxdh_write_dev_config,
+    .get_status     = zxdh_get_status,
+    .set_status     = zxdh_set_status,
+    .get_features   = zxdh_get_features,
+    .set_features   = zxdh_set_features,
+};
+
+uint16_t
+zxdh_pci_get_features(struct zxdh_hw *hw)
+{
+    return ZXDH_VTPCI_OPS(hw)->get_features(hw);
+}
+
+void
+zxdh_pci_reset(struct zxdh_hw *hw)
+{
+    PMD_DRV_LOG(INFO, "port %u device start reset, just wait...", hw->port_id);
+    uint32_t retry = 0;
+
+    ZXDH_VTPCI_OPS(hw)->set_status(hw, ZXDH_CONFIG_STATUS_RESET);
+    /* Flush status write and wait device ready max 3 seconds. */
+    while (ZXDH_VTPCI_OPS(hw)->get_status(hw) != ZXDH_CONFIG_STATUS_RESET) {
+        ++retry;
+        rte_delay_ms(1);
+    }
+    PMD_DRV_LOG(INFO, "port %u device reset %u ms done", hw->port_id, retry);
+}
+
+static void
+*get_cfg_addr(struct rte_pci_device *dev, struct zxdh_pci_cap *cap)
+{
+    uint8_t  bar    = cap->bar;
+    uint32_t length = cap->length;
+    uint32_t offset = cap->offset;
+
+    if (bar >= PCI_MAX_RESOURCE) {
+        PMD_DRV_LOG(ERR, "invalid bar: %u", bar);
+        return NULL;
+    }
+    if (offset + length < offset) {
+        PMD_DRV_LOG(ERR, "offset(%u) + length(%u) overflows", offset, length);
+        return NULL;
+    }
+    if (offset + length > dev->mem_resource[bar].len) {
+        PMD_DRV_LOG(ERR, "invalid cap: overflows bar space");
+        return NULL;
+    }
+    uint8_t *base = dev->mem_resource[bar].addr;
+
+    if (base == NULL) {
+        PMD_DRV_LOG(ERR, "bar %u base addr is NULL", bar);
+        return NULL;
+    }
+    return base + offset;
+}
+
+int32_t
+zxdh_read_pci_caps(struct rte_pci_device *dev, struct zxdh_hw *hw)
+{
+    struct zxdh_pci_cap cap;
+    uint8_t pos = 0;
+    int32_t ret = 0;
+
+    if (dev->mem_resource[0].addr == NULL) {
+        PMD_DRV_LOG(ERR, "bar0 base addr is NULL");
+        return -1;
+    }
+
+    hw->use_msix = zxdh_pci_msix_detect(dev);
+
+    pos = rte_pci_find_capability(dev, RTE_PCI_CAP_ID_VNDR);
+    while (pos) {
+        ret = rte_pci_read_config(dev, &cap, sizeof(cap), pos);
+        if (ret != sizeof(cap)) {
+            PMD_DRV_LOG(ERR, "failed to read pci cap at pos: %x ret %d", pos, ret);
+            break;
+        }
+        if (cap.cap_vndr != RTE_PCI_CAP_ID_VNDR) {
+            PMD_DRV_LOG(DEBUG, "[%2x] skipping non VNDR cap id: %02x",
+                pos, cap.cap_vndr);
+            goto next;
+        }
+        PMD_DRV_LOG(DEBUG, "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
+            pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
+
+        switch (cap.cfg_type) {
+        case ZXDH_PCI_CAP_COMMON_CFG:
+            hw->common_cfg = get_cfg_addr(dev, &cap);
+            break;
+        case ZXDH_PCI_CAP_NOTIFY_CFG: {
+            ret = rte_pci_read_config(dev, &hw->notify_off_multiplier,
+                        4, pos + sizeof(cap));
+            if (ret != 4)
+                PMD_DRV_LOG(ERR,
+                    "failed to read notify_off_multiplier, ret %d", ret);
+            else
+                hw->notify_base = get_cfg_addr(dev, &cap);
+            break;
+        }
+        case ZXDH_PCI_CAP_DEVICE_CFG:
+            hw->dev_cfg = get_cfg_addr(dev, &cap);
+            break;
+        case ZXDH_PCI_CAP_ISR_CFG:
+            hw->isr = get_cfg_addr(dev, &cap);
+            break;
+        case ZXDH_PCI_CAP_PCI_CFG: {
+            hw->pcie_id = *(uint16_t *)&cap.padding[1];
+            PMD_DRV_LOG(DEBUG, "get pcie id 0x%x", hw->pcie_id);
+
+            if ((hw->pcie_id >> 11) & 0x1) /* PF */ {
+                PMD_DRV_LOG(DEBUG, "EP %u PF %u",
+                    hw->pcie_id >> 12, (hw->pcie_id >> 8) & 0x7);
+            } else { /* VF */
+                PMD_DRV_LOG(DEBUG, "EP %u PF %u VF %u",
+                    hw->pcie_id >> 12,
+                    (hw->pcie_id >> 8) & 0x7,
+                    hw->pcie_id & 0xff);
+            }
+            break;
+        }
+        }
+next:
+    pos = cap.cap_next;
+    }
+    if (hw->common_cfg == NULL || hw->notify_base == NULL ||
+        hw->dev_cfg == NULL || hw->isr == NULL) {
+        PMD_DRV_LOG(ERR, "no zxdh pci device found.");
+        return -1;
+    }
+    return 0;
+}
+
+void
+zxdh_pci_read_dev_config(struct zxdh_hw *hw, size_t offset, void *dst, int32_t length)
+{
+    ZXDH_VTPCI_OPS(hw)->read_dev_cfg(hw, offset, dst, length);
+}
+
+void
+zxdh_get_pci_dev_config(struct zxdh_hw *hw)
+{
+    uint64_t guest_features = 0;
+    uint64_t nego_features = 0;
+    uint32_t max_queue_pairs = 0;
+
+    hw->host_features = zxdh_pci_get_features(hw);
+
+    guest_features = (uint64_t)ZXDH_PMD_DEFAULT_GUEST_FEATURES;
+    nego_features = guest_features & hw->host_features;
+
+    hw->guest_features = nego_features;
+
+    if (hw->guest_features & (1ULL << ZXDH_NET_F_MAC)) {
+        zxdh_pci_read_dev_config(hw, offsetof(struct zxdh_net_config, mac),
+                &hw->mac_addr, RTE_ETHER_ADDR_LEN);
+    } else {
+        rte_eth_random_addr(&hw->mac_addr[0]);
+    }
+
+    zxdh_pci_read_dev_config(hw, offsetof(struct zxdh_net_config, max_virtqueue_pairs),
+            &max_queue_pairs, sizeof(max_queue_pairs));
+
+    if (max_queue_pairs == 0)
+        hw->max_queue_pairs = ZXDH_RX_QUEUES_MAX;
+    else
+        hw->max_queue_pairs = RTE_MIN(ZXDH_RX_QUEUES_MAX, max_queue_pairs);
+    PMD_DRV_LOG(DEBUG, "set max queue pairs %d", hw->max_queue_pairs);
+}
+
+enum zxdh_msix_status zxdh_pci_msix_detect(struct rte_pci_device *dev)
+{
+    uint16_t flags = 0;
+    uint8_t pos = 0;
+    int16_t ret = 0;
+
+    pos = rte_pci_find_capability(dev, RTE_PCI_CAP_ID_MSIX);
+
+    if (pos > 0) {
+        ret = rte_pci_read_config(dev, &flags, 2, pos + RTE_PCI_MSIX_FLAGS);
+        if (ret == 2 && flags & RTE_PCI_MSIX_FLAGS_ENABLE)
+            return ZXDH_MSIX_ENABLED;
+        else
+            return ZXDH_MSIX_DISABLED;
+    }
+    return ZXDH_MSIX_NONE;
+}
diff --git a/drivers/net/zxdh/zxdh_pci.h b/drivers/net/zxdh/zxdh_pci.h
new file mode 100644
index 0000000000..7905911a34
--- /dev/null
+++ b/drivers/net/zxdh/zxdh_pci.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2024 ZTE Corporation
+ */
+
+#ifndef ZXDH_PCI_H
+#define ZXDH_PCI_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <bus_pci_driver.h>
+
+#include "zxdh_ethdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum zxdh_msix_status {
+    ZXDH_MSIX_NONE     = 0,
+    ZXDH_MSIX_DISABLED = 1,
+    ZXDH_MSIX_ENABLED  = 2
+};
+
+#define ZXDH_NET_F_MAC               5   /* Host has given MAC address. */
+#define ZXDH_NET_F_MRG_RXBUF         15  /* Host can merge receive buffers. */
+#define ZXDH_NET_F_STATUS            16  /* zxdh_net_config.status available */
+#define ZXDH_NET_F_MQ                22  /* Device supports Receive Flow Steering */
+#define ZXDH_F_ANY_LAYOUT            27 /* Can the device handle any descriptor layout */
+#define ZXDH_F_VERSION_1             32
+#define ZXDH_F_RING_PACKED           34
+#define ZXDH_F_IN_ORDER              35
+#define ZXDH_F_NOTIFICATION_DATA     38
+
+#define ZXDH_PCI_CAP_COMMON_CFG  1 /* Common configuration */
+#define ZXDH_PCI_CAP_NOTIFY_CFG  2 /* Notifications */
+#define ZXDH_PCI_CAP_ISR_CFG     3 /* ISR Status */
+#define ZXDH_PCI_CAP_DEVICE_CFG  4 /* Device specific configuration */
+#define ZXDH_PCI_CAP_PCI_CFG     5 /* PCI configuration access */
+
+/* Status byte for guest to report progress. */
+#define ZXDH_CONFIG_STATUS_RESET           0x00
+#define ZXDH_CONFIG_STATUS_ACK             0x01
+#define ZXDH_CONFIG_STATUS_DRIVER          0x02
+#define ZXDH_CONFIG_STATUS_DRIVER_OK       0x04
+#define ZXDH_CONFIG_STATUS_FEATURES_OK     0x08
+#define ZXDH_CONFIG_STATUS_DEV_NEED_RESET  0x40
+#define ZXDH_CONFIG_STATUS_FAILED          0x80
+
+struct zxdh_net_config {
+    /* The config defining mac address (if ZXDH_NET_F_MAC) */
+    uint8_t    mac[RTE_ETHER_ADDR_LEN];
+    /* See ZXDH_NET_F_STATUS and ZXDH_NET_S_* above */
+    uint16_t   status;
+    uint16_t   max_virtqueue_pairs;
+    uint16_t   mtu;
+    uint32_t   speed;
+    uint8_t    duplex;
+} __rte_packed;
+
+/* This is the PCI capability header: */
+struct zxdh_pci_cap {
+    uint8_t  cap_vndr;   /* Generic PCI field: PCI_CAP_ID_VNDR */
+    uint8_t  cap_next;   /* Generic PCI field: next ptr. */
+    uint8_t  cap_len;    /* Generic PCI field: capability length */
+    uint8_t  cfg_type;   /* Identifies the structure. */
+    uint8_t  bar;        /* Where to find it. */
+    uint8_t  padding[3]; /* Pad to full dword. */
+    uint32_t offset;     /* Offset within bar. */
+    uint32_t length;     /* Length of the structure, in bytes. */
+};
+
+/* Fields in ZXDH_PCI_CAP_COMMON_CFG: */
+struct zxdh_pci_common_cfg {
+    /* About the whole device. */
+    uint32_t device_feature_select; /* read-write */
+    uint32_t device_feature;    /* read-only */
+    uint32_t guest_feature_select;  /* read-write */
+    uint32_t guest_feature;     /* read-write */
+    uint16_t msix_config;       /* read-write */
+    uint16_t num_queues;        /* read-only */
+    uint8_t  device_status;     /* read-write */
+    uint8_t  config_generation; /* read-only */
+
+    /* About a specific virtqueue. */
+    uint16_t queue_select;      /* read-write */
+    uint16_t queue_size;        /* read-write, power of 2. */
+    uint16_t queue_msix_vector; /* read-write */
+    uint16_t queue_enable;      /* read-write */
+    uint16_t queue_notify_off;  /* read-only */
+    uint32_t queue_desc_lo;     /* read-write */
+    uint32_t queue_desc_hi;     /* read-write */
+    uint32_t queue_avail_lo;    /* read-write */
+    uint32_t queue_avail_hi;    /* read-write */
+    uint32_t queue_used_lo;     /* read-write */
+    uint32_t queue_used_hi;     /* read-write */
+};
+
+static inline int32_t vtpci_with_feature(struct zxdh_hw *hw, uint64_t bit)
+{
+    return (hw->guest_features & (1ULL << bit)) != 0;
+}
+
+struct zxdh_pci_ops {
+    void     (*read_dev_cfg)(struct zxdh_hw *hw, size_t offset, void *dst, int32_t len);
+    void     (*write_dev_cfg)(struct zxdh_hw *hw, size_t offset, const void *src, int32_t len);
+
+    uint8_t  (*get_status)(struct zxdh_hw *hw);
+    void     (*set_status)(struct zxdh_hw *hw, uint8_t status);
+
+    uint64_t (*get_features)(struct zxdh_hw *hw);
+    void     (*set_features)(struct zxdh_hw *hw, uint64_t features);
+};
+
+struct zxdh_hw_internal {
+    const struct zxdh_pci_ops *zxdh_vtpci_ops;
+};
+
+#define ZXDH_VTPCI_OPS(hw)  (zxdh_hw_internal[(hw)->port_id].zxdh_vtpci_ops)
+
+extern struct zxdh_hw_internal zxdh_hw_internal[RTE_MAX_ETHPORTS];
+extern const struct zxdh_pci_ops zxdh_dev_pci_ops;
+
+void zxdh_pci_reset(struct zxdh_hw *hw);
+void zxdh_pci_read_dev_config(struct zxdh_hw *hw, size_t offset,
+        void *dst, int32_t length);
+
+int32_t zxdh_read_pci_caps(struct rte_pci_device *dev, struct zxdh_hw *hw);
+void zxdh_get_pci_dev_config(struct zxdh_hw *hw);
+
+uint16_t zxdh_pci_get_features(struct zxdh_hw *hw);
+enum zxdh_msix_status zxdh_pci_msix_detect(struct rte_pci_device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZXDH_PCI_H */
-- 
2.27.0