dma/hisilicon: add probing
This patch add dmadev instances create during the PCI probe, and destroy them during the PCI remove. Internal structures and HW definitions was also included. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
This commit is contained in:
parent
4d0d4cf327
commit
9e16317a38
@ -20,3 +20,14 @@ Device Setup
|
||||
|
||||
Kunpeng DMA devices will need to be bound to a suitable DPDK-supported
|
||||
user-space IO driver such as ``vfio-pci`` in order to be used by DPDK.
|
||||
|
||||
Device Probing and Initialization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once probed successfully, the device will appear as four ``dmadev``
|
||||
which can be accessed using API from the ``rte_dmadev`` library.
|
||||
|
||||
The name of the ``dmadev`` created is like "B:D.F-chX", e.g. DMA 0000:7b:00.0
|
||||
will create four ``dmadev``,
|
||||
the 1st ``dmadev`` name is "7b:00.0-ch0",
|
||||
and the 2nd ``dmadev`` name is "7b:00.0-ch1".
|
||||
|
@ -6,7 +6,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <rte_bus_pci.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_io.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_pci.h>
|
||||
#include <rte_dmadev_pmd.h>
|
||||
@ -30,6 +32,141 @@ RTE_LOG_REGISTER_DEFAULT(hisi_dma_logtype, INFO);
|
||||
#define HISI_DMA_ERR(hw, fmt, args...) \
|
||||
HISI_DMA_LOG_RAW(hw, ERR, fmt, ## args)
|
||||
|
||||
static uint32_t
|
||||
hisi_dma_queue_base(struct hisi_dma_dev *hw)
|
||||
{
|
||||
if (hw->reg_layout == HISI_DMA_REG_LAYOUT_HIP08)
|
||||
return HISI_DMA_HIP08_QUEUE_BASE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_write_reg(void *base, uint32_t off, uint32_t val)
|
||||
{
|
||||
rte_write32(rte_cpu_to_le_32(val),
|
||||
(volatile void *)((char *)base + off));
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_write_dev(struct hisi_dma_dev *hw, uint32_t off, uint32_t val)
|
||||
{
|
||||
hisi_dma_write_reg(hw->io_base, off, val);
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_write_queue(struct hisi_dma_dev *hw, uint32_t qoff, uint32_t val)
|
||||
{
|
||||
uint32_t off = hisi_dma_queue_base(hw) +
|
||||
hw->queue_id * HISI_DMA_QUEUE_REGION_SIZE + qoff;
|
||||
hisi_dma_write_dev(hw, off, val);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
hisi_dma_read_reg(void *base, uint32_t off)
|
||||
{
|
||||
uint32_t val = rte_read32((volatile void *)((char *)base + off));
|
||||
return rte_le_to_cpu_32(val);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
hisi_dma_read_dev(struct hisi_dma_dev *hw, uint32_t off)
|
||||
{
|
||||
return hisi_dma_read_reg(hw->io_base, off);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
hisi_dma_read_queue(struct hisi_dma_dev *hw, uint32_t qoff)
|
||||
{
|
||||
uint32_t off = hisi_dma_queue_base(hw) +
|
||||
hw->queue_id * HISI_DMA_QUEUE_REGION_SIZE + qoff;
|
||||
return hisi_dma_read_dev(hw, off);
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_update_bit(struct hisi_dma_dev *hw, uint32_t off, uint32_t pos,
|
||||
bool set)
|
||||
{
|
||||
uint32_t tmp = hisi_dma_read_dev(hw, off);
|
||||
uint32_t mask = 1u << pos;
|
||||
tmp = set ? tmp | mask : tmp & ~mask;
|
||||
hisi_dma_write_dev(hw, off, tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_update_queue_bit(struct hisi_dma_dev *hw, uint32_t qoff, uint32_t pos,
|
||||
bool set)
|
||||
{
|
||||
uint32_t tmp = hisi_dma_read_queue(hw, qoff);
|
||||
uint32_t mask = 1u << pos;
|
||||
tmp = set ? tmp | mask : tmp & ~mask;
|
||||
hisi_dma_write_queue(hw, qoff, tmp);
|
||||
}
|
||||
|
||||
#define hisi_dma_poll_hw_state(hw, val, cond, sleep_us, timeout_us) ({ \
|
||||
uint32_t timeout = 0; \
|
||||
while (timeout++ <= (timeout_us)) { \
|
||||
(val) = hisi_dma_read_queue(hw, HISI_DMA_QUEUE_FSM_REG); \
|
||||
if (cond) \
|
||||
break; \
|
||||
rte_delay_us(sleep_us); \
|
||||
} \
|
||||
(cond) ? 0 : -ETIME; \
|
||||
})
|
||||
|
||||
static int
|
||||
hisi_dma_reset_hw(struct hisi_dma_dev *hw)
|
||||
{
|
||||
#define POLL_SLEEP_US 100
|
||||
#define POLL_TIMEOUT_US 10000
|
||||
|
||||
uint32_t tmp;
|
||||
int ret;
|
||||
|
||||
hisi_dma_update_queue_bit(hw, HISI_DMA_QUEUE_CTRL0_REG,
|
||||
HISI_DMA_QUEUE_CTRL0_PAUSE_B, true);
|
||||
hisi_dma_update_queue_bit(hw, HISI_DMA_QUEUE_CTRL0_REG,
|
||||
HISI_DMA_QUEUE_CTRL0_EN_B, false);
|
||||
|
||||
ret = hisi_dma_poll_hw_state(hw, tmp,
|
||||
FIELD_GET(HISI_DMA_QUEUE_FSM_STS_M, tmp) != HISI_DMA_STATE_RUN,
|
||||
POLL_SLEEP_US, POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
HISI_DMA_ERR(hw, "disable dma timeout!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hisi_dma_update_queue_bit(hw, HISI_DMA_QUEUE_CTRL1_REG,
|
||||
HISI_DMA_QUEUE_CTRL1_RESET_B, true);
|
||||
hisi_dma_write_queue(hw, HISI_DMA_QUEUE_SQ_TAIL_REG, 0);
|
||||
hisi_dma_write_queue(hw, HISI_DMA_QUEUE_CQ_HEAD_REG, 0);
|
||||
hisi_dma_update_queue_bit(hw, HISI_DMA_QUEUE_CTRL0_REG,
|
||||
HISI_DMA_QUEUE_CTRL0_PAUSE_B, false);
|
||||
|
||||
ret = hisi_dma_poll_hw_state(hw, tmp,
|
||||
FIELD_GET(HISI_DMA_QUEUE_FSM_STS_M, tmp) == HISI_DMA_STATE_IDLE,
|
||||
POLL_SLEEP_US, POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
HISI_DMA_ERR(hw, "reset dma timeout!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_init_gbl(void *pci_bar, uint8_t revision)
|
||||
{
|
||||
struct hisi_dma_dev hw;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
hw.io_base = pci_bar;
|
||||
|
||||
if (revision == HISI_DMA_REVISION_HIP08B)
|
||||
hisi_dma_update_bit(&hw, HISI_DMA_HIP08_MODE_REG,
|
||||
HISI_DMA_HIP08_MODE_SEL_B, true);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
hisi_dma_reg_layout(uint8_t revision)
|
||||
{
|
||||
@ -49,6 +186,57 @@ hisi_dma_gen_pci_device_name(const struct rte_pci_device *pci_dev,
|
||||
pci_dev->addr.function);
|
||||
}
|
||||
|
||||
static void
|
||||
hisi_dma_gen_dev_name(const struct rte_pci_device *pci_dev,
|
||||
uint8_t queue_id, char *name, size_t size)
|
||||
{
|
||||
memset(name, 0, size);
|
||||
(void)snprintf(name, size, "%x:%x.%x-ch%u",
|
||||
pci_dev->addr.bus, pci_dev->addr.devid,
|
||||
pci_dev->addr.function, queue_id);
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_dma_create(struct rte_pci_device *pci_dev, uint8_t queue_id,
|
||||
uint8_t revision)
|
||||
{
|
||||
#define REG_PCI_BAR_INDEX 2
|
||||
|
||||
char name[RTE_DEV_NAME_MAX_LEN];
|
||||
struct rte_dma_dev *dev;
|
||||
struct hisi_dma_dev *hw;
|
||||
int ret;
|
||||
|
||||
hisi_dma_gen_dev_name(pci_dev, queue_id, name, sizeof(name));
|
||||
dev = rte_dma_pmd_allocate(name, pci_dev->device.numa_node,
|
||||
sizeof(*hw));
|
||||
if (dev == NULL) {
|
||||
HISI_DMA_LOG(ERR, "%s allocate dmadev fail!", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->device = &pci_dev->device;
|
||||
|
||||
hw = dev->data->dev_private;
|
||||
hw->data = dev->data;
|
||||
hw->revision = revision;
|
||||
hw->reg_layout = hisi_dma_reg_layout(revision);
|
||||
hw->io_base = pci_dev->mem_resource[REG_PCI_BAR_INDEX].addr;
|
||||
hw->queue_id = queue_id;
|
||||
|
||||
ret = hisi_dma_reset_hw(hw);
|
||||
if (ret) {
|
||||
HISI_DMA_LOG(ERR, "%s init device fail!", name);
|
||||
(void)rte_dma_pmd_release(name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev->state = RTE_DMA_DEV_READY;
|
||||
HISI_DMA_LOG(DEBUG, "%s create dmadev success!", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_dma_check_revision(struct rte_pci_device *pci_dev, const char *name,
|
||||
uint8_t *out_revision)
|
||||
@ -78,6 +266,7 @@ hisi_dma_probe(struct rte_pci_driver *pci_drv __rte_unused,
|
||||
{
|
||||
char name[RTE_DEV_NAME_MAX_LEN] = { 0 };
|
||||
uint8_t revision;
|
||||
uint8_t i;
|
||||
int ret;
|
||||
|
||||
hisi_dma_gen_pci_device_name(pci_dev, name, sizeof(name));
|
||||
@ -92,13 +281,34 @@ hisi_dma_probe(struct rte_pci_driver *pci_drv __rte_unused,
|
||||
return ret;
|
||||
HISI_DMA_LOG(DEBUG, "%s read PCI revision: 0x%x", name, revision);
|
||||
|
||||
hisi_dma_init_gbl(pci_dev->mem_resource[2].addr, revision);
|
||||
|
||||
for (i = 0; i < HISI_DMA_MAX_HW_QUEUES; i++) {
|
||||
ret = hisi_dma_create(pci_dev, i, revision);
|
||||
if (ret) {
|
||||
HISI_DMA_LOG(ERR, "%s create dmadev %u failed!",
|
||||
name, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_dma_remove(struct rte_pci_device *pci_dev)
|
||||
{
|
||||
RTE_SET_USED(pci_dev);
|
||||
char name[RTE_DEV_NAME_MAX_LEN];
|
||||
uint8_t i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < HISI_DMA_MAX_HW_QUEUES; i++) {
|
||||
hisi_dma_gen_dev_name(pci_dev, i, name, sizeof(name));
|
||||
ret = rte_dma_pmd_release(name);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,24 @@
|
||||
#ifndef HISI_DMADEV_H
|
||||
#define HISI_DMADEV_H
|
||||
|
||||
#include <rte_byteorder.h>
|
||||
#include <rte_common.h>
|
||||
|
||||
#define BIT(x) (1ul << (x))
|
||||
#define BITS_PER_LONG (__SIZEOF_LONG__ * 8)
|
||||
#define GENMASK(h, l) \
|
||||
(((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
|
||||
#define BF_SHF(x) (__builtin_ffsll(x) - 1)
|
||||
#define FIELD_GET(mask, reg) \
|
||||
((typeof(mask))(((reg) & (mask)) >> BF_SHF(mask)))
|
||||
|
||||
#define PCI_VENDOR_ID_HUAWEI 0x19e5
|
||||
#define HISI_DMA_DEVICE_ID 0xA122
|
||||
#define HISI_DMA_PCI_REVISION_ID_REG 0x08
|
||||
#define HISI_DMA_REVISION_HIP08B 0x21
|
||||
|
||||
#define HISI_DMA_MAX_HW_QUEUES 4
|
||||
|
||||
/**
|
||||
* The HIP08B(HiSilicon IP08) and later Chip(e.g. HiSilicon IP09) are DMA iEPs,
|
||||
* they have the same pci device id but with different pci revision.
|
||||
@ -21,4 +34,88 @@ enum {
|
||||
HISI_DMA_REG_LAYOUT_HIP08
|
||||
};
|
||||
|
||||
/**
|
||||
* Hardware PCI bar register MAP:
|
||||
*
|
||||
* --------------
|
||||
* | Misc-reg-0 |
|
||||
* | |
|
||||
* -------------- -> Queue base
|
||||
* | |
|
||||
* | Queue-0 |
|
||||
* | |
|
||||
* -------------- ---
|
||||
* | | ^
|
||||
* | Queue-1 | Queue region
|
||||
* | | v
|
||||
* -------------- ---
|
||||
* | ... |
|
||||
* | Queue-x |
|
||||
* | ... |
|
||||
* --------------
|
||||
* | Misc-reg-1 |
|
||||
* --------------
|
||||
*
|
||||
* As described above, a single queue register is continuous and occupies the
|
||||
* length of queue-region. The global offset for a single queue register is
|
||||
* calculated by:
|
||||
* offset = queue-base + (queue-id * queue-region) + reg-offset-in-region.
|
||||
*
|
||||
* The first part of queue region is basically the same for HIP08 and later chip
|
||||
* register layouts, therefore, HISI_QUEUE_* registers are defined for it.
|
||||
*/
|
||||
#define HISI_DMA_QUEUE_SQ_BASE_L_REG 0x0
|
||||
#define HISI_DMA_QUEUE_SQ_BASE_H_REG 0x4
|
||||
#define HISI_DMA_QUEUE_SQ_DEPTH_REG 0x8
|
||||
#define HISI_DMA_QUEUE_SQ_TAIL_REG 0xC
|
||||
#define HISI_DMA_QUEUE_CQ_BASE_L_REG 0x10
|
||||
#define HISI_DMA_QUEUE_CQ_BASE_H_REG 0x14
|
||||
#define HISI_DMA_QUEUE_CQ_DEPTH_REG 0x18
|
||||
#define HISI_DMA_QUEUE_CQ_HEAD_REG 0x1C
|
||||
#define HISI_DMA_QUEUE_CTRL0_REG 0x20
|
||||
#define HISI_DMA_QUEUE_CTRL0_EN_B 0
|
||||
#define HISI_DMA_QUEUE_CTRL0_PAUSE_B 4
|
||||
#define HISI_DMA_QUEUE_CTRL1_REG 0x24
|
||||
#define HISI_DMA_QUEUE_CTRL1_RESET_B 0
|
||||
#define HISI_DMA_QUEUE_FSM_REG 0x30
|
||||
#define HISI_DMA_QUEUE_FSM_STS_M GENMASK(3, 0)
|
||||
#define HISI_DMA_QUEUE_INT_STATUS_REG 0x40
|
||||
#define HISI_DMA_QUEUE_ERR_INT_NUM0_REG 0x84
|
||||
#define HISI_DMA_QUEUE_ERR_INT_NUM1_REG 0x88
|
||||
#define HISI_DMA_QUEUE_ERR_INT_NUM2_REG 0x8C
|
||||
#define HISI_DMA_QUEUE_REGION_SIZE 0x100
|
||||
|
||||
/**
|
||||
* HiSilicon IP08 DMA register and field define:
|
||||
*/
|
||||
#define HISI_DMA_HIP08_QUEUE_BASE 0x0
|
||||
#define HISI_DMA_HIP08_QUEUE_CTRL0_ERR_ABORT_B 2
|
||||
#define HISI_DMA_HIP08_QUEUE_INT_MASK_REG 0x44
|
||||
#define HISI_DMA_HIP08_QUEUE_INT_MASK_M GENMASK(14, 0)
|
||||
#define HISI_DMA_HIP08_QUEUE_ERR_INT_NUM3_REG 0x90
|
||||
#define HISI_DMA_HIP08_QUEUE_ERR_INT_NUM4_REG 0x94
|
||||
#define HISI_DMA_HIP08_QUEUE_ERR_INT_NUM5_REG 0x98
|
||||
#define HISI_DMA_HIP08_QUEUE_ERR_INT_NUM6_REG 0x48
|
||||
#define HISI_DMA_HIP08_MODE_REG 0x217C
|
||||
#define HISI_DMA_HIP08_MODE_SEL_B 0
|
||||
#define HISI_DMA_HIP08_DUMP_START_REG 0x2000
|
||||
#define HISI_DMA_HIP08_DUMP_END_REG 0x2280
|
||||
|
||||
/**
|
||||
* In fact, there are multiple states, but it need to pay attention to
|
||||
* the following two states for the driver:
|
||||
*/
|
||||
enum {
|
||||
HISI_DMA_STATE_IDLE = 0,
|
||||
HISI_DMA_STATE_RUN,
|
||||
};
|
||||
|
||||
struct hisi_dma_dev {
|
||||
struct rte_dma_dev_data *data;
|
||||
uint8_t revision; /**< PCI revision. */
|
||||
uint8_t reg_layout; /**< hardware register layout. */
|
||||
void *io_base;
|
||||
uint8_t queue_id; /**< hardware DMA queue index. */
|
||||
};
|
||||
|
||||
#endif /* HISI_DMADEV_H */
|
||||
|
Loading…
Reference in New Issue
Block a user