mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine
Pull dmaengine updates from Vinod Koul:
"New support:
- Apple admac t8112 device support
- StarFive JH7110 DMA controller
Updates:
- Big pile of idxd updates to support IAA 2.0 device capabilities,
DSA 2.0 Event Log and completion record faulting features and
new DSA operations
- at_xdmac supend & resume updates and driver code cleanup
- k3-udma supend & resume support
- k3-psil thread support for J784s4"
* tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (57 commits)
dmaengine: idxd: add per wq PRS disable
dmaengine: idxd: add pid to exported sysfs attribute for opened file
dmaengine: idxd: expose fault counters to sysfs
dmaengine: idxd: add a device to represent the file opened
dmaengine: idxd: add per file user counters for completion record faults
dmaengine: idxd: process batch descriptor completion record faults
dmaengine: idxd: add descs_completed field for completion record
dmaengine: idxd: process user page faults for completion record
dmaengine: idxd: add idxd_copy_cr() to copy user completion record during page fault handling
dmaengine: idxd: create kmem cache for event log fault items
dmaengine: idxd: add per DSA wq workqueue for processing cr faults
dmanegine: idxd: add debugfs for event log dump
dmaengine: idxd: add interrupt handling for event log
dmaengine: idxd: setup event log configuration
dmaengine: idxd: add event log size sysfs attribute
dmaengine: idxd: make misc interrupt one shot
dt-bindings: dma: snps,dw-axi-dmac: constrain the items of resets for JH7110 dma
dt-bindings: dma: Drop unneeded quotes
dmaengine: at_xdmac: align declaration of ret with the rest of variables
dmaengine: at_xdmac: add a warning message regarding for unpaused channels
...
This commit is contained in:
@@ -136,6 +136,22 @@ Description: The last executed device administrative command's status/error.
|
||||
Also last configuration error overloaded.
|
||||
Writing to it will clear the status.
|
||||
|
||||
What: /sys/bus/dsa/devices/dsa<m>/iaa_cap
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.0.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: IAA (IAX) capability mask. Exported to user space for application
|
||||
consumption. This attribute should only be visible on IAA devices
|
||||
that are version 2 or later.
|
||||
|
||||
What: /sys/bus/dsa/devices/dsa<m>/event_log_size
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.4.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: The event log size to be configured. Default is 64 entries and
|
||||
occupies 4k size if the evl entry is 64 bytes. It's visible
|
||||
only on platforms that support the capability.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/block_on_fault
|
||||
Date: Oct 27, 2020
|
||||
KernelVersion: 5.11.0
|
||||
@@ -219,6 +235,16 @@ Contact: dmaengine@vger.kernel.org
|
||||
Description: Indicate whether ATS disable is turned on for the workqueue.
|
||||
0 indicates ATS is on, and 1 indicates ATS is off for the workqueue.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/prs_disable
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.4.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Controls whether PRS disable is turned on for the workqueue.
|
||||
0 indicates PRS is on, and 1 indicates PRS is off for the
|
||||
workqueue. This option overrides block_on_fault attribute
|
||||
if set. It's visible only on platforms that support the
|
||||
capability.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/occupancy
|
||||
Date May 25, 2021
|
||||
KernelVersion: 5.14.0
|
||||
@@ -302,3 +328,28 @@ Description: Allows control of the number of batch descriptors that can be
|
||||
1 (1/2 of max value), 2 (1/4 of the max value), and 3 (1/8 of
|
||||
the max value). It's visible only on platforms that support
|
||||
the capability.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/cr_faults
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.4.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Show the number of Completion Record (CR) faults this application
|
||||
has caused.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/cr_fault_failures
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.4.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Show the number of Completion Record (CR) faults failures that this
|
||||
application has caused. The failure counter is incremented when the
|
||||
driver cannot fault in the address for the CR. Typically this is caused
|
||||
by a bad address programmed in the submitted descriptor or a malicious
|
||||
submitter is using bad CR address on purpose.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/pid
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.4.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Show the process id of the application that opened the file. This is
|
||||
helpful information for a monitor daemon that wants to kill the
|
||||
application that opened the file.
|
||||
|
||||
@@ -26,6 +26,7 @@ properties:
|
||||
- enum:
|
||||
- apple,t6000-admac
|
||||
- apple,t8103-admac
|
||||
- apple,t8112-admac
|
||||
- const: apple,admac
|
||||
|
||||
reg:
|
||||
|
||||
@@ -24,6 +24,7 @@ properties:
|
||||
- qcom,sm6350-gpi-dma
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,qcm2290-gpi-dma
|
||||
- qcom,qdu1000-gpi-dma
|
||||
- qcom,sc7280-gpi-dma
|
||||
- qcom,sm6115-gpi-dma
|
||||
|
||||
@@ -54,6 +54,11 @@ properties:
|
||||
- description: DMA main clock
|
||||
- description: DMA register access clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: main
|
||||
- const: register
|
||||
|
||||
'#dma-cells':
|
||||
const: 1
|
||||
description:
|
||||
@@ -77,16 +82,23 @@ properties:
|
||||
- description: Reset for DMA ARESETN reset terminal
|
||||
- description: Reset for DMA RST_ASYNC reset terminal
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: arst
|
||||
- const: rst_async
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#dma-cells'
|
||||
- dma-channels
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@@ -124,9 +136,11 @@ examples:
|
||||
"ch12", "ch13", "ch14", "ch15";
|
||||
clocks = <&cpg CPG_MOD R9A07G044_DMAC_ACLK>,
|
||||
<&cpg CPG_MOD R9A07G044_DMAC_PCLK>;
|
||||
clock-names = "main", "register";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A07G044_DMAC_ARESETN>,
|
||||
<&cpg R9A07G044_DMAC_RST_ASYNC>;
|
||||
reset-names = "arst", "rst_async";
|
||||
#dma-cells = <1>;
|
||||
dma-channels = <16>;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ properties:
|
||||
enum:
|
||||
- snps,axi-dma-1.01a
|
||||
- intel,kmb-axi-dma
|
||||
- starfive,jh7110-axi-dma
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
@@ -58,7 +59,8 @@ properties:
|
||||
maximum: 8
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
snps,dma-masters:
|
||||
description: |
|
||||
@@ -109,6 +111,25 @@ required:
|
||||
- snps,priority
|
||||
- snps,block-size
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- starfive,jh7110-axi-dma
|
||||
then:
|
||||
properties:
|
||||
resets:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: AXI reset line
|
||||
- description: AHB reset line
|
||||
- description: module reset
|
||||
else:
|
||||
properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
@@ -43,7 +43,7 @@ description: |
|
||||
configuration of the legacy peripheral.
|
||||
|
||||
allOf:
|
||||
- $ref: "../dma-controller.yaml#"
|
||||
- $ref: ../dma-controller.yaml#
|
||||
- $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
|
||||
|
||||
properties:
|
||||
|
||||
@@ -15,7 +15,7 @@ maintainers:
|
||||
- Michael Tretter <m.tretter@pengutronix.de>
|
||||
|
||||
allOf:
|
||||
- $ref: "../dma-controller.yaml#"
|
||||
- $ref: ../dma-controller.yaml#
|
||||
|
||||
properties:
|
||||
"#dma-cells":
|
||||
|
||||
@@ -16,7 +16,7 @@ maintainers:
|
||||
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "../dma-controller.yaml#"
|
||||
- $ref: ../dma-controller.yaml#
|
||||
|
||||
properties:
|
||||
"#dma-cells":
|
||||
|
||||
@@ -623,6 +623,7 @@ config TEGRA186_GPC_DMA
|
||||
depends on (ARCH_TEGRA || COMPILE_TEST) && ARCH_DMA_ADDR_T_64BIT
|
||||
depends on IOMMU_API
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
Support for the NVIDIA Tegra General Purpose Central DMA controller.
|
||||
The DMA controller has multiple DMA channels which can be configured
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
enum atc_status {
|
||||
AT_XDMAC_CHAN_IS_CYCLIC = 0,
|
||||
AT_XDMAC_CHAN_IS_PAUSED,
|
||||
AT_XDMAC_CHAN_IS_PAUSED_INTERNAL,
|
||||
};
|
||||
|
||||
struct at_xdmac_layout {
|
||||
@@ -245,6 +246,7 @@ struct at_xdmac {
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
u32 save_gim;
|
||||
u32 save_gs;
|
||||
struct dma_pool *at_xdmac_desc_pool;
|
||||
const struct at_xdmac_layout *layout;
|
||||
struct at_xdmac_chan chan[];
|
||||
@@ -347,6 +349,11 @@ 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_chan_is_paused_internal(struct at_xdmac_chan *atchan)
|
||||
{
|
||||
return test_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
|
||||
}
|
||||
|
||||
static inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg)
|
||||
{
|
||||
return cfg & AT_XDMAC_CC_TYPE_PER_TRAN;
|
||||
@@ -412,7 +419,7 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void at_xdmac_off(struct at_xdmac *atxdmac)
|
||||
static void at_xdmac_off(struct at_xdmac *atxdmac, bool suspend_descriptors)
|
||||
{
|
||||
struct dma_chan *chan, *_chan;
|
||||
struct at_xdmac_chan *atchan;
|
||||
@@ -431,7 +438,7 @@ static void at_xdmac_off(struct at_xdmac *atxdmac)
|
||||
at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L);
|
||||
|
||||
/* Decrement runtime PM ref counter for each active descriptor. */
|
||||
if (!list_empty(&atxdmac->dma.channels)) {
|
||||
if (!list_empty(&atxdmac->dma.channels) && suspend_descriptors) {
|
||||
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels,
|
||||
device_node) {
|
||||
atchan = to_at_xdmac_chan(chan);
|
||||
@@ -1898,6 +1905,26 @@ static int at_xdmac_device_config(struct dma_chan *chan,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void at_xdmac_device_pause_set(struct at_xdmac *atxdmac,
|
||||
struct at_xdmac_chan *atchan)
|
||||
{
|
||||
at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
|
||||
while (at_xdmac_chan_read(atchan, AT_XDMAC_CC) &
|
||||
(AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static void at_xdmac_device_pause_internal(struct at_xdmac_chan *atchan)
|
||||
{
|
||||
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atchan->lock, flags);
|
||||
set_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
|
||||
at_xdmac_device_pause_set(atxdmac, atchan);
|
||||
spin_unlock_irqrestore(&atchan->lock, flags);
|
||||
}
|
||||
|
||||
static int at_xdmac_device_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
|
||||
@@ -1915,11 +1942,8 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&atchan->lock, flags);
|
||||
at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
|
||||
while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
|
||||
& (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
|
||||
cpu_relax();
|
||||
|
||||
at_xdmac_device_pause_set(atxdmac, atchan);
|
||||
/* Decrement runtime PM ref counter for each active descriptor. */
|
||||
at_xdmac_runtime_suspend_descriptors(atchan);
|
||||
|
||||
@@ -1931,6 +1955,17 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at_xdmac_device_resume_internal(struct at_xdmac_chan *atchan)
|
||||
{
|
||||
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atchan->lock, flags);
|
||||
at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask);
|
||||
clear_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
|
||||
spin_unlock_irqrestore(&atchan->lock, flags);
|
||||
}
|
||||
|
||||
static int at_xdmac_device_resume(struct dma_chan *chan)
|
||||
{
|
||||
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
|
||||
@@ -2118,19 +2153,26 @@ static int __maybe_unused atmel_xdmac_suspend(struct device *dev)
|
||||
|
||||
atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC);
|
||||
if (at_xdmac_chan_is_cyclic(atchan)) {
|
||||
if (!at_xdmac_chan_is_paused(atchan))
|
||||
at_xdmac_device_pause(chan);
|
||||
if (!at_xdmac_chan_is_paused(atchan)) {
|
||||
dev_warn(chan2dev(chan), "%s: channel %d not paused\n",
|
||||
__func__, chan->chan_id);
|
||||
at_xdmac_device_pause_internal(atchan);
|
||||
at_xdmac_runtime_suspend_descriptors(atchan);
|
||||
}
|
||||
atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
|
||||
atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA);
|
||||
atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC);
|
||||
}
|
||||
|
||||
at_xdmac_runtime_suspend_descriptors(atchan);
|
||||
}
|
||||
atxdmac->save_gim = at_xdmac_read(atxdmac, AT_XDMAC_GIM);
|
||||
atxdmac->save_gs = at_xdmac_read(atxdmac, AT_XDMAC_GS);
|
||||
|
||||
at_xdmac_off(atxdmac);
|
||||
return pm_runtime_force_suspend(atxdmac->dev);
|
||||
at_xdmac_off(atxdmac, false);
|
||||
pm_runtime_mark_last_busy(atxdmac->dev);
|
||||
pm_runtime_put_noidle(atxdmac->dev);
|
||||
clk_disable_unprepare(atxdmac->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_xdmac_resume(struct device *dev)
|
||||
@@ -2139,13 +2181,14 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
|
||||
struct at_xdmac_chan *atchan;
|
||||
struct dma_chan *chan, *_chan;
|
||||
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
|
||||
int i;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
ret = pm_runtime_force_resume(atxdmac->dev);
|
||||
if (ret < 0)
|
||||
ret = clk_prepare_enable(atxdmac->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_noresume(atxdmac->dev);
|
||||
|
||||
at_xdmac_axi_config(pdev);
|
||||
|
||||
/* Clear pending interrupts. */
|
||||
@@ -2159,19 +2202,33 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
|
||||
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
|
||||
atchan = to_at_xdmac_chan(chan);
|
||||
|
||||
ret = at_xdmac_runtime_resume_descriptors(atchan);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc);
|
||||
if (at_xdmac_chan_is_cyclic(atchan)) {
|
||||
if (at_xdmac_chan_is_paused(atchan))
|
||||
at_xdmac_device_resume(chan);
|
||||
/*
|
||||
* Resume only channels not explicitly paused by
|
||||
* consumers.
|
||||
*/
|
||||
if (at_xdmac_chan_is_paused_internal(atchan)) {
|
||||
ret = at_xdmac_runtime_resume_descriptors(atchan);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
at_xdmac_device_resume_internal(atchan);
|
||||
}
|
||||
|
||||
/*
|
||||
* We may resume from a deep sleep state where power
|
||||
* to DMA controller is cut-off. Thus, restore the
|
||||
* suspend state of channels set though dmaengine API.
|
||||
*/
|
||||
else if (at_xdmac_chan_is_paused(atchan))
|
||||
at_xdmac_device_pause_set(atxdmac, atchan);
|
||||
|
||||
at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda);
|
||||
at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc);
|
||||
at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim);
|
||||
wmb();
|
||||
at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
|
||||
if (atxdmac->save_gs & atchan->mask)
|
||||
at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2312,7 +2369,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
|
||||
INIT_LIST_HEAD(&atxdmac->dma.channels);
|
||||
|
||||
/* Disable all chans and interrupts. */
|
||||
at_xdmac_off(atxdmac);
|
||||
at_xdmac_off(atxdmac, true);
|
||||
|
||||
for (i = 0; i < nr_channels; i++) {
|
||||
struct at_xdmac_chan *atchan = &atxdmac->chan[i];
|
||||
@@ -2376,7 +2433,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
|
||||
struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
at_xdmac_off(atxdmac);
|
||||
at_xdmac_off(atxdmac, true);
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
dma_async_device_unregister(&atxdmac->dma);
|
||||
pm_runtime_disable(atxdmac->dev);
|
||||
|
||||
@@ -38,7 +38,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
|
||||
{
|
||||
int rv;
|
||||
const u32 *regaddr_p;
|
||||
u64 regaddr64, size64;
|
||||
struct resource res;
|
||||
unsigned int psize;
|
||||
|
||||
/* Create our state struct */
|
||||
@@ -56,21 +56,18 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
|
||||
}
|
||||
|
||||
/* Get address and size of the sram */
|
||||
regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
|
||||
if (!regaddr_p) {
|
||||
rv = of_address_to_resource(sram_node, 0, &res);
|
||||
if (rv) {
|
||||
printk(KERN_ERR "%s: bcom_sram_init: "
|
||||
"Invalid device node !\n", owner);
|
||||
rv = -EINVAL;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
regaddr64 = of_translate_address(sram_node, regaddr_p);
|
||||
|
||||
bcom_sram->base_phys = (phys_addr_t) regaddr64;
|
||||
bcom_sram->size = (unsigned int) size64;
|
||||
bcom_sram->base_phys = res.start;
|
||||
bcom_sram->size = resource_size(&res);
|
||||
|
||||
/* Request region */
|
||||
if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) {
|
||||
if (!request_mem_region(res.start, resource_size(&res), owner)) {
|
||||
printk(KERN_ERR "%s: bcom_sram_init: "
|
||||
"Couldn't request region !\n", owner);
|
||||
rv = -EBUSY;
|
||||
@@ -79,7 +76,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
|
||||
|
||||
/* Map SRAM */
|
||||
/* sram is not really __iomem */
|
||||
bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size);
|
||||
bcom_sram->base_virt = (void *)ioremap(res.start, resource_size(&res));
|
||||
|
||||
if (!bcom_sram->base_virt) {
|
||||
printk(KERN_ERR "%s: bcom_sram_init: "
|
||||
@@ -120,7 +117,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
|
||||
return 0;
|
||||
|
||||
error_release:
|
||||
release_mem_region(bcom_sram->base_phys, bcom_sram->size);
|
||||
release_mem_region(res.start, resource_size(&res));
|
||||
error_free:
|
||||
kfree(bcom_sram);
|
||||
bcom_sram = NULL;
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@@ -46,6 +48,10 @@
|
||||
DMA_SLAVE_BUSWIDTH_32_BYTES | \
|
||||
DMA_SLAVE_BUSWIDTH_64_BYTES)
|
||||
|
||||
#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0)
|
||||
#define AXI_DMA_FLAG_HAS_RESETS BIT(1)
|
||||
#define AXI_DMA_FLAG_USE_CFG2 BIT(2)
|
||||
|
||||
static inline void
|
||||
axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val)
|
||||
{
|
||||
@@ -86,7 +92,8 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan,
|
||||
|
||||
cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS |
|
||||
config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
|
||||
if (chan->chip->dw->hdata->reg_map_8_channels) {
|
||||
if (chan->chip->dw->hdata->reg_map_8_channels &&
|
||||
!chan->chip->dw->hdata->use_cfg2) {
|
||||
cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS |
|
||||
config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS |
|
||||
config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS |
|
||||
@@ -1140,7 +1147,7 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
|
||||
axi_chan_disable(chan);
|
||||
|
||||
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
|
||||
!(val & chan_active), 1000, 10000);
|
||||
!(val & chan_active), 1000, 50000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(dchan2dev(dchan),
|
||||
"%s failed to stop\n", axi_chan_name(chan));
|
||||
@@ -1367,10 +1374,11 @@ static int parse_device_properties(struct axi_dma_chip *chip)
|
||||
|
||||
static int dw_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct axi_dma_chip *chip;
|
||||
struct dw_axi_dma *dw;
|
||||
struct dw_axi_dma_hcfg *hdata;
|
||||
struct reset_control *resets;
|
||||
unsigned int flags;
|
||||
u32 i;
|
||||
int ret;
|
||||
|
||||
@@ -1398,12 +1406,25 @@ static int dw_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(chip->regs))
|
||||
return PTR_ERR(chip->regs);
|
||||
|
||||
if (of_device_is_compatible(node, "intel,kmb-axi-dma")) {
|
||||
flags = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
if (flags & AXI_DMA_FLAG_HAS_APB_REGS) {
|
||||
chip->apb_regs = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(chip->apb_regs))
|
||||
return PTR_ERR(chip->apb_regs);
|
||||
}
|
||||
|
||||
if (flags & AXI_DMA_FLAG_HAS_RESETS) {
|
||||
resets = devm_reset_control_array_get_exclusive(&pdev->dev);
|
||||
if (IS_ERR(resets))
|
||||
return PTR_ERR(resets);
|
||||
|
||||
ret = reset_control_deassert(resets);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2);
|
||||
|
||||
chip->core_clk = devm_clk_get(chip->dev, "core-clk");
|
||||
if (IS_ERR(chip->core_clk))
|
||||
return PTR_ERR(chip->core_clk);
|
||||
@@ -1554,8 +1575,15 @@ static const struct dev_pm_ops dw_axi_dma_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_dma_of_id_table[] = {
|
||||
{ .compatible = "snps,axi-dma-1.01a" },
|
||||
{ .compatible = "intel,kmb-axi-dma" },
|
||||
{
|
||||
.compatible = "snps,axi-dma-1.01a"
|
||||
}, {
|
||||
.compatible = "intel,kmb-axi-dma",
|
||||
.data = (void *)AXI_DMA_FLAG_HAS_APB_REGS,
|
||||
}, {
|
||||
.compatible = "starfive,jh7110-axi-dma",
|
||||
.data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
|
||||
|
||||
@@ -33,6 +33,7 @@ struct dw_axi_dma_hcfg {
|
||||
/* Register map for DMAX_NUM_CHANNELS <= 8 */
|
||||
bool reg_map_8_channels;
|
||||
bool restrict_axi_burst_len;
|
||||
bool use_cfg2;
|
||||
};
|
||||
|
||||
struct axi_dma_chan {
|
||||
|
||||
@@ -181,7 +181,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
|
||||
dw_edma_free_desc(vd2dw_edma_desc(vdesc));
|
||||
}
|
||||
|
||||
static void dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
static int dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
{
|
||||
struct dw_edma_chunk *child;
|
||||
struct dw_edma_desc *desc;
|
||||
@@ -189,16 +189,16 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
|
||||
vd = vchan_next_desc(&chan->vc);
|
||||
if (!vd)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
desc = vd2dw_edma_desc(vd);
|
||||
if (!desc)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
child = list_first_entry_or_null(&desc->chunk->list,
|
||||
struct dw_edma_chunk, list);
|
||||
if (!child)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dw_edma_v0_core_start(child, !desc->xfer_sz);
|
||||
desc->xfer_sz += child->ll_region.sz;
|
||||
@@ -206,6 +206,8 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
list_del(&child->list);
|
||||
kfree(child);
|
||||
desc->chunks_alloc--;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void dw_edma_device_caps(struct dma_chan *dchan,
|
||||
@@ -306,9 +308,12 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
|
||||
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
|
||||
unsigned long flags;
|
||||
|
||||
if (!chan->configured)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
if (chan->configured && chan->request == EDMA_REQ_NONE &&
|
||||
chan->status == EDMA_ST_IDLE && vchan_issue_pending(&chan->vc)) {
|
||||
if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE &&
|
||||
chan->status == EDMA_ST_IDLE) {
|
||||
chan->status = EDMA_ST_BUSY;
|
||||
dw_edma_start_transfer(chan);
|
||||
}
|
||||
@@ -602,14 +607,14 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
|
||||
switch (chan->request) {
|
||||
case EDMA_REQ_NONE:
|
||||
desc = vd2dw_edma_desc(vd);
|
||||
if (desc->chunks_alloc) {
|
||||
chan->status = EDMA_ST_BUSY;
|
||||
dw_edma_start_transfer(chan);
|
||||
} else {
|
||||
if (!desc->chunks_alloc) {
|
||||
list_del(&vd->node);
|
||||
vchan_cookie_complete(vd);
|
||||
chan->status = EDMA_ST_IDLE;
|
||||
}
|
||||
|
||||
/* Continue transferring if there are remaining chunks or issued requests.
|
||||
*/
|
||||
chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
|
||||
break;
|
||||
|
||||
case EDMA_REQ_STOP:
|
||||
|
||||
@@ -159,62 +159,6 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
|
||||
#define GET_CH_32(dw, dir, ch, name) \
|
||||
readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
|
||||
|
||||
static inline void writeq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
|
||||
u64 value, void __iomem *addr)
|
||||
{
|
||||
if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
|
||||
u32 viewport_sel;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&dw->lock, flags);
|
||||
|
||||
viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
|
||||
if (dir == EDMA_DIR_READ)
|
||||
viewport_sel |= BIT(31);
|
||||
|
||||
writel(viewport_sel,
|
||||
&(__dw_regs(dw)->type.legacy.viewport_sel));
|
||||
writeq(value, addr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&dw->lock, flags);
|
||||
} else {
|
||||
writeq(value, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
|
||||
const void __iomem *addr)
|
||||
{
|
||||
u64 value;
|
||||
|
||||
if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
|
||||
u32 viewport_sel;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&dw->lock, flags);
|
||||
|
||||
viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
|
||||
if (dir == EDMA_DIR_READ)
|
||||
viewport_sel |= BIT(31);
|
||||
|
||||
writel(viewport_sel,
|
||||
&(__dw_regs(dw)->type.legacy.viewport_sel));
|
||||
value = readq(addr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&dw->lock, flags);
|
||||
} else {
|
||||
value = readq(addr);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#define SET_CH_64(dw, dir, ch, name, value) \
|
||||
writeq_ch(dw, dir, ch, value, &(__dw_ch_regs(dw, dir, ch)->name))
|
||||
|
||||
#define GET_CH_64(dw, dir, ch, name) \
|
||||
readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
|
||||
|
||||
/* eDMA management callbacks */
|
||||
void dw_edma_v0_core_off(struct dw_edma *dw)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=IDXD
|
||||
|
||||
obj-$(CONFIG_INTEL_IDXD) += idxd.o
|
||||
idxd-y := init.o irq.o device.o sysfs.o submit.o dma.o cdev.o
|
||||
idxd-y := init.o irq.o device.o sysfs.o submit.o dma.o cdev.o debugfs.o
|
||||
|
||||
idxd-$(CONFIG_INTEL_IDXD_PERFMON) += perfmon.o
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <uapi/linux/idxd.h>
|
||||
#include <linux/xarray.h>
|
||||
#include "registers.h"
|
||||
#include "idxd.h"
|
||||
|
||||
@@ -21,6 +23,13 @@ struct idxd_cdev_context {
|
||||
struct ida minor_ida;
|
||||
};
|
||||
|
||||
/*
|
||||
* Since user file names are global in DSA devices, define their ida's as
|
||||
* global to avoid conflict file names.
|
||||
*/
|
||||
static DEFINE_IDA(file_ida);
|
||||
static DEFINE_MUTEX(ida_lock);
|
||||
|
||||
/*
|
||||
* ictx is an array based off of accelerator types. enum idxd_type
|
||||
* is used as index
|
||||
@@ -34,8 +43,119 @@ struct idxd_user_context {
|
||||
struct idxd_wq *wq;
|
||||
struct task_struct *task;
|
||||
unsigned int pasid;
|
||||
struct mm_struct *mm;
|
||||
unsigned int flags;
|
||||
struct iommu_sva *sva;
|
||||
struct idxd_dev idxd_dev;
|
||||
u64 counters[COUNTER_MAX];
|
||||
int id;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid);
|
||||
static void idxd_xa_pasid_remove(struct idxd_user_context *ctx);
|
||||
|
||||
static inline struct idxd_user_context *dev_to_uctx(struct device *dev)
|
||||
{
|
||||
struct idxd_dev *idxd_dev = confdev_to_idxd_dev(dev);
|
||||
|
||||
return container_of(idxd_dev, struct idxd_user_context, idxd_dev);
|
||||
}
|
||||
|
||||
static ssize_t cr_faults_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct idxd_user_context *ctx = dev_to_uctx(dev);
|
||||
|
||||
return sysfs_emit(buf, "%llu\n", ctx->counters[COUNTER_FAULTS]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(cr_faults);
|
||||
|
||||
static ssize_t cr_fault_failures_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct idxd_user_context *ctx = dev_to_uctx(dev);
|
||||
|
||||
return sysfs_emit(buf, "%llu\n", ctx->counters[COUNTER_FAULT_FAILS]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(cr_fault_failures);
|
||||
|
||||
static ssize_t pid_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct idxd_user_context *ctx = dev_to_uctx(dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", ctx->pid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(pid);
|
||||
|
||||
static struct attribute *cdev_file_attributes[] = {
|
||||
&dev_attr_cr_faults.attr,
|
||||
&dev_attr_cr_fault_failures.attr,
|
||||
&dev_attr_pid.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t cdev_file_attr_visible(struct kobject *kobj, struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, typeof(*dev), kobj);
|
||||
struct idxd_user_context *ctx = dev_to_uctx(dev);
|
||||
struct idxd_wq *wq = ctx->wq;
|
||||
|
||||
if (!wq_pasid_enabled(wq))
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group cdev_file_attribute_group = {
|
||||
.attrs = cdev_file_attributes,
|
||||
.is_visible = cdev_file_attr_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *cdev_file_attribute_groups[] = {
|
||||
&cdev_file_attribute_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void idxd_file_dev_release(struct device *dev)
|
||||
{
|
||||
struct idxd_user_context *ctx = dev_to_uctx(dev);
|
||||
struct idxd_wq *wq = ctx->wq;
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ida_lock);
|
||||
ida_free(&file_ida, ctx->id);
|
||||
mutex_unlock(&ida_lock);
|
||||
|
||||
/* Wait for in-flight operations to complete. */
|
||||
if (wq_shared(wq)) {
|
||||
idxd_device_drain_pasid(idxd, ctx->pasid);
|
||||
} else {
|
||||
if (device_user_pasid_enabled(idxd)) {
|
||||
/* The wq disable in the disable pasid function will drain the wq */
|
||||
rc = idxd_wq_disable_pasid(wq);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "wq disable pasid failed.\n");
|
||||
} else {
|
||||
idxd_wq_drain(wq);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->sva) {
|
||||
idxd_cdev_evl_drain_pasid(wq, ctx->pasid);
|
||||
iommu_sva_unbind_device(ctx->sva);
|
||||
idxd_xa_pasid_remove(ctx);
|
||||
}
|
||||
kfree(ctx);
|
||||
mutex_lock(&wq->wq_lock);
|
||||
idxd_wq_put(wq);
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
}
|
||||
|
||||
static struct device_type idxd_cdev_file_type = {
|
||||
.name = "idxd_file",
|
||||
.release = idxd_file_dev_release,
|
||||
.groups = cdev_file_attribute_groups,
|
||||
};
|
||||
|
||||
static void idxd_cdev_dev_release(struct device *dev)
|
||||
@@ -68,15 +188,46 @@ static inline struct idxd_wq *inode_wq(struct inode *inode)
|
||||
return idxd_cdev->wq;
|
||||
}
|
||||
|
||||
static void idxd_xa_pasid_remove(struct idxd_user_context *ctx)
|
||||
{
|
||||
struct idxd_wq *wq = ctx->wq;
|
||||
void *ptr;
|
||||
|
||||
mutex_lock(&wq->uc_lock);
|
||||
ptr = xa_cmpxchg(&wq->upasid_xa, ctx->pasid, ctx, NULL, GFP_KERNEL);
|
||||
if (ptr != (void *)ctx)
|
||||
dev_warn(&wq->idxd->pdev->dev, "xarray cmpxchg failed for pasid %u\n",
|
||||
ctx->pasid);
|
||||
mutex_unlock(&wq->uc_lock);
|
||||
}
|
||||
|
||||
void idxd_user_counter_increment(struct idxd_wq *wq, u32 pasid, int index)
|
||||
{
|
||||
struct idxd_user_context *ctx;
|
||||
|
||||
if (index >= COUNTER_MAX)
|
||||
return;
|
||||
|
||||
mutex_lock(&wq->uc_lock);
|
||||
ctx = xa_load(&wq->upasid_xa, pasid);
|
||||
if (!ctx) {
|
||||
mutex_unlock(&wq->uc_lock);
|
||||
return;
|
||||
}
|
||||
ctx->counters[index]++;
|
||||
mutex_unlock(&wq->uc_lock);
|
||||
}
|
||||
|
||||
static int idxd_cdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct idxd_user_context *ctx;
|
||||
struct idxd_device *idxd;
|
||||
struct idxd_wq *wq;
|
||||
struct device *dev;
|
||||
struct device *dev, *fdev;
|
||||
int rc = 0;
|
||||
struct iommu_sva *sva;
|
||||
unsigned int pasid;
|
||||
struct idxd_cdev *idxd_cdev;
|
||||
|
||||
wq = inode_wq(inode);
|
||||
idxd = wq->idxd;
|
||||
@@ -97,6 +248,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
|
||||
|
||||
ctx->wq = wq;
|
||||
filp->private_data = ctx;
|
||||
ctx->pid = current->pid;
|
||||
|
||||
if (device_user_pasid_enabled(idxd)) {
|
||||
sva = iommu_sva_bind_device(dev, current->mm);
|
||||
@@ -108,65 +260,118 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
|
||||
|
||||
pasid = iommu_sva_get_pasid(sva);
|
||||
if (pasid == IOMMU_PASID_INVALID) {
|
||||
iommu_sva_unbind_device(sva);
|
||||
rc = -EINVAL;
|
||||
goto failed;
|
||||
goto failed_get_pasid;
|
||||
}
|
||||
|
||||
ctx->sva = sva;
|
||||
ctx->pasid = pasid;
|
||||
ctx->mm = current->mm;
|
||||
|
||||
mutex_lock(&wq->uc_lock);
|
||||
rc = xa_insert(&wq->upasid_xa, pasid, ctx, GFP_KERNEL);
|
||||
mutex_unlock(&wq->uc_lock);
|
||||
if (rc < 0)
|
||||
dev_warn(dev, "PASID entry already exist in xarray.\n");
|
||||
|
||||
if (wq_dedicated(wq)) {
|
||||
rc = idxd_wq_set_pasid(wq, pasid);
|
||||
if (rc < 0) {
|
||||
iommu_sva_unbind_device(sva);
|
||||
dev_err(dev, "wq set pasid failed: %d\n", rc);
|
||||
goto failed;
|
||||
goto failed_set_pasid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idxd_cdev = wq->idxd_cdev;
|
||||
mutex_lock(&ida_lock);
|
||||
ctx->id = ida_alloc(&file_ida, GFP_KERNEL);
|
||||
mutex_unlock(&ida_lock);
|
||||
if (ctx->id < 0) {
|
||||
dev_warn(dev, "ida alloc failure\n");
|
||||
goto failed_ida;
|
||||
}
|
||||
ctx->idxd_dev.type = IDXD_DEV_CDEV_FILE;
|
||||
fdev = user_ctx_dev(ctx);
|
||||
device_initialize(fdev);
|
||||
fdev->parent = cdev_dev(idxd_cdev);
|
||||
fdev->bus = &dsa_bus_type;
|
||||
fdev->type = &idxd_cdev_file_type;
|
||||
|
||||
rc = dev_set_name(fdev, "file%d", ctx->id);
|
||||
if (rc < 0) {
|
||||
dev_warn(dev, "set name failure\n");
|
||||
goto failed_dev_name;
|
||||
}
|
||||
|
||||
rc = device_add(fdev);
|
||||
if (rc < 0) {
|
||||
dev_warn(dev, "file device add failure\n");
|
||||
goto failed_dev_add;
|
||||
}
|
||||
|
||||
idxd_wq_get(wq);
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
failed_dev_add:
|
||||
failed_dev_name:
|
||||
put_device(fdev);
|
||||
failed_ida:
|
||||
failed_set_pasid:
|
||||
if (device_user_pasid_enabled(idxd))
|
||||
idxd_xa_pasid_remove(ctx);
|
||||
failed_get_pasid:
|
||||
if (device_user_pasid_enabled(idxd))
|
||||
iommu_sva_unbind_device(sva);
|
||||
failed:
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
kfree(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid)
|
||||
{
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
struct idxd_evl *evl = idxd->evl;
|
||||
union evl_status_reg status;
|
||||
u16 h, t, size;
|
||||
int ent_size = evl_ent_size(idxd);
|
||||
struct __evl_entry *entry_head;
|
||||
|
||||
if (!evl)
|
||||
return;
|
||||
|
||||
spin_lock(&evl->lock);
|
||||
status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
|
||||
t = status.tail;
|
||||
h = evl->head;
|
||||
size = evl->size;
|
||||
|
||||
while (h != t) {
|
||||
entry_head = (struct __evl_entry *)(evl->log + (h * ent_size));
|
||||
if (entry_head->pasid == pasid && entry_head->wq_idx == wq->id)
|
||||
set_bit(h, evl->bmap);
|
||||
h = (h + 1) % size;
|
||||
}
|
||||
spin_unlock(&evl->lock);
|
||||
|
||||
drain_workqueue(wq->wq);
|
||||
}
|
||||
|
||||
static int idxd_cdev_release(struct inode *node, struct file *filep)
|
||||
{
|
||||
struct idxd_user_context *ctx = filep->private_data;
|
||||
struct idxd_wq *wq = ctx->wq;
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
int rc;
|
||||
|
||||
dev_dbg(dev, "%s called\n", __func__);
|
||||
filep->private_data = NULL;
|
||||
|
||||
/* Wait for in-flight operations to complete. */
|
||||
if (wq_shared(wq)) {
|
||||
idxd_device_drain_pasid(idxd, ctx->pasid);
|
||||
} else {
|
||||
if (device_user_pasid_enabled(idxd)) {
|
||||
/* The wq disable in the disable pasid function will drain the wq */
|
||||
rc = idxd_wq_disable_pasid(wq);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "wq disable pasid failed.\n");
|
||||
} else {
|
||||
idxd_wq_drain(wq);
|
||||
}
|
||||
}
|
||||
device_unregister(user_ctx_dev(ctx));
|
||||
|
||||
if (ctx->sva)
|
||||
iommu_sva_unbind_device(ctx->sva);
|
||||
kfree(ctx);
|
||||
mutex_lock(&wq->wq_lock);
|
||||
idxd_wq_put(wq);
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -297,6 +502,7 @@ void idxd_wq_del_cdev(struct idxd_wq *wq)
|
||||
struct idxd_cdev *idxd_cdev;
|
||||
|
||||
idxd_cdev = wq->idxd_cdev;
|
||||
ida_destroy(&file_ida);
|
||||
wq->idxd_cdev = NULL;
|
||||
cdev_device_del(&idxd_cdev->cdev, cdev_dev(idxd_cdev));
|
||||
put_device(cdev_dev(idxd_cdev));
|
||||
@@ -330,6 +536,13 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev)
|
||||
}
|
||||
|
||||
mutex_lock(&wq->wq_lock);
|
||||
|
||||
wq->wq = create_workqueue(dev_name(wq_confdev(wq)));
|
||||
if (!wq->wq) {
|
||||
rc = -ENOMEM;
|
||||
goto wq_err;
|
||||
}
|
||||
|
||||
wq->type = IDXD_WQT_USER;
|
||||
rc = drv_enable_wq(wq);
|
||||
if (rc < 0)
|
||||
@@ -348,7 +561,9 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev)
|
||||
err_cdev:
|
||||
drv_disable_wq(wq);
|
||||
err:
|
||||
destroy_workqueue(wq->wq);
|
||||
wq->type = IDXD_WQT_NONE;
|
||||
wq_err:
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
return rc;
|
||||
}
|
||||
@@ -361,6 +576,8 @@ static void idxd_user_drv_remove(struct idxd_dev *idxd_dev)
|
||||
idxd_wq_del_cdev(wq);
|
||||
drv_disable_wq(wq);
|
||||
wq->type = IDXD_WQT_NONE;
|
||||
destroy_workqueue(wq->wq);
|
||||
wq->wq = NULL;
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
}
|
||||
|
||||
@@ -407,3 +624,70 @@ void idxd_cdev_remove(void)
|
||||
ida_destroy(&ictx[i].minor_ida);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* idxd_copy_cr - copy completion record to user address space found by wq and
|
||||
* PASID
|
||||
* @wq: work queue
|
||||
* @pasid: PASID
|
||||
* @addr: user fault address to write
|
||||
* @cr: completion record
|
||||
* @len: number of bytes to copy
|
||||
*
|
||||
* This is called by a work that handles completion record fault.
|
||||
*
|
||||
* Return: number of bytes copied.
|
||||
*/
|
||||
int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
|
||||
void *cr, int len)
|
||||
{
|
||||
struct device *dev = &wq->idxd->pdev->dev;
|
||||
int left = len, status_size = 1;
|
||||
struct idxd_user_context *ctx;
|
||||
struct mm_struct *mm;
|
||||
|
||||
mutex_lock(&wq->uc_lock);
|
||||
|
||||
ctx = xa_load(&wq->upasid_xa, pasid);
|
||||
if (!ctx) {
|
||||
dev_warn(dev, "No user context\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
mm = ctx->mm;
|
||||
/*
|
||||
* The completion record fault handling work is running in kernel
|
||||
* thread context. It temporarily switches to the mm to copy cr
|
||||
* to addr in the mm.
|
||||
*/
|
||||
kthread_use_mm(mm);
|
||||
left = copy_to_user((void __user *)addr + status_size, cr + status_size,
|
||||
len - status_size);
|
||||
/*
|
||||
* Copy status only after the rest of completion record is copied
|
||||
* successfully so that the user gets the complete completion record
|
||||
* when a non-zero status is polled.
|
||||
*/
|
||||
if (!left) {
|
||||
u8 status;
|
||||
|
||||
/*
|
||||
* Ensure that the completion record's status field is written
|
||||
* after the rest of the completion record has been written.
|
||||
* This ensures that the user receives the correct completion
|
||||
* record information once polling for a non-zero status.
|
||||
*/
|
||||
wmb();
|
||||
status = *(u8 *)cr;
|
||||
if (put_user(status, (u8 __user *)addr))
|
||||
left += status_size;
|
||||
} else {
|
||||
left += status_size;
|
||||
}
|
||||
kthread_unuse_mm(mm);
|
||||
|
||||
out:
|
||||
mutex_unlock(&wq->uc_lock);
|
||||
|
||||
return len - left;
|
||||
}
|
||||
|
||||
138
drivers/dma/idxd/debugfs.c
Normal file
138
drivers/dma/idxd/debugfs.c
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <uapi/linux/idxd.h>
|
||||
#include "idxd.h"
|
||||
#include "registers.h"
|
||||
|
||||
static struct dentry *idxd_debugfs_dir;
|
||||
|
||||
static void dump_event_entry(struct idxd_device *idxd, struct seq_file *s,
|
||||
u16 index, int *count, bool processed)
|
||||
{
|
||||
struct idxd_evl *evl = idxd->evl;
|
||||
struct dsa_evl_entry *entry;
|
||||
struct dsa_completion_record *cr;
|
||||
u64 *raw;
|
||||
int i;
|
||||
int evl_strides = evl_ent_size(idxd) / sizeof(u64);
|
||||
|
||||
entry = (struct dsa_evl_entry *)evl->log + index;
|
||||
|
||||
if (!entry->e.desc_valid)
|
||||
return;
|
||||
|
||||
seq_printf(s, "Event Log entry %d (real index %u) processed: %u\n",
|
||||
*count, index, processed);
|
||||
|
||||
seq_printf(s, "desc valid %u wq idx valid %u\n"
|
||||
"batch %u fault rw %u priv %u error 0x%x\n"
|
||||
"wq idx %u op %#x pasid %u batch idx %u\n"
|
||||
"fault addr %#llx\n",
|
||||
entry->e.desc_valid, entry->e.wq_idx_valid,
|
||||
entry->e.batch, entry->e.fault_rw, entry->e.priv,
|
||||
entry->e.error, entry->e.wq_idx, entry->e.operation,
|
||||
entry->e.pasid, entry->e.batch_idx, entry->e.fault_addr);
|
||||
|
||||
cr = &entry->cr;
|
||||
seq_printf(s, "status %#x result %#x fault_info %#x bytes_completed %u\n"
|
||||
"fault addr %#llx inv flags %#x\n\n",
|
||||
cr->status, cr->result, cr->fault_info, cr->bytes_completed,
|
||||
cr->fault_addr, cr->invalid_flags);
|
||||
|
||||
raw = (u64 *)entry;
|
||||
|
||||
for (i = 0; i < evl_strides; i++)
|
||||
seq_printf(s, "entry[%d] = %#llx\n", i, raw[i]);
|
||||
|
||||
seq_puts(s, "\n");
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
static int debugfs_evl_show(struct seq_file *s, void *d)
|
||||
{
|
||||
struct idxd_device *idxd = s->private;
|
||||
struct idxd_evl *evl = idxd->evl;
|
||||
union evl_status_reg evl_status;
|
||||
u16 h, t, evl_size, i;
|
||||
int count = 0;
|
||||
bool processed = true;
|
||||
|
||||
if (!evl || !evl->log)
|
||||
return 0;
|
||||
|
||||
spin_lock(&evl->lock);
|
||||
|
||||
h = evl->head;
|
||||
evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
|
||||
t = evl_status.tail;
|
||||
evl_size = evl->size;
|
||||
|
||||
seq_printf(s, "Event Log head %u tail %u interrupt pending %u\n\n",
|
||||
evl_status.head, evl_status.tail, evl_status.int_pending);
|
||||
|
||||
i = t;
|
||||
while (1) {
|
||||
i = (i + 1) % evl_size;
|
||||
if (i == t)
|
||||
break;
|
||||
|
||||
if (processed && i == h)
|
||||
processed = false;
|
||||
dump_event_entry(idxd, s, i, &count, processed);
|
||||
}
|
||||
|
||||
spin_unlock(&evl->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(debugfs_evl);
|
||||
|
||||
int idxd_device_init_debugfs(struct idxd_device *idxd)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(idxd_debugfs_dir))
|
||||
return 0;
|
||||
|
||||
idxd->dbgfs_dir = debugfs_create_dir(dev_name(idxd_confdev(idxd)), idxd_debugfs_dir);
|
||||
if (IS_ERR(idxd->dbgfs_dir))
|
||||
return PTR_ERR(idxd->dbgfs_dir);
|
||||
|
||||
if (idxd->evl) {
|
||||
idxd->dbgfs_evl_file = debugfs_create_file("event_log", 0400,
|
||||
idxd->dbgfs_dir, idxd,
|
||||
&debugfs_evl_fops);
|
||||
if (IS_ERR(idxd->dbgfs_evl_file)) {
|
||||
debugfs_remove_recursive(idxd->dbgfs_dir);
|
||||
idxd->dbgfs_dir = NULL;
|
||||
return PTR_ERR(idxd->dbgfs_evl_file);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void idxd_device_remove_debugfs(struct idxd_device *idxd)
|
||||
{
|
||||
debugfs_remove_recursive(idxd->dbgfs_dir);
|
||||
}
|
||||
|
||||
int idxd_init_debugfs(void)
|
||||
{
|
||||
if (!debugfs_initialized())
|
||||
return 0;
|
||||
|
||||
idxd_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
if (IS_ERR(idxd_debugfs_dir))
|
||||
return PTR_ERR(idxd_debugfs_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void idxd_remove_debugfs(void)
|
||||
{
|
||||
debugfs_remove_recursive(idxd_debugfs_dir);
|
||||
}
|
||||
@@ -752,6 +752,101 @@ void idxd_device_clear_state(struct idxd_device *idxd)
|
||||
spin_unlock(&idxd->dev_lock);
|
||||
}
|
||||
|
||||
static int idxd_device_evl_setup(struct idxd_device *idxd)
|
||||
{
|
||||
union gencfg_reg gencfg;
|
||||
union evlcfg_reg evlcfg;
|
||||
union genctrl_reg genctrl;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
void *addr;
|
||||
dma_addr_t dma_addr;
|
||||
int size;
|
||||
struct idxd_evl *evl = idxd->evl;
|
||||
unsigned long *bmap;
|
||||
int rc;
|
||||
|
||||
if (!evl)
|
||||
return 0;
|
||||
|
||||
size = evl_size(idxd);
|
||||
|
||||
bmap = bitmap_zalloc(size, GFP_KERNEL);
|
||||
if (!bmap) {
|
||||
rc = -ENOMEM;
|
||||
goto err_bmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Address needs to be page aligned. However, dma_alloc_coherent() provides
|
||||
* at minimal page size aligned address. No manual alignment required.
|
||||
*/
|
||||
addr = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
|
||||
if (!addr) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
memset(addr, 0, size);
|
||||
|
||||
spin_lock(&evl->lock);
|
||||
evl->log = addr;
|
||||
evl->dma = dma_addr;
|
||||
evl->log_size = size;
|
||||
evl->bmap = bmap;
|
||||
|
||||
memset(&evlcfg, 0, sizeof(evlcfg));
|
||||
evlcfg.bits[0] = dma_addr & GENMASK(63, 12);
|
||||
evlcfg.size = evl->size;
|
||||
|
||||
iowrite64(evlcfg.bits[0], idxd->reg_base + IDXD_EVLCFG_OFFSET);
|
||||
iowrite64(evlcfg.bits[1], idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
|
||||
|
||||
genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
|
||||
genctrl.evl_int_en = 1;
|
||||
iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
|
||||
|
||||
gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
|
||||
gencfg.evl_en = 1;
|
||||
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
|
||||
|
||||
spin_unlock(&evl->lock);
|
||||
return 0;
|
||||
|
||||
err_alloc:
|
||||
bitmap_free(bmap);
|
||||
err_bmap:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void idxd_device_evl_free(struct idxd_device *idxd)
|
||||
{
|
||||
union gencfg_reg gencfg;
|
||||
union genctrl_reg genctrl;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
struct idxd_evl *evl = idxd->evl;
|
||||
|
||||
gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
|
||||
if (!gencfg.evl_en)
|
||||
return;
|
||||
|
||||
spin_lock(&evl->lock);
|
||||
gencfg.evl_en = 0;
|
||||
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
|
||||
|
||||
genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
|
||||
genctrl.evl_int_en = 0;
|
||||
iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
|
||||
|
||||
iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET);
|
||||
iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
|
||||
|
||||
dma_free_coherent(dev, evl->log_size, evl->log, evl->dma);
|
||||
bitmap_free(evl->bmap);
|
||||
evl->log = NULL;
|
||||
evl->size = IDXD_EVL_SIZE_MIN;
|
||||
spin_unlock(&evl->lock);
|
||||
}
|
||||
|
||||
static void idxd_group_config_write(struct idxd_group *group)
|
||||
{
|
||||
struct idxd_device *idxd = group->idxd;
|
||||
@@ -872,12 +967,16 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
|
||||
wq->wqcfg->priority = wq->priority;
|
||||
|
||||
if (idxd->hw.gen_cap.block_on_fault &&
|
||||
test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags))
|
||||
test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags) &&
|
||||
!test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags))
|
||||
wq->wqcfg->bof = 1;
|
||||
|
||||
if (idxd->hw.wq_cap.wq_ats_support)
|
||||
wq->wqcfg->wq_ats_disable = test_bit(WQ_FLAG_ATS_DISABLE, &wq->flags);
|
||||
|
||||
if (idxd->hw.wq_cap.wq_prs_support)
|
||||
wq->wqcfg->wq_prs_disable = test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags);
|
||||
|
||||
/* bytes 12-15 */
|
||||
wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
|
||||
idxd_wqcfg_set_max_batch_shift(idxd->data->type, wq->wqcfg, ilog2(wq->max_batch_size));
|
||||
@@ -1451,15 +1550,24 @@ int idxd_device_drv_probe(struct idxd_dev *idxd_dev)
|
||||
if (rc < 0)
|
||||
return -ENXIO;
|
||||
|
||||
rc = idxd_device_evl_setup(idxd);
|
||||
if (rc < 0) {
|
||||
idxd->cmd_status = IDXD_SCMD_DEV_EVL_ERR;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Start device */
|
||||
rc = idxd_device_enable(idxd);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
idxd_device_evl_free(idxd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Setup DMA device without channels */
|
||||
rc = idxd_register_dma_device(idxd);
|
||||
if (rc < 0) {
|
||||
idxd_device_disable(idxd);
|
||||
idxd_device_evl_free(idxd);
|
||||
idxd->cmd_status = IDXD_SCMD_DEV_DMA_ERR;
|
||||
return rc;
|
||||
}
|
||||
@@ -1488,6 +1596,7 @@ void idxd_device_drv_remove(struct idxd_dev *idxd_dev)
|
||||
idxd_device_disable(idxd);
|
||||
if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
|
||||
idxd_device_reset(idxd);
|
||||
idxd_device_evl_free(idxd);
|
||||
}
|
||||
|
||||
static enum idxd_dev_type dev_types[] = {
|
||||
|
||||
@@ -32,6 +32,7 @@ enum idxd_dev_type {
|
||||
IDXD_DEV_GROUP,
|
||||
IDXD_DEV_ENGINE,
|
||||
IDXD_DEV_CDEV,
|
||||
IDXD_DEV_CDEV_FILE,
|
||||
IDXD_DEV_MAX_TYPE,
|
||||
};
|
||||
|
||||
@@ -127,6 +128,12 @@ struct idxd_pmu {
|
||||
|
||||
#define IDXD_MAX_PRIORITY 0xf
|
||||
|
||||
enum {
|
||||
COUNTER_FAULTS = 0,
|
||||
COUNTER_FAULT_FAILS,
|
||||
COUNTER_MAX
|
||||
};
|
||||
|
||||
enum idxd_wq_state {
|
||||
IDXD_WQ_DISABLED = 0,
|
||||
IDXD_WQ_ENABLED,
|
||||
@@ -136,6 +143,7 @@ enum idxd_wq_flag {
|
||||
WQ_FLAG_DEDICATED = 0,
|
||||
WQ_FLAG_BLOCK_ON_FAULT,
|
||||
WQ_FLAG_ATS_DISABLE,
|
||||
WQ_FLAG_PRS_DISABLE,
|
||||
};
|
||||
|
||||
enum idxd_wq_type {
|
||||
@@ -185,6 +193,7 @@ struct idxd_wq {
|
||||
struct idxd_dev idxd_dev;
|
||||
struct idxd_cdev *idxd_cdev;
|
||||
struct wait_queue_head err_queue;
|
||||
struct workqueue_struct *wq;
|
||||
struct idxd_device *idxd;
|
||||
int id;
|
||||
struct idxd_irq_entry ie;
|
||||
@@ -214,6 +223,10 @@ struct idxd_wq {
|
||||
char name[WQ_NAME_SIZE + 1];
|
||||
u64 max_xfer_bytes;
|
||||
u32 max_batch_size;
|
||||
|
||||
/* Lock to protect upasid_xa access. */
|
||||
struct mutex uc_lock;
|
||||
struct xarray upasid_xa;
|
||||
};
|
||||
|
||||
struct idxd_engine {
|
||||
@@ -232,6 +245,7 @@ struct idxd_hw {
|
||||
union engine_cap_reg engine_cap;
|
||||
struct opcap opcap;
|
||||
u32 cmd_cap;
|
||||
union iaa_cap_reg iaa_cap;
|
||||
};
|
||||
|
||||
enum idxd_device_state {
|
||||
@@ -258,6 +272,32 @@ struct idxd_driver_data {
|
||||
struct device_type *dev_type;
|
||||
int compl_size;
|
||||
int align;
|
||||
int evl_cr_off;
|
||||
int cr_status_off;
|
||||
int cr_result_off;
|
||||
};
|
||||
|
||||
struct idxd_evl {
|
||||
/* Lock to protect event log access. */
|
||||
spinlock_t lock;
|
||||
void *log;
|
||||
dma_addr_t dma;
|
||||
/* Total size of event log = number of entries * entry size. */
|
||||
unsigned int log_size;
|
||||
/* The number of entries in the event log. */
|
||||
u16 size;
|
||||
u16 head;
|
||||
unsigned long *bmap;
|
||||
bool batch_fail[IDXD_MAX_BATCH_IDENT];
|
||||
};
|
||||
|
||||
struct idxd_evl_fault {
|
||||
struct work_struct work;
|
||||
struct idxd_wq *wq;
|
||||
u8 status;
|
||||
|
||||
/* make this last member always */
|
||||
struct __evl_entry entry[];
|
||||
};
|
||||
|
||||
struct idxd_device {
|
||||
@@ -316,8 +356,24 @@ struct idxd_device {
|
||||
struct idxd_pmu *idxd_pmu;
|
||||
|
||||
unsigned long *opcap_bmap;
|
||||
struct idxd_evl *evl;
|
||||
struct kmem_cache *evl_cache;
|
||||
|
||||
struct dentry *dbgfs_dir;
|
||||
struct dentry *dbgfs_evl_file;
|
||||
};
|
||||
|
||||
static inline unsigned int evl_ent_size(struct idxd_device *idxd)
|
||||
{
|
||||
return idxd->hw.gen_cap.evl_support ?
|
||||
(32 * (1 << idxd->hw.gen_cap.evl_support)) : 0;
|
||||
}
|
||||
|
||||
static inline unsigned int evl_size(struct idxd_device *idxd)
|
||||
{
|
||||
return idxd->evl->size * evl_ent_size(idxd);
|
||||
}
|
||||
|
||||
/* IDXD software descriptor */
|
||||
struct idxd_desc {
|
||||
union {
|
||||
@@ -351,6 +407,7 @@ enum idxd_completion_status {
|
||||
#define engine_confdev(engine) &engine->idxd_dev.conf_dev
|
||||
#define group_confdev(group) &group->idxd_dev.conf_dev
|
||||
#define cdev_dev(cdev) &cdev->idxd_dev.conf_dev
|
||||
#define user_ctx_dev(ctx) (&(ctx)->idxd_dev.conf_dev)
|
||||
|
||||
#define confdev_to_idxd_dev(dev) container_of(dev, struct idxd_dev, conf_dev)
|
||||
#define idxd_dev_to_idxd(idxd_dev) container_of(idxd_dev, struct idxd_device, idxd_dev)
|
||||
@@ -598,6 +655,7 @@ int idxd_register_driver(void);
|
||||
void idxd_unregister_driver(void);
|
||||
void idxd_wqs_quiesce(struct idxd_device *idxd);
|
||||
bool idxd_queue_int_handle_resubmit(struct idxd_desc *desc);
|
||||
void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count);
|
||||
|
||||
/* device interrupt control */
|
||||
irqreturn_t idxd_misc_thread(int vec, void *data);
|
||||
@@ -662,6 +720,9 @@ void idxd_cdev_remove(void);
|
||||
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);
|
||||
int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
|
||||
void *buf, int len);
|
||||
void idxd_user_counter_increment(struct idxd_wq *wq, u32 pasid, int index);
|
||||
|
||||
/* perfmon */
|
||||
#if IS_ENABLED(CONFIG_INTEL_IDXD_PERFMON)
|
||||
@@ -678,4 +739,10 @@ static inline void perfmon_init(void) {}
|
||||
static inline void perfmon_exit(void) {}
|
||||
#endif
|
||||
|
||||
/* debugfs */
|
||||
int idxd_device_init_debugfs(struct idxd_device *idxd);
|
||||
void idxd_device_remove_debugfs(struct idxd_device *idxd);
|
||||
int idxd_init_debugfs(void);
|
||||
void idxd_remove_debugfs(void);
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user