mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
dinghai: add hardware register access and PCI capability scanning
Implement PCI configuration space access, BAR mapping, capability scanning (common/notify/device), and hardware queue register definitions for DingHai PF device. Signed-off-by: Junyang Han <han.junyang@zte.com.cn> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
b8180f7977
commit
19df707eee
56
drivers/net/ethernet/zte/dinghai/dh_queue.h
Normal file
56
drivers/net/ethernet/zte/dinghai/dh_queue.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* ZTE DingHai Ethernet driver - PCI capability definitions
|
||||
* Copyright (c) 2022-2026, ZTE Corporation.
|
||||
*/
|
||||
|
||||
#ifndef __DH_QUEUE_H__
|
||||
#define __DH_QUEUE_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* This is the PCI capability header: */
|
||||
struct zxdh_pf_pci_cap {
|
||||
__u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */
|
||||
__u8 cap_next; /* Generic PCI field: next ptr. */
|
||||
__u8 cap_len; /* Generic PCI field: capability length */
|
||||
__u8 cfg_type; /* Identifies the structure. */
|
||||
__u8 bar; /* Where to find it. */
|
||||
__u8 id; /* Multiple capabilities of the same type */
|
||||
__u8 padding[2]; /* Pad to full dword. */
|
||||
__le32 offset; /* Offset within bar. */
|
||||
__le32 length; /* Length of the structure, in bytes. */
|
||||
};
|
||||
|
||||
/* Fields in ZXDH_PF_PCI_CAP_COMMON_CFG: */
|
||||
struct zxdh_pf_pci_common_cfg {
|
||||
/* About the whole device. */
|
||||
__le32 device_feature_select; /* read-write */
|
||||
__le32 device_feature; /* read-only */
|
||||
__le32 guest_feature_select; /* read-write */
|
||||
__le32 guest_feature; /* read-write */
|
||||
__le16 msix_config; /* read-write */
|
||||
__le16 num_queues; /* read-only */
|
||||
__u8 device_status; /* read-write */
|
||||
__u8 config_generation; /* read-only */
|
||||
|
||||
/* About a specific virtqueue. */
|
||||
__le16 queue_select; /* read-write */
|
||||
__le16 queue_size; /* read-write, power of 2. */
|
||||
__le16 queue_msix_vector; /* read-write */
|
||||
__le16 queue_enable; /* read-write */
|
||||
__le16 queue_notify_off; /* read-only */
|
||||
__le32 queue_desc_lo; /* read-write */
|
||||
__le32 queue_desc_hi; /* read-write */
|
||||
__le32 queue_avail_lo; /* read-write */
|
||||
__le32 queue_avail_hi; /* read-write */
|
||||
__le32 queue_used_lo; /* read-write */
|
||||
__le32 queue_used_hi; /* read-write */
|
||||
};
|
||||
|
||||
struct zxdh_pf_pci_notify_cap {
|
||||
struct zxdh_pf_pci_cap cap;
|
||||
__le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */
|
||||
};
|
||||
|
||||
#endif /* __DH_QUEUE_H__ */
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <net/devlink.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "en_pf.h"
|
||||
#include "dh_queue.h"
|
||||
|
||||
MODULE_AUTHOR("Junyang Han <han.junyang@zte.com.cn>");
|
||||
MODULE_DESCRIPTION("ZTE DingHai series Ethernet driver");
|
||||
@@ -103,6 +104,271 @@ void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev)
|
||||
pci_disable_device(zxdh_dev->pdev);
|
||||
}
|
||||
|
||||
int zxdh_pf_pci_find_capability(struct pci_dev *pdev, u8 cfg_type,
|
||||
u32 ioresource_types, int *bars)
|
||||
{
|
||||
int pos;
|
||||
u8 type;
|
||||
u8 bar;
|
||||
|
||||
for (pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR); pos > 0;
|
||||
pos = pci_find_next_capability(pdev, pos, PCI_CAP_ID_VNDR)) {
|
||||
pci_read_config_byte(pdev,
|
||||
pos + offsetof(struct zxdh_pf_pci_cap,
|
||||
cfg_type), &type);
|
||||
pci_read_config_byte(pdev,
|
||||
pos + offsetof(struct zxdh_pf_pci_cap, bar), &bar);
|
||||
|
||||
/* ignore structures with reserved BAR values */
|
||||
if (bar > ZXDH_PF_MAX_BAR_VAL)
|
||||
continue;
|
||||
|
||||
if (type == cfg_type) {
|
||||
if (pci_resource_len(pdev, bar) &&
|
||||
pci_resource_flags(pdev, bar) & ioresource_types) {
|
||||
*bars |= (1 << bar);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __iomem *zxdh_pf_map_capability(struct zxdh_core_dev *zxdh_dev, int off,
|
||||
size_t minlen, u32 align,
|
||||
u32 start, u32 size,
|
||||
size_t *len, resource_size_t *pa,
|
||||
u32 *bar_off)
|
||||
{
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
void __iomem *p;
|
||||
u32 offset;
|
||||
u32 length;
|
||||
u8 bar;
|
||||
|
||||
pci_read_config_byte(pdev,
|
||||
off + offsetof(struct zxdh_pf_pci_cap, bar), &bar);
|
||||
|
||||
if (bar > ZXDH_PF_MAX_BAR_VAL) {
|
||||
dev_err(zxdh_dev->device, "invalid bar %u\n", bar);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pci_read_config_dword(pdev,
|
||||
off + offsetof(struct zxdh_pf_pci_cap,
|
||||
offset), &offset);
|
||||
pci_read_config_dword(pdev,
|
||||
off + offsetof(struct zxdh_pf_pci_cap,
|
||||
length), &length);
|
||||
|
||||
if (bar_off)
|
||||
*bar_off = offset;
|
||||
|
||||
if (length <= start) {
|
||||
dev_err(zxdh_dev->device, "bad capability len %u (>%u expected)\n",
|
||||
length, start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (length - start < minlen) {
|
||||
dev_err(zxdh_dev->device, "bad capability len %u (>=%zu expected)\n",
|
||||
length, minlen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length -= start;
|
||||
if (start + offset < offset) {
|
||||
dev_err(zxdh_dev->device, "map wrap-around %u+%u\n", start, offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
offset += start;
|
||||
if (offset & (align - 1)) {
|
||||
dev_err(zxdh_dev->device, "offset %u not aligned to %u\n", offset, align);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (length > size)
|
||||
length = size;
|
||||
|
||||
if (len)
|
||||
*len = length;
|
||||
|
||||
if (length + offset < offset ||
|
||||
length + offset > pci_resource_len(pdev, bar)) {
|
||||
dev_err(zxdh_dev->device,
|
||||
"map %u@%u out of range on bar %u length %lu\n",
|
||||
length, offset, bar,
|
||||
(unsigned long)pci_resource_len(pdev, bar));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = pci_iomap_range(pdev, bar, offset, length);
|
||||
if (!p) {
|
||||
dev_err(zxdh_dev->device, "unable to map custom queue %u@%u on bar %u\n",
|
||||
length, offset, bar);
|
||||
} else if (pa) {
|
||||
*pa = pci_resource_start(pdev, bar) + offset;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int zxdh_pf_common_cfg_init(struct zxdh_core_dev *zxdh_dev)
|
||||
{
|
||||
struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
int common;
|
||||
|
||||
/* check for a common config: if not, use legacy mode (bar 0). */
|
||||
common = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_COMMON_CFG,
|
||||
IORESOURCE_MEM,
|
||||
&pf_dev->modern_bars);
|
||||
if (!common) {
|
||||
dev_err(zxdh_dev->device,
|
||||
"missing capabilities, leaving for legacy driver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pf_dev->common = zxdh_pf_map_capability(zxdh_dev, common,
|
||||
sizeof(struct zxdh_pf_pci_common_cfg),
|
||||
ZXDH_PF_ALIGN4, 0,
|
||||
sizeof(struct zxdh_pf_pci_common_cfg),
|
||||
NULL, NULL, NULL);
|
||||
if (!pf_dev->common) {
|
||||
dev_err(zxdh_dev->device, "pf_dev->common is null\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zxdh_pf_notify_cfg_init(struct zxdh_core_dev *zxdh_dev)
|
||||
{
|
||||
struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
u32 notify_length;
|
||||
u32 notify_offset;
|
||||
int notify;
|
||||
|
||||
/* If common is there, these should be too... */
|
||||
notify = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_NOTIFY_CFG,
|
||||
IORESOURCE_MEM,
|
||||
&pf_dev->modern_bars);
|
||||
if (!notify) {
|
||||
dev_err(zxdh_dev->device, "missing notify cfg capability\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pci_read_config_dword(pdev,
|
||||
notify + offsetof(struct zxdh_pf_pci_notify_cap,
|
||||
notify_off_multiplier),
|
||||
&pf_dev->notify_offset_multiplier);
|
||||
pci_read_config_dword(pdev,
|
||||
notify + offsetof(struct zxdh_pf_pci_notify_cap,
|
||||
cap.length), ¬ify_length);
|
||||
pci_read_config_dword(pdev,
|
||||
notify + offsetof(struct zxdh_pf_pci_notify_cap,
|
||||
cap.offset), ¬ify_offset);
|
||||
|
||||
/* We don't know how many VQs we'll map, ahead of the time.
|
||||
* If notify length is small, map it all now. Otherwise,
|
||||
* map each VQ individually later.
|
||||
*/
|
||||
if (notify_length <= PAGE_SIZE - (notify_offset % PAGE_SIZE)) {
|
||||
pf_dev->notify_base = zxdh_pf_map_capability(zxdh_dev, notify,
|
||||
ZXDH_PF_MAP_MINLEN2,
|
||||
ZXDH_PF_ALIGN2, 0,
|
||||
notify_length,
|
||||
&pf_dev->notify_len,
|
||||
&pf_dev->notify_pa, NULL);
|
||||
if (!pf_dev->notify_base) {
|
||||
dev_err(zxdh_dev->device, "pf_dev->notify_base is null\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
pf_dev->notify_map_cap = notify;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zxdh_pf_device_cfg_init(struct zxdh_core_dev *zxdh_dev)
|
||||
{
|
||||
struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
int device;
|
||||
|
||||
/* Device capability is only mandatory for
|
||||
* devices that have device-specific configuration.
|
||||
*/
|
||||
device = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_DEVICE_CFG,
|
||||
IORESOURCE_MEM,
|
||||
&pf_dev->modern_bars);
|
||||
|
||||
/* we don't know how much we should map,
|
||||
* but PAGE_SIZE is more than enough for all existing devices.
|
||||
*/
|
||||
if (device) {
|
||||
pf_dev->device = zxdh_pf_map_capability(zxdh_dev, device, 0,
|
||||
ZXDH_PF_ALIGN4, 0, PAGE_SIZE,
|
||||
&pf_dev->device_len, NULL,
|
||||
&pf_dev->dev_cfg_bar_off);
|
||||
if (!pf_dev->device) {
|
||||
dev_err(zxdh_dev->device, "pf_dev->device is null\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zxdh_pf_modern_cfg_uninit(struct zxdh_core_dev *zxdh_dev)
|
||||
{
|
||||
struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
|
||||
if (pf_dev->device)
|
||||
pci_iounmap(pdev, pf_dev->device);
|
||||
if (pf_dev->notify_base)
|
||||
pci_iounmap(pdev, pf_dev->notify_base);
|
||||
pci_iounmap(pdev, pf_dev->common);
|
||||
}
|
||||
|
||||
int zxdh_pf_modern_cfg_init(struct zxdh_core_dev *zxdh_dev)
|
||||
{
|
||||
struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
|
||||
struct pci_dev *pdev = zxdh_dev->pdev;
|
||||
int ret;
|
||||
|
||||
ret = zxdh_pf_common_cfg_init(zxdh_dev);
|
||||
if (ret) {
|
||||
dev_err(zxdh_dev->device, "zxdh_pf_common_cfg_init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = zxdh_pf_notify_cfg_init(zxdh_dev);
|
||||
if (ret) {
|
||||
dev_err(zxdh_dev->device, "zxdh_pf_notify_cfg_init failed: %d\n", ret);
|
||||
goto err_map_notify;
|
||||
}
|
||||
|
||||
ret = zxdh_pf_device_cfg_init(zxdh_dev);
|
||||
if (ret) {
|
||||
dev_err(zxdh_dev->device, "zxdh_pf_device_cfg_init failed: %d\n", ret);
|
||||
goto err_map_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_map_device:
|
||||
if (pf_dev->notify_base)
|
||||
pci_iounmap(pdev, pf_dev->notify_base);
|
||||
err_map_notify:
|
||||
pci_iounmap(pdev, pf_dev->common);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct zxdh_core_dev *zxdh_dev;
|
||||
@@ -133,10 +399,18 @@ static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
goto err_pci_init;
|
||||
}
|
||||
|
||||
ret = zxdh_pf_modern_cfg_init(zxdh_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "zxdh_pf_modern_cfg_init failed: %d\n", ret);
|
||||
goto err_cfg_init;
|
||||
}
|
||||
|
||||
devlink_register(devlink);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cfg_init:
|
||||
zxdh_pf_pci_close(zxdh_dev);
|
||||
err_pci_init:
|
||||
zxdh_core_free_priv(zxdh_dev);
|
||||
err_pf_dev:
|
||||
@@ -150,6 +424,7 @@ static void zxdh_pf_remove(struct pci_dev *pdev)
|
||||
struct devlink *devlink = priv_to_devlink(zxdh_dev);
|
||||
|
||||
devlink_unregister(devlink);
|
||||
zxdh_pf_modern_cfg_uninit(zxdh_dev);
|
||||
zxdh_pf_pci_close(zxdh_dev);
|
||||
zxdh_core_free_priv(zxdh_dev);
|
||||
devlink_free(devlink);
|
||||
|
||||
@@ -7,10 +7,28 @@
|
||||
#ifndef __ZXDH_EN_PF_H__
|
||||
#define __ZXDH_EN_PF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ZXDH_PF_VENDOR_ID 0x1cf2
|
||||
#define ZXDH_PF_DEVICE_ID 0x8040
|
||||
#define ZXDH_VF_DEVICE_ID 0x8041
|
||||
|
||||
/* Common configuration */
|
||||
#define ZXDH_PCI_CAP_COMMON_CFG 1
|
||||
/* Notifications */
|
||||
#define ZXDH_PCI_CAP_NOTIFY_CFG 2
|
||||
/* ISR access */
|
||||
#define ZXDH_PCI_CAP_ISR_CFG 3
|
||||
/* Device specific configuration */
|
||||
#define ZXDH_PCI_CAP_DEVICE_CFG 4
|
||||
/* PCI configuration access */
|
||||
#define ZXDH_PCI_CAP_PCI_CFG 5
|
||||
|
||||
#define ZXDH_PF_MAX_BAR_VAL 0x5
|
||||
#define ZXDH_PF_ALIGN4 4
|
||||
#define ZXDH_PF_ALIGN2 2
|
||||
#define ZXDH_PF_MAP_MINLEN2 2
|
||||
|
||||
struct zxdh_core_dev {
|
||||
struct device *device;
|
||||
struct pci_dev *pdev;
|
||||
@@ -19,11 +37,39 @@ struct zxdh_core_dev {
|
||||
};
|
||||
|
||||
struct zxdh_pf_dev {
|
||||
struct zxdh_pf_pci_common_cfg __iomem *common;
|
||||
/* Device-specific data (non-legacy mode) */
|
||||
/* Base of vq notifications (non-legacy mode). */
|
||||
void __iomem *device;
|
||||
void __iomem *notify_base;
|
||||
/* Physical base of vq notifications */
|
||||
resource_size_t notify_pa;
|
||||
/* So we can sanity-check accesses. */
|
||||
size_t notify_len;
|
||||
size_t device_len;
|
||||
/* Capability for when we need to map notifications per-vq. */
|
||||
s32 notify_map_cap;
|
||||
u32 notify_offset_multiplier;
|
||||
/* Multiply queue_notify_off by this value. (non-legacy mode). */
|
||||
s32 modern_bars;
|
||||
void __iomem *pci_ioremap_addr[6];
|
||||
u32 dev_cfg_bar_off;
|
||||
};
|
||||
|
||||
void *zxdh_core_alloc_priv(struct zxdh_core_dev *zxdh_dev, size_t size);
|
||||
void zxdh_core_free_priv(struct zxdh_core_dev *zxdh_dev);
|
||||
void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev);
|
||||
int zxdh_pf_pci_find_capability(struct pci_dev *pdev, u8 cfg_type,
|
||||
u32 ioresource_types, int *bars);
|
||||
void __iomem *zxdh_pf_map_capability(struct zxdh_core_dev *zxdh_dev, int off,
|
||||
size_t minlen, u32 align,
|
||||
u32 start, u32 size,
|
||||
size_t *len, resource_size_t *pa,
|
||||
u32 *bar_off);
|
||||
int zxdh_pf_common_cfg_init(struct zxdh_core_dev *zxdh_dev);
|
||||
int zxdh_pf_notify_cfg_init(struct zxdh_core_dev *zxdh_dev);
|
||||
int zxdh_pf_device_cfg_init(struct zxdh_core_dev *zxdh_dev);
|
||||
void zxdh_pf_modern_cfg_uninit(struct zxdh_core_dev *zxdh_dev);
|
||||
int zxdh_pf_modern_cfg_init(struct zxdh_core_dev *zxdh_dev);
|
||||
|
||||
#endif /* __ZXDH_EN_PF_H__ */
|
||||
|
||||
Reference in New Issue
Block a user