Commit e4adffb8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dmaengine-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

Pull dmaengine updates from Vinod Koul:
 "New drivers/devices:

   - Support for QCOM SM8150 GPI DMA

  Updates:

   - Big pile of idxd updates including support for performance
     monitoring

   - Support in dw-edma for interleaved dma

   - Support for synchronize() in Xilinx driver"

* tag 'dmaengine-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (42 commits)
  dmaengine: idxd: Enable IDXD performance monitor support
  dmaengine: idxd: Add IDXD performance monitor support
  dmaengine: idxd: remove MSIX masking for interrupt handlers
  dmaengine: idxd: device cmd should use dedicated lock
  dmaengine: idxd: support reporting of halt interrupt
  dmaengine: idxd: enable SVA feature for IOMMU
  dmaengine: idxd: convert sprintf() to sysfs_emit() for all usages
  dmaengine: idxd: add interrupt handle request and release support
  dmaengine: idxd: add support for readonly config mode
  dmaengine: idxd: add percpu_ref to descriptor submission path
  dmaengine: idxd: remove detection of device type
  dmaengine: idxd: iax bus removal
  dmaengine: idxd: fix cdev setup and free device lifetime issues
  dmaengine: idxd: fix group conf_dev lifetime
  dmaengine: idxd: fix engine conf_dev lifetime
  dmaengine: idxd: fix wq conf_dev 'struct device' lifetime
  dmaengine: idxd: fix idxd conf_dev 'struct device' lifetime
  dmaengine: idxd: use ida for device instance enumeration
  dmaengine: idxd: removal of pcim managed mmio mapping
  dmaengine: idxd: cleanup pci interrupt vector allocation management
  ...
parents 8796ac1d 0bde4444
What: /sys/bus/event_source/devices/dsa*/format
Date: April 2021
KernelVersion: 5.13
Contact: Tom Zanussi <tom.zanussi@linux.intel.com>
Description: Read-only. Attribute group to describe the magic bits
that go into perf_event_attr.config or
perf_event_attr.config1 for the IDXD DSA pmu. (See also
ABI/testing/sysfs-bus-event_source-devices-format).
Each attribute in this group defines a bit range in
perf_event_attr.config or perf_event_attr.config1.
All supported attributes are listed below (See the
IDXD DSA Spec for possible attribute values)::
event_category = "config:0-3" - event category
event = "config:4-31" - event ID
filter_wq = "config1:0-31" - workqueue filter
filter_tc = "config1:32-39" - traffic class filter
filter_pgsz = "config1:40-43" - page size filter
filter_sz = "config1:44-51" - transfer size filter
filter_eng = "config1:52-59" - engine filter
What: /sys/bus/event_source/devices/dsa*/cpumask
Date: April 2021
KernelVersion: 5.13
Contact: Tom Zanussi <tom.zanussi@linux.intel.com>
Description: Read-only. This file always returns the cpu to which the
IDXD DSA pmu is bound for access to all dsa pmu
performance monitoring events.
......@@ -20,6 +20,7 @@ properties:
compatible:
enum:
- qcom,sdm845-gpi-dma
- qcom,sm8150-gpi-dma
reg:
maxItems: 1
......
......@@ -300,6 +300,18 @@ config INTEL_IDXD_SVM
depends on PCI_PASID
depends on PCI_IOV
config INTEL_IDXD_PERFMON
bool "Intel Data Accelerators performance monitor support"
depends on INTEL_IDXD
help
Enable performance monitor (pmu) support for the Intel(R)
data accelerators present in Intel Xeon CPU. With this
enabled, perf can be used to monitor the DSA (Intel Data
Streaming Accelerator) events described in the Intel DSA
spec.
If unsure, say N.
config INTEL_IOATDMA
tristate "Intel I/OAT DMA support"
depends on PCI && X86_64
......
......@@ -344,17 +344,6 @@ static inline int at_xdmac_chan_is_paused(struct at_xdmac_chan *atchan)
return test_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
}
static inline int at_xdmac_csize(u32 maxburst)
{
int csize;
csize = ffs(maxburst) - 1;
if (csize > 4)
csize = -EINVAL;
return csize;
};
static inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg)
{
return cfg & AT_XDMAC_CC_TYPE_PER_TRAN;
......
This diff is collapsed.
......@@ -15,15 +15,18 @@
#include "../virt-dma.h"
#define EDMA_LL_SZ 24
#define EDMA_MAX_WR_CH 8
#define EDMA_MAX_RD_CH 8
enum dw_edma_dir {
EDMA_DIR_WRITE = 0,
EDMA_DIR_READ
};
enum dw_edma_mode {
EDMA_MODE_LEGACY = 0,
EDMA_MODE_UNROLL
enum dw_edma_map_format {
EDMA_MF_EDMA_LEGACY = 0x0,
EDMA_MF_EDMA_UNROLL = 0x1,
EDMA_MF_HDMA_COMPAT = 0x5
};
enum dw_edma_request {
......@@ -38,6 +41,12 @@ enum dw_edma_status {
EDMA_ST_BUSY
};
enum dw_edma_xfer_type {
EDMA_XFER_SCATTER_GATHER = 0,
EDMA_XFER_CYCLIC,
EDMA_XFER_INTERLEAVED
};
struct dw_edma_chan;
struct dw_edma_chunk;
......@@ -82,11 +91,8 @@ struct dw_edma_chan {
int id;
enum dw_edma_dir dir;
off_t ll_off;
u32 ll_max;
off_t dt_off;
struct msi_msg msi;
enum dw_edma_request request;
......@@ -117,19 +123,23 @@ struct dw_edma {
u16 rd_ch_cnt;
struct dw_edma_region rg_region; /* Registers */
struct dw_edma_region ll_region; /* Linked list */
struct dw_edma_region dt_region; /* Data */
struct dw_edma_region ll_region_wr[EDMA_MAX_WR_CH];
struct dw_edma_region ll_region_rd[EDMA_MAX_RD_CH];
struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH];
struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH];
struct dw_edma_irq *irq;
int nr_irqs;
u32 version;
enum dw_edma_mode mode;
enum dw_edma_map_format mf;
struct dw_edma_chan *chan;
const struct dw_edma_core_ops *ops;
raw_spinlock_t lock; /* Only for legacy */
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif /* CONFIG_DEBUG_FS */
};
struct dw_edma_sg {
......@@ -146,12 +156,13 @@ struct dw_edma_cyclic {
struct dw_edma_transfer {
struct dma_chan *dchan;
union dw_edma_xfer {
struct dw_edma_sg sg;
struct dw_edma_cyclic cyclic;
struct dw_edma_sg sg;
struct dw_edma_cyclic cyclic;
struct dma_interleaved_template *il;
} xfer;
enum dma_transfer_direction direction;
unsigned long flags;
bool cyclic;
enum dw_edma_xfer_type type;
};
static inline
......
This diff is collapsed.
This diff is collapsed.
......@@ -23,6 +23,6 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first);
int dw_edma_v0_core_device_config(struct dw_edma_chan *chan);
/* eDMA debug fs callbacks */
void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip);
void dw_edma_v0_core_debugfs_off(void);
void dw_edma_v0_core_debugfs_off(struct dw_edma_chip *chip);
#endif /* _DW_EDMA_V0_CORE_H */
......@@ -38,7 +38,6 @@
#define CHANNEL_STR "channel"
#define REGISTERS_STR "registers"
static struct dentry *base_dir;
static struct dw_edma *dw;
static struct dw_edma_v0_regs __iomem *regs;
......@@ -55,7 +54,7 @@ struct debugfs_entries {
static int dw_edma_debugfs_u32_get(void *data, u64 *val)
{
void __iomem *reg = (void __force __iomem *)data;
if (dw->mode == EDMA_MODE_LEGACY &&
if (dw->mf == EDMA_MF_EDMA_LEGACY &&
reg >= (void __iomem *)&regs->type.legacy.ch) {
void __iomem *ptr = &regs->type.legacy.ch;
u32 viewport_sel = 0;
......@@ -114,12 +113,12 @@ static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs,
REGISTER(ch_control1),
REGISTER(ch_control2),
REGISTER(transfer_size),
REGISTER(sar_low),
REGISTER(sar_high),
REGISTER(dar_low),
REGISTER(dar_high),
REGISTER(llp_low),
REGISTER(llp_high),
REGISTER(sar.lsb),
REGISTER(sar.msb),
REGISTER(dar.lsb),
REGISTER(dar.msb),
REGISTER(llp.lsb),
REGISTER(llp.msb),
};
nr_entries = ARRAY_SIZE(debugfs_regs);
......@@ -132,17 +131,17 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
/* eDMA global registers */
WR_REGISTER(engine_en),
WR_REGISTER(doorbell),
WR_REGISTER(ch_arb_weight_low),
WR_REGISTER(ch_arb_weight_high),
WR_REGISTER(ch_arb_weight.lsb),
WR_REGISTER(ch_arb_weight.msb),
/* eDMA interrupts registers */
WR_REGISTER(int_status),
WR_REGISTER(int_mask),
WR_REGISTER(int_clear),
WR_REGISTER(err_status),
WR_REGISTER(done_imwr_low),
WR_REGISTER(done_imwr_high),
WR_REGISTER(abort_imwr_low),
WR_REGISTER(abort_imwr_high),
WR_REGISTER(done_imwr.lsb),
WR_REGISTER(done_imwr.msb),
WR_REGISTER(abort_imwr.lsb),
WR_REGISTER(abort_imwr.msb),
WR_REGISTER(ch01_imwr_data),
WR_REGISTER(ch23_imwr_data),
WR_REGISTER(ch45_imwr_data),
......@@ -152,8 +151,8 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
const struct debugfs_entries debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
WR_REGISTER_UNROLL(engine_chgroup),
WR_REGISTER_UNROLL(engine_hshake_cnt_low),
WR_REGISTER_UNROLL(engine_hshake_cnt_high),
WR_REGISTER_UNROLL(engine_hshake_cnt.lsb),
WR_REGISTER_UNROLL(engine_hshake_cnt.msb),
WR_REGISTER_UNROLL(ch0_pwr_en),
WR_REGISTER_UNROLL(ch1_pwr_en),
WR_REGISTER_UNROLL(ch2_pwr_en),
......@@ -174,7 +173,7 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
if (dw->mode == EDMA_MODE_UNROLL) {
if (dw->mf == EDMA_MF_HDMA_COMPAT) {
nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
regs_dir);
......@@ -200,19 +199,19 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
/* eDMA global registers */
RD_REGISTER(engine_en),
RD_REGISTER(doorbell),
RD_REGISTER(ch_arb_weight_low),
RD_REGISTER(ch_arb_weight_high),
RD_REGISTER(ch_arb_weight.lsb),
RD_REGISTER(ch_arb_weight.msb),
/* eDMA interrupts registers */
RD_REGISTER(int_status),
RD_REGISTER(int_mask),
RD_REGISTER(int_clear),
RD_REGISTER(err_status_low),
RD_REGISTER(err_status_high),
RD_REGISTER(err_status.lsb),
RD_REGISTER(err_status.msb),
RD_REGISTER(linked_list_err_en),
RD_REGISTER(done_imwr_low),
RD_REGISTER(done_imwr_high),
RD_REGISTER(abort_imwr_low),
RD_REGISTER(abort_imwr_high),
RD_REGISTER(done_imwr.lsb),
RD_REGISTER(done_imwr.msb),
RD_REGISTER(abort_imwr.lsb),
RD_REGISTER(abort_imwr.msb),
RD_REGISTER(ch01_imwr_data),
RD_REGISTER(ch23_imwr_data),
RD_REGISTER(ch45_imwr_data),
......@@ -221,8 +220,8 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
const struct debugfs_entries debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
RD_REGISTER_UNROLL(engine_chgroup),
RD_REGISTER_UNROLL(engine_hshake_cnt_low),
RD_REGISTER_UNROLL(engine_hshake_cnt_high),
RD_REGISTER_UNROLL(engine_hshake_cnt.lsb),
RD_REGISTER_UNROLL(engine_hshake_cnt.msb),
RD_REGISTER_UNROLL(ch0_pwr_en),
RD_REGISTER_UNROLL(ch1_pwr_en),
RD_REGISTER_UNROLL(ch2_pwr_en),
......@@ -243,7 +242,7 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
if (dw->mode == EDMA_MODE_UNROLL) {
if (dw->mf == EDMA_MF_HDMA_COMPAT) {
nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
regs_dir);
......@@ -272,7 +271,7 @@ static void dw_edma_debugfs_regs(void)
struct dentry *regs_dir;
int nr_entries;
regs_dir = debugfs_create_dir(REGISTERS_STR, base_dir);
regs_dir = debugfs_create_dir(REGISTERS_STR, dw->debugfs);
if (!regs_dir)
return;
......@@ -293,19 +292,23 @@ void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
if (!regs)
return;
base_dir = debugfs_create_dir(dw->name, NULL);
if (!base_dir)
dw->debugfs = debugfs_create_dir(dw->name, NULL);
if (!dw->debugfs)
return;
debugfs_create_u32("version", 0444, base_dir, &dw->version);
debugfs_create_u32("mode", 0444, base_dir, &dw->mode);
debugfs_create_u16("wr_ch_cnt", 0444, base_dir, &dw->wr_ch_cnt);
debugfs_create_u16("rd_ch_cnt", 0444, base_dir, &dw->rd_ch_cnt);
debugfs_create_u32("mf", 0444, dw->debugfs, &dw->mf);
debugfs_create_u16("wr_ch_cnt", 0444, dw->debugfs, &dw->wr_ch_cnt);
debugfs_create_u16("rd_ch_cnt", 0444, dw->debugfs, &dw->rd_ch_cnt);
dw_edma_debugfs_regs();
}
void dw_edma_v0_debugfs_off(void)
void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip)
{
debugfs_remove_recursive(base_dir);
dw = chip->dw;
if (!dw)
return;
debugfs_remove_recursive(dw->debugfs);
dw->debugfs = NULL;
}
......@@ -13,13 +13,13 @@
#ifdef CONFIG_DEBUG_FS
void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip);
void dw_edma_v0_debugfs_off(void);
void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip);
#else
static inline void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
{
}
static inline void dw_edma_v0_debugfs_off(void)
static inline void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip)
{
}
#endif /* CONFIG_DEBUG_FS */
......
......@@ -25,134 +25,209 @@
#define EDMA_V0_CH_EVEN_MSI_DATA_MASK GENMASK(15, 0)
struct dw_edma_v0_ch_regs {
u32 ch_control1; /* 0x000 */
u32 ch_control2; /* 0x004 */
u32 transfer_size; /* 0x008 */
u32 sar_low; /* 0x00c */
u32 sar_high; /* 0x010 */
u32 dar_low; /* 0x014 */
u32 dar_high; /* 0x018 */
u32 llp_low; /* 0x01c */
u32 llp_high; /* 0x020 */
};
u32 ch_control1; /* 0x0000 */
u32 ch_control2; /* 0x0004 */
u32 transfer_size; /* 0x0008 */
union {
u64 reg; /* 0x000c..0x0010 */
struct {
u32 lsb; /* 0x000c */
u32 msb; /* 0x0010 */
};
} sar;
union {
u64 reg; /* 0x0014..0x0018 */
struct {
u32 lsb; /* 0x0014 */
u32 msb; /* 0x0018 */
};
} dar;
union {
u64 reg; /* 0x001c..0x0020 */
struct {
u32 lsb; /* 0x001c */
u32 msb; /* 0x0020 */
};
} llp;
} __packed;
struct dw_edma_v0_ch {
struct dw_edma_v0_ch_regs wr; /* 0x200 */
u32 padding_1[55]; /* [0x224..0x2fc] */
struct dw_edma_v0_ch_regs rd; /* 0x300 */
u32 padding_2[55]; /* [0x324..0x3fc] */
};
struct dw_edma_v0_ch_regs wr; /* 0x0200 */
u32 padding_1[55]; /* 0x0224..0x02fc */
struct dw_edma_v0_ch_regs rd; /* 0x0300 */
u32 padding_2[55]; /* 0x0324..0x03fc */
} __packed;
struct dw_edma_v0_unroll {
u32 padding_1; /* 0x0f8 */
u32 wr_engine_chgroup; /* 0x100 */
u32 rd_engine_chgroup; /* 0x104 */
u32 wr_engine_hshake_cnt_low; /* 0x108 */
u32 wr_engine_hshake_cnt_high; /* 0x10c */
u32 padding_2[2]; /* [0x110..0x114] */
u32 rd_engine_hshake_cnt_low; /* 0x118 */
u32 rd_engine_hshake_cnt_high; /* 0x11c */
u32 padding_3[2]; /* [0x120..0x124] */
u32 wr_ch0_pwr_en; /* 0x128 */
u32 wr_ch1_pwr_en; /* 0x12c */
u32 wr_ch2_pwr_en; /* 0x130 */
u32 wr_ch3_pwr_en; /* 0x134 */
u32 wr_ch4_pwr_en; /* 0x138 */
u32 wr_ch5_pwr_en; /* 0x13c */
u32 wr_ch6_pwr_en; /* 0x140 */
u32 wr_ch7_pwr_en; /* 0x144 */
u32 padding_4[8]; /* [0x148..0x164] */
u32 rd_ch0_pwr_en; /* 0x168 */
u32 rd_ch1_pwr_en; /* 0x16c */
u32 rd_ch2_pwr_en; /* 0x170 */
u32 rd_ch3_pwr_en; /* 0x174 */
u32 rd_ch4_pwr_en; /* 0x178 */
u32 rd_ch5_pwr_en; /* 0x18c */
u32 rd_ch6_pwr_en; /* 0x180 */
u32 rd_ch7_pwr_en; /* 0x184 */
u32 padding_5[30]; /* [0x188..0x1fc] */
struct dw_edma_v0_ch ch[EDMA_V0_MAX_NR_CH]; /* [0x200..0x1120] */
};
u32 padding_1; /* 0x00f8 */
u32 wr_engine_chgroup; /* 0x0100 */
u32 rd_engine_chgroup; /* 0x0104 */
union {
u64 reg; /* 0x0108..0x010c */
struct {
u32 lsb; /* 0x0108 */
u32 msb; /* 0x010c */
};
} wr_engine_hshake_cnt;
u32 padding_2[2]; /* 0x0110..0x0114 */
union {
u64 reg; /* 0x0120..0x0124 */
struct {
u32 lsb; /* 0x0120 */
u32 msb; /* 0x0124 */
};
} rd_engine_hshake_cnt;
u32 padding_3[2]; /* 0x0120..0x0124 */
u32 wr_ch0_pwr_en; /* 0x0128 */
u32 wr_ch1_pwr_en; /* 0x012c */
u32 wr_ch2_pwr_en; /* 0x0130 */
u32 wr_ch3_pwr_en; /* 0x0134 */
u32 wr_ch4_pwr_en; /* 0x0138 */
u32 wr_ch5_pwr_en; /* 0x013c */
u32 wr_ch6_pwr_en; /* 0x0140 */
u32 wr_ch7_pwr_en; /* 0x0144 */
u32 padding_4[8]; /* 0x0148..0x0164 */
u32 rd_ch0_pwr_en; /* 0x0168 */
u32 rd_ch1_pwr_en; /* 0x016c */
u32 rd_ch2_pwr_en; /* 0x0170 */
u32 rd_ch3_pwr_en; /* 0x0174 */
u32 rd_ch4_pwr_en; /* 0x0178 */
u32 rd_ch5_pwr_en; /* 0x018c */
u32 rd_ch6_pwr_en; /* 0x0180 */
u32 rd_ch7_pwr_en; /* 0x0184 */
u32 padding_5[30]; /* 0x0188..0x01fc */
struct dw_edma_v0_ch ch[EDMA_V0_MAX_NR_CH]; /* 0x0200..0x1120 */
} __packed;
struct dw_edma_v0_legacy {
u32 viewport_sel; /* 0x0f8 */
struct dw_edma_v0_ch_regs ch; /* [0x100..0x120] */
};
u32 viewport_sel; /* 0x00f8 */
struct dw_edma_v0_ch_regs ch; /* 0x0100..0x0120 */
} __packed;
struct dw_edma_v0_regs {
/* eDMA global registers */
u32 ctrl_data_arb_prior; /* 0x000 */
u32 padding_1; /* 0x004 */
u32 ctrl; /* 0x008 */
u32 wr_engine_en; /* 0x00c */
u32 wr_doorbell; /* 0x010 */
u32 padding_2; /* 0x014 */
u32 wr_ch_arb_weight_low; /* 0x018 */
u32 wr_ch_arb_weight_high; /* 0x01c */
u32 padding_3[3]; /* [0x020..0x028] */
u32 rd_engine_en; /* 0x02c */
u32 rd_doorbell; /* 0x030 */
u32 padding_4; /* 0x034 */
u32 rd_ch_arb_weight_low; /* 0x038 */
u32 rd_ch_arb_weight_high; /* 0x03c */
u32 padding_5[3]; /* [0x040..0x048] */
u32 ctrl_data_arb_prior; /* 0x0000 */
u32 padding_1; /* 0x0004 */
u32 ctrl; /* 0x0008 */
u32 wr_engine_en; /* 0x000c */
u32 wr_doorbell; /* 0x0010 */
u32 padding_2; /* 0x0014 */
union {
u64 reg; /* 0x0018..0x001c */
struct {
u32 lsb; /* 0x0018 */
u32 msb; /* 0x001c */
};
} wr_ch_arb_weight;
u32 padding_3[3]; /* 0x0020..0x0028 */
u32 rd_engine_en; /* 0x002c */
u32 rd_doorbell; /* 0x0030 */
u32 padding_4; /* 0x0034 */
union {
u64 reg; /* 0x0038..0x003c */
struct {
u32 lsb; /* 0x0038 */
u32 msb; /* 0x003c */
};
} rd_ch_arb_weight;
u32 padding_5[3]; /* 0x0040..0x0048 */
/* eDMA interrupts registers */
u32 wr_int_status; /* 0x04c */
u32 padding_6; /* 0x050 */
u32 wr_int_mask; /* 0x054 */
u32 wr_int_clear; /* 0x058 */
u32 wr_err_status; /* 0x05c */
u32 wr_done_imwr_low; /* 0x060 */
u32 wr_done_imwr_high; /* 0x064 */
u32 wr_abort_imwr_low; /* 0x068 */
u32 wr_abort_imwr_high; /* 0x06c */
u32 wr_ch01_imwr_data; /* 0x070 */
u32 wr_ch23_imwr_data; /* 0x074 */
u32 wr_ch45_imwr_data; /* 0x078 */
u32 wr_ch67_imwr_data; /* 0x07c */
u32 padding_7[4]; /* [0x080..0x08c] */
u32 wr_linked_list_err_en; /* 0x090 */
u32 padding_8[3]; /* [0x094..0x09c] */
u32 rd_int_status; /* 0x0a0 */
u32 padding_9; /* 0x0a4 */
u32 rd_int_mask; /* 0x0a8 */
u32 rd_int_clear; /* 0x0ac */
u32 padding_10; /* 0x0b0 */
u32 rd_err_status_low; /* 0x0b4 */
u32 rd_err_status_high; /* 0x0b8 */
u32 padding_11[2]; /* [0x0bc..0x0c0] */
u32 rd_linked_list_err_en; /* 0x0c4 */
u32 padding_12; /* 0x0c8 */
u32 rd_done_imwr_low; /* 0x0cc */
u32 rd_done_imwr_high; /* 0x0d0 */
u32 rd_abort_imwr_low; /* 0x0d4 */
u32 rd_abort_imwr_high; /* 0x0d8 */
u32 rd_ch01_imwr_data; /* 0x0dc */
u32 rd_ch23_imwr_data; /* 0x0e0 */
u32 rd_ch45_imwr_data; /* 0x0e4 */
u32 rd_ch67_imwr_data; /* 0x0e8 */
u32 padding_13[4]; /* [0x0ec..0x0f8] */
u32 wr_int_status; /* 0x004c */
u32 padding_6; /* 0x0050 */
u32 wr_int_mask; /* 0x0054 */
u32 wr_int_clear; /* 0x0058 */
u32 wr_err_status; /* 0x005c */
union {
u64 reg; /* 0x0060..0x0064 */
struct {
u32 lsb; /* 0x0060 */
u32 msb; /* 0x0064 */
};
} wr_done_imwr;
union {
u64 reg; /* 0x0068..0x006c */
struct {
u32 lsb; /* 0x0068 */
u32 msb; /* 0x006c */
};
} wr_abort_imwr;
u32 wr_ch01_imwr_data; /* 0x0070 */
u32 wr_ch23_imwr_data; /* 0x0074 */
u32 wr_ch45_imwr_data; /* 0x0078 */
u32 wr_ch67_imwr_data; /* 0x007c */
u32 padding_7[4]; /* 0x0080..0x008c */
u32 wr_linked_list_err_en; /* 0x0090 */
u32 padding_8[3]; /* 0x0094..0x009c */
u32 rd_int_status; /* 0x00a0 */
u32 padding_9; /* 0x00a4 */
u32 rd_int_mask; /* 0x00a8 */
u32 rd_int_clear; /* 0x00ac */
u32 padding_10; /* 0x00b0 */
union {
u64 reg; /* 0x00b4..0x00b8 */
struct {
u32 lsb; /* 0x00b4 */
u32 msb; /* 0x00b8 */
};
} rd_err_status;
u32 padding_11[2]; /* 0x00bc..0x00c0 */
u32 rd_linked_list_err_en; /* 0x00c4 */
u32 padding_12; /* 0x00c8 */
union {
u64 reg; /* 0x00cc..0x00d0 */
struct {
u32 lsb; /* 0x00cc */
u32 msb; /* 0x00d0 */
};
} rd_done_imwr;
union {
u64 reg; /* 0x00d4..0x00d8 */
struct {
u32 lsb; /* 0x00d4 */
u32 msb; /* 0x00d8 */
};
} rd_abort_imwr;
u32 rd_ch01_imwr_data; /* 0x00dc */
u32 rd_ch23_imwr_data; /* 0x00e0 */
u32 rd_ch45_imwr_data; /* 0x00e4 */
u32 rd_ch67_imwr_data; /* 0x00e8 */
u32 padding_13[4]; /* 0x00ec..0x00f8 */
/* eDMA channel context grouping */
union dw_edma_v0_type {
struct dw_edma_v0_legacy legacy; /* [0x0f8..0x120] */
struct dw_edma_v0_unroll unroll; /* [0x0f8..0x1120] */
struct dw_edma_v0_legacy legacy; /* 0x00f8..0x0120 */
struct dw_edma_v0_unroll unroll; /* 0x00f8..0x1120 */
} type;
};
} __packed;
struct dw_edma_v0_lli {
u32 control;
u32 transfer_size;
u32 sar_low;
u32 sar_high;
u32 dar_low;
u32 dar_high;
};
union {
u64 reg;
struct {
u32 lsb;
u32 msb;
};
} sar;
union {
u64 reg;
struct {
u32 lsb;
u32 msb;
};
} dar;
} __packed;
struct dw_edma_v0_llp {
u32 control;
u32 reserved;
u32 llp_low;
u32 llp_high;
};
union {
u64 reg;
struct {
u32 lsb;
u32 msb;
};
} llp;
} __packed;
#endif /* _DW_EDMA_V0_REGS_H */
obj-$(CONFIG_INTEL_IDXD) += idxd.o
idxd-y := init.o irq.o device.o sysfs.o submit.o dma.o cdev.o
idxd-$(CONFIG_INTEL_IDXD_PERFMON) += perfmon.o
......@@ -39,15 +39,15 @@ struct idxd_user_context {
struct iommu_sva *sva;
};
enum idxd_cdev_cleanup {
CDEV_NORMAL = 0,
CDEV_FAILED,
};
static void idxd_cdev_dev_release(struct device *dev)
{
dev_dbg(dev, "releasing cdev device\n");
kfree(dev);
struct idxd_cdev *idxd_cdev = container_of(dev, struct idxd_cdev, dev);
struct idxd_cdev_context *cdev_ctx;
struct idxd_wq *wq = idxd_cdev->wq;
cdev_ctx = &ictx[wq->idxd->data->type];
ida_simple_remove(&cdev_ctx->minor_ida, idxd_cdev->minor);
kfree(idxd_cdev);
}
static struct device_type idxd_cdev_device_type = {
......@@ -62,14 +62,11 @@ static inline struct idxd_cdev *inode_idxd_cdev(struct inode *inode)
return container_of(cdev, struct idxd_cdev, cdev);
}
static inline struct idxd_wq *idxd_cdev_wq(struct idxd_cdev *idxd_cdev)
{
return container_of(idxd_cdev, struct idxd_wq, idxd_cdev);
}
static inline struct idxd_wq *inode_wq(struct inode *inode)
{
return idxd_cdev_wq(inode_idxd_cdev(inode));
struct idxd_cdev *idxd_cdev = inode_idxd_cdev(inode);
return idxd_cdev->wq;
}
static int idxd_cdev_open(struct inode *inode, struct file *filp)
......@@ -220,11 +217,10 @@ static __poll_t idxd_cdev_poll(struct file *filp,
struct idxd_user_context *ctx = filp->private_data;
struct idxd_wq *wq = ctx->wq;
struct idxd_device *idxd = wq->idxd;
struct idxd_cdev *idxd_cdev = &wq->idxd_cdev;
unsigned long flags;
__poll_t out = 0;
poll_wait(filp, &idxd_cdev->err_queue, wait);
poll_wait(filp, &wq->err_queue, wait);
spin_lock_irqsave(&idxd->dev_lock, flags);
if (idxd->sw_err.valid)
out = EPOLLIN | EPOLLRDNORM;
......@@ -243,101 +239,69 @@ static const struct file_operations idxd_cdev_fops = {
int idxd_cdev_get_major(struct idxd_device *idxd)
{
return MAJOR(ictx[idxd->type].devt);
return MAJOR(ictx[idxd->data->type].devt);
}
static int idxd_wq_cdev_dev_setup(struct idxd_wq *wq)
int idxd_wq_add_cdev(struct idxd_wq *wq)
{
struct idxd_device *idxd = wq->idxd;
struct idxd_cdev *idxd_cdev = &wq->idxd_cdev;
struct idxd_cdev_context *cdev_ctx;
struct idxd_cdev *idxd_cdev;
struct cdev *cdev;
struct device *dev;
int minor, rc;
struct idxd_cdev_context *cdev_ctx;
int rc, minor;
idxd_cdev->dev = kzalloc(sizeof(*idxd_cdev->dev), GFP_KERNEL);
if (!idxd_cdev->dev)
idxd_cdev = kzalloc(sizeof(*idxd_cdev), GFP_KERNEL);
if (!idxd_cdev)
return -ENOMEM;
dev = idxd_cdev->dev;
dev->parent = &idxd->pdev->dev;
dev_set_name(dev, "%s/wq%u.%u", idxd_get_dev_name(idxd),
idxd->id, wq->id);
dev->bus = idxd_get_bus_type(idxd);
cdev_ctx = &ictx[wq->idxd->type];
idxd_cdev->wq = wq;
cdev = &idxd_cdev->cdev;
dev = &idxd_cdev->dev;
cdev_ctx = &ictx[wq->idxd->data->type];
minor = ida_simple_get(&cdev_ctx->minor_ida, 0, MINORMASK, GFP_KERNEL);
if (minor < 0) {
rc = minor;
kfree(dev);
goto ida_err;
}
dev->devt = MKDEV(MAJOR(cdev_ctx->devt), minor);
dev->type = &idxd_cdev_device_type;
rc = device_register(dev);
if (rc < 0) {
dev_err(&idxd->pdev->dev, "device register failed\n");
goto dev_reg_err;
kfree(idxd_cdev);
return minor;
}
idxd_cdev->minor = minor;
return 0;
dev_reg_err:
ida_simple_remove(&cdev_ctx->minor_ida, MINOR(dev->devt));
put_device(dev);
ida_err:
idxd_cdev->dev = NULL;
return rc;
}
static void idxd_wq_cdev_cleanup(struct idxd_wq *wq,
enum idxd_cdev_cleanup cdev_state)
{
struct idxd_cdev *idxd_cdev = &wq->idxd_cdev;
struct idxd_cdev_context *cdev_ctx;
cdev_ctx = &ictx[wq->idxd->type];
if (cdev_state == CDEV_NORMAL)
cdev_del(&idxd_cdev->cdev);
device_unregister(idxd_cdev->dev);
/*
* The device_type->release() will be called on the device and free
* the allocated struct device. We can just forget it.
*/
ida_simple_remove(&cdev_ctx->minor_ida, idxd_cdev->minor);
idxd_cdev->dev = NULL;
idxd_cdev->minor = -1;
}
int idxd_wq_add_cdev(struct idxd_wq *wq)
{
struct idxd_cdev *idxd_cdev = &wq->idxd_cdev;
struct cdev *cdev = &idxd_cdev->cdev;
struct device *dev;
int rc;
device_initialize(dev);
dev->parent = &wq->conf_dev;
dev->bus = &dsa_bus_type;
dev->type = &idxd_cdev_device_type;
dev->devt = MKDEV(MAJOR(cdev_ctx->devt), minor);
rc = idxd_wq_cdev_dev_setup(wq);
rc = dev_set_name(dev, "%s/wq%u.%u", idxd->data->name_prefix, idxd->id, wq->id);
if (rc < 0)
return rc;
goto err;
dev = idxd_cdev->dev;
wq->idxd_cdev = idxd_cdev;
cdev_init(cdev, &idxd_cdev_fops);
cdev_set_parent(cdev, &dev->kobj);
rc = cdev_add(cdev, dev->devt, 1);
rc = cdev_device_add(cdev, dev);
if (rc) {
dev_dbg(&wq->idxd->pdev->dev, "cdev_add failed: %d\n", rc);
idxd_wq_cdev_cleanup(wq, CDEV_FAILED);
return rc;
goto err;
}
init_waitqueue_head(&idxd_cdev->err_queue);
return 0;
err:
put_device(dev);
wq->idxd_cdev = NULL;
return rc;
}
void idxd_wq_del_cdev(struct idxd_wq *wq)
{
idxd_wq_cdev_cleanup(wq, CDEV_NORMAL);
struct idxd_cdev *idxd_cdev;
struct idxd_cdev_context *cdev_ctx;
cdev_ctx = &ictx[wq->idxd->data->type];
idxd_cdev = wq->idxd_cdev;
wq->idxd_cdev = NULL;
cdev_device_del(&idxd_cdev->cdev, &idxd_cdev->dev);
put_device(&idxd_cdev->dev);
}
int idxd_cdev_register(void)
......
This diff is collapsed.
......@@ -14,7 +14,10 @@
static inline struct idxd_wq *to_idxd_wq(struct dma_chan *c)
{
return container_of(c, struct idxd_wq, dma_chan);
struct idxd_dma_chan *idxd_chan;
idxd_chan = container_of(c, struct idxd_dma_chan, chan);
return idxd_chan->wq;
}
void idxd_dma_complete_txd(struct idxd_desc *desc,
......@@ -135,7 +138,7 @@ static void idxd_dma_issue_pending(struct dma_chan *dma_chan)
{
}
dma_cookie_t idxd_dma_tx_submit(struct dma_async_tx_descriptor *tx)
static dma_cookie_t idxd_dma_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct dma_chan *c = tx->chan;
struct idxd_wq *wq = to_idxd_wq(c);
......@@ -156,14 +159,25 @@ dma_cookie_t idxd_dma_tx_submit(struct dma_async_tx_descriptor *tx)
static void idxd_dma_release(struct dma_device *device)
{
struct idxd_dma_dev *idxd_dma = container_of(device, struct idxd_dma_dev, dma);
kfree(idxd_dma);
}
int idxd_register_dma_device(struct idxd_device *idxd)
{
struct dma_device *dma = &idxd->dma_dev;
struct idxd_dma_dev *idxd_dma;
struct dma_device *dma;
struct device *dev = &idxd->pdev->dev;
int rc;
idxd_dma = kzalloc_node(sizeof(*idxd_dma), GFP_KERNEL, dev_to_node(dev));
if (!idxd_dma)
return -ENOMEM;
dma = &idxd_dma->dma;
INIT_LIST_HEAD(&dma->channels);
dma->dev = &idxd->pdev->dev;
dma->dev = dev;
dma_cap_set(DMA_PRIVATE, dma->cap_mask);
dma_cap_set(DMA_COMPLETION_NO_ORDER, dma->cap_mask);
......@@ -179,35 +193,72 @@ int idxd_register_dma_device(struct idxd_device *idxd)
dma->device_alloc_chan_resources = idxd_dma_alloc_chan_resources;
dma->device_free_chan_resources = idxd_dma_free_chan_resources;
return dma_async_device_register(&idxd->dma_dev);
rc = dma_async_device_register(dma);
if (rc < 0) {
kfree(idxd_dma);
return rc;
}
idxd_dma->idxd = idxd;
/*
* This pointer is protected by the refs taken by the dma_chan. It will remain valid
* as long as there are outstanding channels.
*/
idxd->idxd_dma = idxd_dma;
return 0;
}
void idxd_unregister_dma_device(struct idxd_device *idxd)
{
dma_async_device_unregister(&idxd->dma_dev);
dma_async_device_unregister(&idxd->idxd_dma->dma);
}
int idxd_register_dma_channel(struct idxd_wq *wq)
{
struct idxd_device *idxd = wq->idxd;
struct dma_device *dma = &idxd->dma_dev;
struct dma_chan *chan = &wq->dma_chan;
int rc;
struct dma_device *dma = &idxd->idxd_dma->dma;
struct device *dev = &idxd->pdev->dev;
struct idxd_dma_chan *idxd_chan;
struct dma_chan *chan;
int rc, i;
idxd_chan = kzalloc_node(sizeof(*idxd_chan), GFP_KERNEL, dev_to_node(dev));
if (!idxd_chan)
return -ENOMEM;
memset(&wq->dma_chan, 0, sizeof(struct dma_chan));
chan = &idxd_chan->chan;
chan->device = dma;
list_add_tail(&chan->device_node, &dma->channels);
for (i = 0; i < wq->num_descs; i++) {
struct idxd_desc *desc = wq->descs[i];
dma_async_tx_descriptor_init(&desc->txd, chan);
desc->txd.tx_submit = idxd_dma_tx_submit;
}
rc = dma_async_device_channel_register(dma, chan);
if (rc < 0)
if (rc < 0) {
kfree(idxd_chan);
return rc;
}
wq->idxd_chan = idxd_chan;
idxd_chan->wq = wq;
get_device(&wq->conf_dev);
return 0;
}
void idxd_unregister_dma_channel(struct idxd_wq *wq)
{
struct dma_chan *chan = &wq->dma_chan;
struct idxd_dma_chan *idxd_chan = wq->idxd_chan;
struct dma_chan *chan = &idxd_chan->chan;
struct idxd_dma_dev *idxd_dma = wq->idxd->idxd_dma;
dma_async_device_channel_unregister(&wq->idxd->dma_dev, chan);
dma_async_device_channel_unregister(&idxd_dma->dma, chan);
list_del(&chan->device_node);
kfree(wq->idxd_chan);
wq->idxd_chan = NULL;
put_device(&wq->conf_dev);
}
......@@ -8,12 +8,18 @@
#include <linux/percpu-rwsem.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/pci.h>
#include <linux/perf_event.h>
#include "registers.h"
#define IDXD_DRIVER_VERSION "1.00"
extern struct kmem_cache *idxd_desc_pool;
struct idxd_device;
struct idxd_wq;
#define IDXD_REG_TIMEOUT 50
#define IDXD_DRAIN_TIMEOUT 5000
......@@ -25,6 +31,7 @@ enum idxd_type {
};
#define IDXD_NAME_SIZE 128
#define IDXD_PMU_EVENT_MAX 64
struct idxd_device_driver {
struct device_driver drv;
......@@ -33,6 +40,7 @@ struct idxd_device_driver {
struct idxd_irq_entry {
struct idxd_device *idxd;
int id;
int vector;
struct llist_head pending_llist;
struct list_head work_list;
/*
......@@ -56,6 +64,31 @@ struct idxd_group {
int tc_b;
};
struct idxd_pmu {
struct idxd_device *idxd;
struct perf_event *event_list[IDXD_PMU_EVENT_MAX];
int n_events;
DECLARE_BITMAP(used_mask, IDXD_PMU_EVENT_MAX);
struct pmu pmu;
char name[IDXD_NAME_SIZE];
int cpu;
int n_counters;
int counter_width;
int n_event_categories;
bool per_counter_caps_supported;
unsigned long supported_event_categories;
unsigned long supported_filters;
int n_filters;
struct hlist_node cpuhp_node;
};
#define IDXD_MAX_PRIORITY 0xf
enum idxd_wq_state {
......@@ -75,10 +108,10 @@ enum idxd_wq_type {
};
struct idxd_cdev {
struct idxd_wq *wq;
struct cdev cdev;
struct device *dev;
struct device dev;
int minor;
struct wait_queue_head err_queue;
};
#define IDXD_ALLOCATED_BATCH_SIZE 128U
......@@ -96,10 +129,18 @@ enum idxd_complete_type {
IDXD_COMPLETE_DEV_FAIL,
};
struct idxd_dma_chan {
struct dma_chan chan;
struct idxd_wq *wq;
};
struct idxd_wq {
void __iomem *portal;
struct percpu_ref wq_active;
struct completion wq_dead;
struct device conf_dev;
struct idxd_cdev idxd_cdev;
struct idxd_cdev *idxd_cdev;
struct wait_queue_head err_queue;
struct idxd_device *idxd;
int id;
enum idxd_wq_type type;
......@@ -125,7 +166,7 @@ struct idxd_wq {
int compls_size;
struct idxd_desc **descs;
struct sbitmap_queue sbq;
struct dma_chan dma_chan;
struct idxd_dma_chan *idxd_chan;
char name[WQ_NAME_SIZE + 1];
u64 max_xfer_bytes;
u32 max_batch_size;
......@@ -147,6 +188,7 @@ struct idxd_hw {
union group_cap_reg group_cap;
union engine_cap_reg engine_cap;
struct opcap opcap;
u32 cmd_cap;
};
enum idxd_device_state {
......@@ -162,9 +204,22 @@ enum idxd_device_flag {
IDXD_FLAG_PASID_ENABLED,
};
struct idxd_device {
struct idxd_dma_dev {
struct idxd_device *idxd;
struct dma_device dma;
};
struct idxd_driver_data {
const char *name_prefix;
enum idxd_type type;
struct device_type *dev_type;
int compl_size;
int align;
};
struct idxd_device {
struct device conf_dev;
struct idxd_driver_data *data;
struct list_head list;
struct idxd_hw hw;
enum idxd_device_state state;
......@@ -177,10 +232,11 @@ struct idxd_device {
void __iomem *reg_base;
spinlock_t dev_lock; /* spinlock for device */
spinlock_t cmd_lock; /* spinlock for device commands */
struct completion *cmd_done;
struct idxd_group *groups;
struct idxd_wq *wqs;
struct idxd_engine *engines;
struct idxd_group **groups;
struct idxd_wq **wqs;
struct idxd_engine **engines;
struct iommu_sva *sva;
unsigned int pasid;
......@@ -202,17 +258,19 @@ struct idxd_device {
int token_limit;
int nr_tokens; /* non-reserved tokens */
unsigned int wqcfg_size;
int compl_size;
union sw_err_reg sw_err;
wait_queue_head_t cmd_waitq;
struct msix_entry *msix_entries;
int num_wq_irqs;
struct idxd_irq_entry *irq_entries;
struct dma_device dma_dev;
struct idxd_dma_dev *idxd_dma;
struct workqueue_struct *wq;
struct work_struct work;
int *int_handles;
struct idxd_pmu *idxd_pmu;
};
/* IDXD software descriptor */
......@@ -232,6 +290,7 @@ struct idxd_desc {
struct list_head list;
int id;
int cpu;
unsigned int vector;
struct idxd_wq *wq;
};
......@@ -242,6 +301,44 @@ extern struct bus_type dsa_bus_type;
extern struct bus_type iax_bus_type;
extern bool support_enqcmd;
extern struct ida idxd_ida;
extern struct device_type dsa_device_type;
extern struct device_type iax_device_type;
extern struct device_type idxd_wq_device_type;
extern struct device_type idxd_engine_device_type;
extern struct device_type idxd_group_device_type;
static inline bool is_dsa_dev(struct device *dev)
{
return dev->type == &dsa_device_type;
}
static inline bool is_iax_dev(struct device *dev)
{
return dev->type == &iax_device_type;
}
static inline bool is_idxd_dev(struct device *dev)
{
return is_dsa_dev(dev) || is_iax_dev(dev);
}
static inline bool is_idxd_wq_dev(struct device *dev)
{
return dev->type == &idxd_wq_device_type;
}
static inline bool is_idxd_wq_dmaengine(struct idxd_wq *wq)
{
if (wq->type == IDXD_WQT_KERNEL && strcmp(wq->name, "dmaengine") == 0)
return true;
return false;
}
static inline bool is_idxd_wq_cdev(struct idxd_wq *wq)
{
return wq->type == IDXD_WQT_USER;
}
static inline bool wq_dedicated(struct idxd_wq *wq)
{
......@@ -268,6 +365,11 @@ enum idxd_portal_prot {
IDXD_PORTAL_LIMITED,
};
enum idxd_interrupt_type {
IDXD_IRQ_MSIX = 0,
IDXD_IRQ_IMS,
};
static inline int idxd_get_wq_portal_offset(enum idxd_portal_prot prot)
{
return prot * 0x1000;
......@@ -279,18 +381,6 @@ static inline int idxd_get_wq_portal_full_offset(int wq_id,
return ((wq_id * 4) << PAGE_SHIFT) + idxd_get_wq_portal_offset(prot);
}
static inline void idxd_set_type(struct idxd_device *idxd)
{
struct pci_dev *pdev = idxd->pdev;
if (pdev->device == PCI_DEVICE_ID_INTEL_DSA_SPR0)
idxd->type = IDXD_TYPE_DSA;
else if (pdev->device == PCI_DEVICE_ID_INTEL_IAX_SPR0)
idxd->type = IDXD_TYPE_IAX;
else
idxd->type = IDXD_TYPE_UNKNOWN;
}
static inline void idxd_wq_get(struct idxd_wq *wq)
{
wq->client_count++;
......@@ -306,19 +396,17 @@ static inline int idxd_wq_refcount(struct idxd_wq *wq)
return wq->client_count;
};
const char *idxd_get_dev_name(struct idxd_device *idxd);
int idxd_register_bus_type(void);
void idxd_unregister_bus_type(void);
int idxd_setup_sysfs(struct idxd_device *idxd);
void idxd_cleanup_sysfs(struct idxd_device *idxd);
int idxd_register_devices(struct idxd_device *idxd);
void idxd_unregister_devices(struct idxd_device *idxd);
int idxd_register_driver(void);
void idxd_unregister_driver(void);
struct bus_type *idxd_get_bus_type(struct idxd_device *idxd);
void idxd_wqs_quiesce(struct idxd_device *idxd);
/* device interrupt control */
void idxd_msix_perm_setup(struct idxd_device *idxd);
void idxd_msix_perm_clear(struct idxd_device *idxd);
irqreturn_t idxd_irq_handler(int vec, void *data);
irqreturn_t idxd_misc_thread(int vec, void *data);
irqreturn_t idxd_wq_thread(int irq, void *data);
void idxd_mask_error_interrupts(struct idxd_device *idxd);
......@@ -336,8 +424,14 @@ void idxd_device_cleanup(struct idxd_device *idxd);
int idxd_device_config(struct idxd_device *idxd);
void idxd_device_wqs_clear_state(struct idxd_device *idxd);
void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid);
int idxd_device_load_config(struct idxd_device *idxd);
int idxd_device_request_int_handle(struct idxd_device *idxd, int idx, int *handle,
enum idxd_interrupt_type irq_type);
int idxd_device_release_int_handle(struct idxd_device *idxd, int handle,
enum idxd_interrupt_type irq_type);
/* work queue control */
void idxd_wqs_unmap_portal(struct idxd_device *idxd);
int idxd_wq_alloc_resources(struct idxd_wq *wq);
void idxd_wq_free_resources(struct idxd_wq *wq);
int idxd_wq_enable(struct idxd_wq *wq);
......@@ -349,6 +443,8 @@ void idxd_wq_unmap_portal(struct idxd_wq *wq);
void idxd_wq_disable_cleanup(struct idxd_wq *wq);
int idxd_wq_set_pasid(struct idxd_wq *wq, int pasid);
int idxd_wq_disable_pasid(struct idxd_wq *wq);
void idxd_wq_quiesce(struct idxd_wq *wq);
int idxd_wq_init_percpu_ref(struct idxd_wq *wq);
/* submission */
int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc);
......@@ -363,7 +459,6 @@ void idxd_unregister_dma_channel(struct idxd_wq *wq);
void idxd_parse_completion_status(u8 status, enum dmaengine_tx_result *res);
void idxd_dma_complete_txd(struct idxd_desc *desc,
enum idxd_complete_type comp_type);
dma_cookie_t idxd_dma_tx_submit(struct dma_async_tx_descriptor *tx);
/* cdev */
int idxd_cdev_register(void);
......@@ -372,4 +467,19 @@ int idxd_cdev_get_major(struct idxd_device *idxd);
int idxd_wq_add_cdev(struct idxd_wq *wq);
void idxd_wq_del_cdev(struct idxd_wq *wq);
/* perfmon */
#if IS_ENABLED(CONFIG_INTEL_IDXD_PERFMON)
int perfmon_pmu_init(struct idxd_device *idxd);
void perfmon_pmu_remove(struct idxd_device *idxd);
void perfmon_counter_overflow(struct idxd_device *idxd);
void perfmon_init(void);
void perfmon_exit(void);
#else
static inline int perfmon_pmu_init(struct idxd_device *idxd) { return 0; }
static inline void perfmon_pmu_remove(struct idxd_device *idxd) {}
static inline void perfmon_counter_overflow(struct idxd_device *idxd) {}
static inline void perfmon_init(void) {}
static inline void perfmon_exit(void) {}
#endif
#endif
This diff is collapsed.
......@@ -45,7 +45,7 @@ static void idxd_device_reinit(struct work_struct *work)
goto out;
for (i = 0; i < idxd->max_wqs; i++) {
struct idxd_wq *wq = &idxd->wqs[i];
struct idxd_wq *wq = idxd->wqs[i];
if (wq->state == IDXD_WQ_ENABLED) {
rc = idxd_wq_enable(wq);
......@@ -102,15 +102,6 @@ static int idxd_device_schedule_fault_process(struct idxd_device *idxd,
return 0;
}
irqreturn_t idxd_irq_handler(int vec, void *data)
{
struct idxd_irq_entry *irq_entry = data;
struct idxd_device *idxd = irq_entry->idxd;
idxd_mask_msix_vector(idxd, irq_entry->id);
return IRQ_WAKE_THREAD;
}
static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
{
struct device *dev = &idxd->pdev->dev;
......@@ -130,18 +121,18 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
if (idxd->sw_err.valid && idxd->sw_err.wq_idx_valid) {
int id = idxd->sw_err.wq_idx;
struct idxd_wq *wq = &idxd->wqs[id];
struct idxd_wq *wq = idxd->wqs[id];
if (wq->type == IDXD_WQT_USER)
wake_up_interruptible(&wq->idxd_cdev.err_queue);
wake_up_interruptible(&wq->err_queue);
} else {
int i;
for (i = 0; i < idxd->max_wqs; i++) {
struct idxd_wq *wq = &idxd->wqs[i];
struct idxd_wq *wq = idxd->wqs[i];
if (wq->type == IDXD_WQT_USER)
wake_up_interruptible(&wq->idxd_cdev.err_queue);
wake_up_interruptible(&wq->err_queue);
}
}
......@@ -165,11 +156,8 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
}
if (cause & IDXD_INTC_PERFMON_OVFL) {
/*
* Driver does not utilize perfmon counter overflow interrupt
* yet.
*/
val |= IDXD_INTC_PERFMON_OVFL;
perfmon_counter_overflow(idxd);
}
val ^= cause;
......@@ -202,6 +190,8 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
queue_work(idxd->wq, &idxd->work);
} else {
spin_lock_bh(&idxd->dev_lock);
idxd_wqs_quiesce(idxd);
idxd_wqs_unmap_portal(idxd);
idxd_device_wqs_clear_state(idxd);
dev_err(&idxd->pdev->dev,
"idxd halted, need %s.\n",
......@@ -235,7 +225,6 @@ irqreturn_t idxd_misc_thread(int vec, void *data)
iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
}
idxd_unmask_msix_vector(idxd, irq_entry->id);
return IRQ_HANDLED;
}
......@@ -392,8 +381,6 @@ irqreturn_t idxd_wq_thread(int irq, void *data)
int processed;
processed = idxd_desc_process(irq_entry);
idxd_unmask_msix_vector(irq_entry->idxd, irq_entry->id);
if (processed == 0)
return IRQ_NONE;
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2020 Intel Corporation. All rights rsvd. */
#ifndef _PERFMON_H_
#define _PERFMON_H_
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/sbitmap.h>
#include <linux/dmaengine.h>
#include <linux/percpu-rwsem.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/uuid.h>
#include <linux/idxd.h>
#include <linux/perf_event.h>
#include "registers.h"
static inline struct idxd_pmu *event_to_pmu(struct perf_event *event)
{
struct idxd_pmu *idxd_pmu;
struct pmu *pmu;
pmu = event->pmu;
idxd_pmu = container_of(pmu, struct idxd_pmu, pmu);
return idxd_pmu;
}
static inline struct idxd_device *event_to_idxd(struct perf_event *event)
{
struct idxd_pmu *idxd_pmu;
struct pmu *pmu;
pmu = event->pmu;
idxd_pmu = container_of(pmu, struct idxd_pmu, pmu);
return idxd_pmu->idxd;
}
static inline struct idxd_device *pmu_to_idxd(struct pmu *pmu)
{
struct idxd_pmu *idxd_pmu;
idxd_pmu = container_of(pmu, struct idxd_pmu, pmu);
return idxd_pmu->idxd;
}
enum dsa_perf_events {
DSA_PERF_EVENT_WQ = 0,
DSA_PERF_EVENT_ENGINE,
DSA_PERF_EVENT_ADDR_TRANS,
DSA_PERF_EVENT_OP,
DSA_PERF_EVENT_COMPL,
DSA_PERF_EVENT_MAX,
};
enum filter_enc {
FLT_WQ = 0,
FLT_TC,
FLT_PG_SZ,
FLT_XFER_SZ,
FLT_ENG,
FLT_MAX,
};
#define CONFIG_RESET 0x0000000000000001
#define CNTR_RESET 0x0000000000000002
#define CNTR_ENABLE 0x0000000000000001
#define INTR_OVFL 0x0000000000000002
#define COUNTER_FREEZE 0x00000000FFFFFFFF
#define COUNTER_UNFREEZE 0x0000000000000000
#define OVERFLOW_SIZE 32
#define CNTRCFG_ENABLE BIT(0)
#define CNTRCFG_IRQ_OVERFLOW BIT(1)
#define CNTRCFG_CATEGORY_SHIFT 8
#define CNTRCFG_EVENT_SHIFT 32
#define PERFMON_TABLE_OFFSET(_idxd) \
({ \
typeof(_idxd) __idxd = (_idxd); \
((__idxd)->reg_base + (__idxd)->perfmon_offset); \
})
#define PERFMON_REG_OFFSET(idxd, offset) \
(PERFMON_TABLE_OFFSET(idxd) + (offset))
#define PERFCAP_REG(idxd) (PERFMON_REG_OFFSET(idxd, IDXD_PERFCAP_OFFSET))
#define PERFRST_REG(idxd) (PERFMON_REG_OFFSET(idxd, IDXD_PERFRST_OFFSET))
#define OVFSTATUS_REG(idxd) (PERFMON_REG_OFFSET(idxd, IDXD_OVFSTATUS_OFFSET))
#define PERFFRZ_REG(idxd) (PERFMON_REG_OFFSET(idxd, IDXD_PERFFRZ_OFFSET))
#define FLTCFG_REG(idxd, cntr, flt) \
(PERFMON_REG_OFFSET(idxd, IDXD_FLTCFG_OFFSET) + ((cntr) * 32) + ((flt) * 4))
#define CNTRCFG_REG(idxd, cntr) \
(PERFMON_REG_OFFSET(idxd, IDXD_CNTRCFG_OFFSET) + ((cntr) * 8))
#define CNTRDATA_REG(idxd, cntr) \
(PERFMON_REG_OFFSET(idxd, IDXD_CNTRDATA_OFFSET) + ((cntr) * 8))
#define CNTRCAP_REG(idxd, cntr) \
(PERFMON_REG_OFFSET(idxd, IDXD_CNTRCAP_OFFSET) + ((cntr) * 8))
#define EVNTCAP_REG(idxd, category) \
(PERFMON_REG_OFFSET(idxd, IDXD_EVNTCAP_OFFSET) + ((category) * 8))
#define DEFINE_PERFMON_FORMAT_ATTR(_name, _format) \
static ssize_t __perfmon_idxd_##_name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, \
char *page) \
{ \
BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
return sprintf(page, _format "\n"); \
} \
static struct kobj_attribute format_attr_idxd_##_name = \
__ATTR(_name, 0444, __perfmon_idxd_##_name##_show, NULL)
#endif
......@@ -24,8 +24,8 @@ union gen_cap_reg {
u64 overlap_copy:1;
u64 cache_control_mem:1;
u64 cache_control_cache:1;
u64 cmd_cap:1;
u64 rsvd:3;
u64 int_handle_req:1;
u64 dest_readback:1;
u64 drain_readback:1;
u64 rsvd2:6;
......@@ -120,7 +120,8 @@ union gencfg_reg {
union genctrl_reg {
struct {
u32 softerr_int_en:1;
u32 rsvd:31;
u32 halt_int_en:1;
u32 rsvd:30;
};
u32 bits;
} __packed;
......@@ -180,8 +181,11 @@ enum idxd_cmd {
IDXD_CMD_DRAIN_PASID,
IDXD_CMD_ABORT_PASID,
IDXD_CMD_REQUEST_INT_HANDLE,
IDXD_CMD_RELEASE_INT_HANDLE,
};
#define CMD_INT_HANDLE_IMS 0x10000
#define IDXD_CMDSTS_OFFSET 0xa8
union cmdsts_reg {
struct {
......@@ -193,6 +197,8 @@ union cmdsts_reg {
u32 bits;
} __packed;
#define IDXD_CMDSTS_ACTIVE 0x80000000
#define IDXD_CMDSTS_ERR_MASK 0xff
#define IDXD_CMDSTS_RES_SHIFT 8
enum idxd_cmdsts_err {
IDXD_CMDSTS_SUCCESS = 0,
......@@ -228,6 +234,8 @@ enum idxd_cmdsts_err {
IDXD_CMDSTS_ERR_NO_HANDLE,
};
#define IDXD_CMDCAP_OFFSET 0xb0
#define IDXD_SWERR_OFFSET 0xc0
#define IDXD_SWERR_VALID 0x00000001
#define IDXD_SWERR_OVERFLOW 0x00000002
......@@ -378,4 +386,112 @@ union wqcfg {
#define GRPENGCFG_OFFSET(idxd_dev, n) ((idxd_dev)->grpcfg_offset + (n) * GRPCFG_SIZE + 32)
#define GRPFLGCFG_OFFSET(idxd_dev, n) ((idxd_dev)->grpcfg_offset + (n) * GRPCFG_SIZE + 40)
/* Following is performance monitor registers */
#define IDXD_PERFCAP_OFFSET 0x0
union idxd_perfcap {
struct {
u64 num_perf_counter:6;
u64 rsvd1:2;
u64 counter_width:8;
u64 num_event_category:4;
u64 global_event_category:16;
u64 filter:8;
u64 rsvd2:8;
u64 cap_per_counter:1;
u64 writeable_counter:1;
u64 counter_freeze:1;
u64 overflow_interrupt:1;
u64 rsvd3:8;
};
u64 bits;
} __packed;
#define IDXD_EVNTCAP_OFFSET 0x80
union idxd_evntcap {
struct {
u64 events:28;
u64 rsvd:36;
};
u64 bits;
} __packed;
struct idxd_event {
union {
struct {
u32 event_category:4;
u32 events:28;
};
u32 val;
};
} __packed;
#define IDXD_CNTRCAP_OFFSET 0x800
struct idxd_cntrcap {
union {
struct {
u32 counter_width:8;
u32 rsvd:20;
u32 num_events:4;
};
u32 val;
};
struct idxd_event events[];
} __packed;
#define IDXD_PERFRST_OFFSET 0x10
union idxd_perfrst {
struct {
u32 perfrst_config:1;
u32 perfrst_counter:1;
u32 rsvd:30;
};
u32 val;
} __packed;
#define IDXD_OVFSTATUS_OFFSET 0x30
#define IDXD_PERFFRZ_OFFSET 0x20
#define IDXD_CNTRCFG_OFFSET 0x100
union idxd_cntrcfg {
struct {
u64 enable:1;
u64 interrupt_ovf:1;
u64 global_freeze_ovf:1;
u64 rsvd1:5;
u64 event_category:4;
u64 rsvd2:20;
u64 events:28;
u64 rsvd3:4;
};
u64 val;
} __packed;
#define IDXD_FLTCFG_OFFSET 0x300
#define IDXD_CNTRDATA_OFFSET 0x200
union idxd_cntrdata {
struct {
u64 event_count_value;
};
u64 val;
} __packed;
union event_cfg {
struct {
u64 event_cat:4;
u64 event_enc:28;
};
u64 val;
} __packed;
union filter_cfg {
struct {
u64 wq:32;
u64 tc:8;
u64 pg_sz:4;
u64 xfer_sz:8;
u64 eng:8;
};
u64 val;
} __packed;
#endif
......@@ -15,18 +15,30 @@ static struct idxd_desc *__get_desc(struct idxd_wq *wq, int idx, int cpu)
desc = wq->descs[idx];
memset(desc->hw, 0, sizeof(struct dsa_hw_desc));
memset(desc->completion, 0, idxd->compl_size);
memset(desc->completion, 0, idxd->data->compl_size);
desc->cpu = cpu;
if (device_pasid_enabled(idxd))
desc->hw->pasid = idxd->pasid;
/*
* Descriptor completion vectors are 1-8 for MSIX. We will round
* robin through the 8 vectors.
* Descriptor completion vectors are 1...N for MSIX. We will round
* robin through the N vectors.
*/
wq->vec_ptr = (wq->vec_ptr % idxd->num_wq_irqs) + 1;
desc->hw->int_handle = wq->vec_ptr;
if (!idxd->int_handles) {
desc->hw->int_handle = wq->vec_ptr;
} else {
desc->vector = wq->vec_ptr;
/*
* int_handles are only for descriptor completion. However for device
* MSIX enumeration, vec 0 is used for misc interrupts. Therefore even
* though we are rotating through 1...N for descriptor interrupts, we
* need to acqurie the int_handles from 0..N-1.
*/
desc->hw->int_handle = idxd->int_handles[desc->vector - 1];
}
return desc;
}
......@@ -79,13 +91,15 @@ void idxd_free_desc(struct idxd_wq *wq, struct idxd_desc *desc)
int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc)
{
struct idxd_device *idxd = wq->idxd;
int vec = desc->hw->int_handle;
void __iomem *portal;
int rc;
if (idxd->state != IDXD_DEV_ENABLED)
return -EIO;
if (!percpu_ref_tryget_live(&wq->wq_active))
return -ENXIO;
portal = wq->portal;
/*
......@@ -108,13 +122,25 @@ int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc)
return rc;
}
percpu_ref_put(&wq->wq_active);
/*
* Pending the descriptor to the lockless list for the irq_entry
* that we designated the descriptor to.
*/
if (desc->hw->flags & IDXD_OP_FLAG_RCI)
llist_add(&desc->llnode,
&idxd->irq_entries[vec].pending_llist);
if (desc->hw->flags & IDXD_OP_FLAG_RCI) {
int vec;
/*
* If the driver is on host kernel, it would be the value
* assigned to interrupt handle, which is index for MSIX
* vector. If it's guest then can't use the int_handle since
* that is the index to IMS for the entire device. The guest
* device local index will be used.
*/
vec = !idxd->int_handles ? desc->hw->int_handle : desc->vector;
llist_add(&desc->llnode, &idxd->irq_entries[vec].pending_llist);
}
return 0;
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 - 2015 Linaro Ltd.
* Copyright (c) 2013 Hisilicon Limited.
* Copyright (c) 2013 HiSilicon Limited.
*/
#include <linux/sched.h>
#include <linux/device.h>
......@@ -1039,6 +1039,6 @@ static struct platform_driver k3_pdma_driver = {
module_platform_driver(k3_pdma_driver);
MODULE_DESCRIPTION("Hisilicon k3 DMA Driver");
MODULE_DESCRIPTION("HiSilicon k3 DMA Driver");
MODULE_ALIAS("platform:k3dma");
MODULE_LICENSE("GPL v2");
......@@ -2281,6 +2281,7 @@ static int gpi_probe(struct platform_device *pdev)
static const struct of_device_id gpi_of_match[] = {
{ .compatible = "qcom,sdm845-gpi-dma" },
{ .compatible = "qcom,sm8150-gpi-dma" },
{ },
};
MODULE_DEVICE_TABLE(of, gpi_of_match);
......
......@@ -90,12 +90,6 @@ static inline struct hidma_chan *to_hidma_chan(struct dma_chan *dmach)
return container_of(dmach, struct hidma_chan, chan);
}
static inline
struct hidma_desc *to_hidma_desc(struct dma_async_tx_descriptor *t)
{
return container_of(t, struct hidma_desc, desc);
}
static void hidma_free(struct hidma_dev *dmadev)
{
INIT_LIST_HEAD(&dmadev->ddev.channels);
......
......@@ -2453,6 +2453,13 @@ static int xilinx_dma_terminate_all(struct dma_chan *dchan)
return 0;
}
static void xilinx_dma_synchronize(struct dma_chan *dchan)
{
struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
tasklet_kill(&chan->tasklet);
}
/**
* xilinx_dma_channel_set_config - Configure VDMA channel
* Run-time configuration for Axi VDMA, supports:
......@@ -3074,6 +3081,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
xdev->common.device_free_chan_resources =
xilinx_dma_free_chan_resources;
xdev->common.device_terminate_all = xilinx_dma_terminate_all;
xdev->common.device_synchronize = xilinx_dma_synchronize;
xdev->common.device_tx_status = xilinx_dma_tx_status;
xdev->common.device_issue_pending = xilinx_dma_issue_pending;
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
......
......@@ -692,6 +692,36 @@ u8 pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
}
EXPORT_SYMBOL_GPL(pci_find_ht_capability);
/**
* pci_find_vsec_capability - Find a vendor-specific extended capability
* @dev: PCI device to query
* @vendor: Vendor ID for which capability is defined
* @cap: Vendor-specific capability ID
*
* If @dev has Vendor ID @vendor, search for a VSEC capability with
* VSEC ID @cap. If found, return the capability offset in
* config space; otherwise return 0.
*/
u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap)
{
u16 vsec = 0;
u32 header;
if (vendor != dev->vendor)
return 0;
while ((vsec = pci_find_next_ext_capability(dev, vsec,
PCI_EXT_CAP_ID_VNDR))) {
if (pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER,
&header) == PCIBIOS_SUCCESSFUL &&
PCI_VNDR_HEADER_ID(header) == cap)
return vsec;
}
return 0;
}
EXPORT_SYMBOL_GPL(pci_find_vsec_capability);
/**
* pci_find_parent_resource - return resource region of parent bus of given
* region
......
......@@ -169,6 +169,7 @@ enum cpuhp_state {
CPUHP_AP_PERF_X86_RAPL_ONLINE,
CPUHP_AP_PERF_X86_CQM_ONLINE,
CPUHP_AP_PERF_X86_CSTATE_ONLINE,
CPUHP_AP_PERF_X86_IDXD_ONLINE,
CPUHP_AP_PERF_S390_CF_ONLINE,
CPUHP_AP_PERF_S390_CFD_ONLINE,
CPUHP_AP_PERF_S390_SF_ONLINE,
......
......@@ -1085,6 +1085,7 @@ u8 pci_find_next_ht_capability(struct pci_dev *dev, u8 pos, int ht_cap);
u16 pci_find_ext_capability(struct pci_dev *dev, int cap);
u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap);
struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap);
u64 pci_get_dsn(struct pci_dev *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment