mirror of
https://github.com/torvalds/linux.git
synced 2026-06-06 05:27:07 +02:00
Merge fb893de323 ("Merge tag 'tag-chrome-platform-for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux") into android-mainline
Steps along the way to 5.9-rc1 Fixes conflicts in: drivers/iommu/Kconfig Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I18e6789f4d31ebe065aeacd47411b31fa928f6b4
This commit is contained in:
commit
eb1a2a5f9b
|
|
@ -202,6 +202,25 @@ Description:
|
|||
functions. See the section named 'NVDIMM Root Device _DSMs' in
|
||||
the ACPI specification.
|
||||
|
||||
What: /sys/bus/nd/devices/ndbusX/nfit/firmware_activate_noidle
|
||||
Date: Apr, 2020
|
||||
KernelVersion: v5.8
|
||||
Contact: linux-nvdimm@lists.01.org
|
||||
Description:
|
||||
(RW) The Intel platform implementation of firmware activate
|
||||
support exposes an option let the platform force idle devices in
|
||||
the system over the activation event, or trust that the OS will
|
||||
do it. The safe default is to let the platform force idle
|
||||
devices since the kernel is already in a suspend state, and on
|
||||
the chance that a driver does not properly quiesce bus-mastering
|
||||
after a suspend callback the platform will handle it. However,
|
||||
the activation might abort if, for example, platform firmware
|
||||
determines that the activation time exceeds the max PCI-E
|
||||
completion timeout. Since the platform does not know whether the
|
||||
OS is running the activation from a suspend context it aborts,
|
||||
but if the system owner trusts driver suspend callback to be
|
||||
sufficient then 'firmware_activation_noidle' can be
|
||||
enabled to bypass the activation abort.
|
||||
|
||||
What: /sys/bus/nd/devices/regionX/nfit/range_index
|
||||
Date: Jun, 2015
|
||||
|
|
|
|||
2
Documentation/ABI/testing/sysfs-bus-nvdimm
Normal file
2
Documentation/ABI/testing/sysfs-bus-nvdimm
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
The libnvdimm sub-system implements a common sysfs interface for
|
||||
platform nvdimm resources. See Documentation/driver-api/nvdimm/.
|
||||
|
|
@ -125,6 +125,9 @@ stable kernels.
|
|||
| Cavium | ThunderX2 Core | #219 | CAVIUM_TX2_ERRATUM_219 |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Marvell | ARM-MMU-500 | #582743 | N/A |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/arm/keystone/ti,k3-sci-common.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common K3 TI-SCI bindings
|
||||
|
||||
maintainers:
|
||||
- Nishanth Menon <nm@ti.com>
|
||||
|
||||
description: |
|
||||
The TI K3 family of SoCs usually have a central System Controller Processor
|
||||
that is responsible for managing various SoC-level resources like clocks,
|
||||
resets, interrupts etc. The communication with that processor is performed
|
||||
through the TI-SCI protocol.
|
||||
|
||||
Each specific device management node like a clock controller node, a reset
|
||||
controller node or an interrupt-controller node should define a common set
|
||||
of properties that enables them to implement the corresponding functionality
|
||||
over the TI-SCI protocol. The following are some of the common properties
|
||||
needed by such individual nodes. The required properties for each device
|
||||
management node is defined in the respective binding.
|
||||
|
||||
properties:
|
||||
ti,sci:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Should be a phandle to the TI-SCI System Controller node
|
||||
|
||||
ti,sci-dev-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Should contain the TI-SCI device id corresponding to the device. Please
|
||||
refer to the corresponding System Controller documentation for valid
|
||||
values for the desired device.
|
||||
|
||||
ti,sci-proc-ids:
|
||||
description: Should contain a single tuple of <proc_id host_id>.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: TI-SCI processor id for the remote processor device
|
||||
- description: TI-SCI host id to which processor control ownership
|
||||
should be transferred to
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
Qualcomm Hardware Mutex Block:
|
||||
|
||||
The hardware block provides mutexes utilized between different processors on
|
||||
the SoC as part of the communication protocol used by these processors.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,sfpb-mutex",
|
||||
"qcom,tcsr-mutex"
|
||||
|
||||
- syscon:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: one cell containing:
|
||||
syscon phandle
|
||||
offset of the hwmutex block within the syscon
|
||||
stride of the hwmutex registers
|
||||
|
||||
- #hwlock-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: must be 1, the specified cell represent the lock id
|
||||
(hwlock standard property, see hwlock.txt)
|
||||
|
||||
Example:
|
||||
|
||||
tcsr_mutex_block: syscon@fd484000 {
|
||||
compatible = "syscon";
|
||||
reg = <0xfd484000 0x2000>;
|
||||
};
|
||||
|
||||
hwlock@fd484000 {
|
||||
compatible = "qcom,tcsr-mutex";
|
||||
syscon = <&tcsr_mutex_block 0 0x80>;
|
||||
|
||||
#hwlock-cells = <1>;
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwlock/qcom-hwspinlock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Hardware Mutex Block
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description:
|
||||
The hardware block provides mutexes utilized between different processors on
|
||||
the SoC as part of the communication protocol used by these processors.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sfpb-mutex
|
||||
- qcom,tcsr-mutex
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#hwlock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#hwlock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
tcsr_mutex: hwlock@1f40000 {
|
||||
compatible = "qcom,tcsr-mutex";
|
||||
reg = <0x01f40000 0x40000>;
|
||||
#hwlock-cells = <1>;
|
||||
};
|
||||
...
|
||||
|
|
@ -37,7 +37,18 @@ properties:
|
|||
- enum:
|
||||
- qcom,sc7180-smmu-500
|
||||
- qcom,sdm845-smmu-500
|
||||
- qcom,sm8150-smmu-500
|
||||
- qcom,sm8250-smmu-500
|
||||
- const: arm,mmu-500
|
||||
- description: Marvell SoCs implementing "arm,mmu-500"
|
||||
items:
|
||||
- const: marvell,ap806-smmu-500
|
||||
- const: arm,mmu-500
|
||||
- description: NVIDIA SoCs that program two ARM MMU-500s identically
|
||||
items:
|
||||
- enum:
|
||||
- nvidia,tegra194-smmu
|
||||
- const: nvidia,smmu-500
|
||||
- items:
|
||||
- const: arm,mmu-500
|
||||
- const: arm,smmu-v2
|
||||
|
|
@ -55,7 +66,8 @@ properties:
|
|||
- cavium,smmu-v2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
'#global-interrupts':
|
||||
description: The number of global interrupts exposed by the device.
|
||||
|
|
@ -138,6 +150,23 @@ required:
|
|||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- nvidia,tegra194-smmu
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
examples:
|
||||
- |+
|
||||
/* SMMU with stream matching or stream indexing */
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ Required properties:
|
|||
- compatible : must be one of the following string:
|
||||
"mediatek,mt2701-m4u" for mt2701 which uses generation one m4u HW.
|
||||
"mediatek,mt2712-m4u" for mt2712 which uses generation two m4u HW.
|
||||
"mediatek,mt6779-m4u" for mt6779 which uses generation two m4u HW.
|
||||
"mediatek,mt7623-m4u", "mediatek,mt2701-m4u" for mt7623 which uses
|
||||
generation one m4u HW.
|
||||
"mediatek,mt8173-m4u" for mt8173 which uses generation two m4u HW.
|
||||
|
|
@ -78,6 +79,7 @@ Required properties:
|
|||
Specifies the mtk_m4u_id as defined in
|
||||
dt-binding/memory/mt2701-larb-port.h for mt2701, mt7623
|
||||
dt-binding/memory/mt2712-larb-port.h for mt2712,
|
||||
dt-binding/memory/mt6779-larb-port.h for mt6779,
|
||||
dt-binding/memory/mt8173-larb-port.h for mt8173, and
|
||||
dt-binding/memory/mt8183-larb-port.h for mt8183.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ properties:
|
|||
- renesas,ipmmu-r8a774c0 # RZ/G2E
|
||||
- renesas,ipmmu-r8a7795 # R-Car H3
|
||||
- renesas,ipmmu-r8a7796 # R-Car M3-W
|
||||
- renesas,ipmmu-r8a77961 # R-Car M3-W+
|
||||
- renesas,ipmmu-r8a77965 # R-Car M3-N
|
||||
- renesas,ipmmu-r8a77970 # R-Car V3M
|
||||
- renesas,ipmmu-r8a77980 # R-Car V3H
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
|
|||
Mediatek SMI have two generations of HW architecture, here is the list
|
||||
which generation the SoCs use:
|
||||
generation 1: mt2701 and mt7623.
|
||||
generation 2: mt2712, mt8173 and mt8183.
|
||||
generation 2: mt2712, mt6779, mt8173 and mt8183.
|
||||
|
||||
There's slight differences between the two SMI, for generation 2, the
|
||||
register which control the iommu port is at each larb's register base. But
|
||||
|
|
@ -18,6 +18,7 @@ Required properties:
|
|||
- compatible : must be one of :
|
||||
"mediatek,mt2701-smi-common"
|
||||
"mediatek,mt2712-smi-common"
|
||||
"mediatek,mt6779-smi-common"
|
||||
"mediatek,mt7623-smi-common", "mediatek,mt2701-smi-common"
|
||||
"mediatek,mt8173-smi-common"
|
||||
"mediatek,mt8183-smi-common"
|
||||
|
|
@ -35,7 +36,7 @@ Required properties:
|
|||
and these 2 option clocks for generation 2 smi HW:
|
||||
- "gals0": the path0 clock of GALS(Global Async Local Sync).
|
||||
- "gals1": the path1 clock of GALS(Global Async Local Sync).
|
||||
Here is the list which has this GALS: mt8183.
|
||||
Here is the list which has this GALS: mt6779 and mt8183.
|
||||
|
||||
Example:
|
||||
smi_common: smi@14022000 {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Required properties:
|
|||
- compatible : must be one of :
|
||||
"mediatek,mt2701-smi-larb"
|
||||
"mediatek,mt2712-smi-larb"
|
||||
"mediatek,mt6779-smi-larb"
|
||||
"mediatek,mt7623-smi-larb", "mediatek,mt2701-smi-larb"
|
||||
"mediatek,mt8173-smi-larb"
|
||||
"mediatek,mt8183-smi-larb"
|
||||
|
|
@ -21,7 +22,7 @@ Required properties:
|
|||
- "gals": the clock for GALS(Global Async Local Sync).
|
||||
Here is the list which has this GALS: mt8183.
|
||||
|
||||
Required property for mt2701, mt2712 and mt7623:
|
||||
Required property for mt2701, mt2712, mt6779 and mt7623:
|
||||
- mediatek,larb-id :the hardware id of this larb.
|
||||
|
||||
Example:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/remoteproc/qcom,pil-info.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm peripheral image loader relocation info binding
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description:
|
||||
The Qualcomm peripheral image loader relocation memory region, in IMEM, is
|
||||
used for communicating remoteproc relocation information to post mortem
|
||||
debugging tools.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,pil-reloc-info
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
imem@146bf000 {
|
||||
compatible = "syscon", "simple-mfd";
|
||||
reg = <0x146bf000 0x1000>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
ranges = <0 0x146bf000 0x1000>;
|
||||
|
||||
pil-reloc@94c {
|
||||
compatible = "qcom,pil-reloc-info";
|
||||
reg = <0x94c 0xc8>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/remoteproc/ti,k3-dsp-rproc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI K3 DSP devices
|
||||
|
||||
maintainers:
|
||||
- Suman Anna <s-anna@ti.com>
|
||||
|
||||
description: |
|
||||
The TI K3 family of SoCs usually have one or more TI DSP Core sub-systems
|
||||
that are used to offload some of the processor-intensive tasks or algorithms,
|
||||
for achieving various system level goals.
|
||||
|
||||
These processor sub-systems usually contain additional sub-modules like
|
||||
L1 and/or L2 caches/SRAMs, an Interrupt Controller, an external memory
|
||||
controller, a dedicated local power/sleep controller etc. The DSP processor
|
||||
cores in the K3 SoCs are usually either a TMS320C66x CorePac processor or a
|
||||
TMS320C71x CorePac processor.
|
||||
|
||||
Each DSP Core sub-system is represented as a single DT node. Each node has a
|
||||
number of required or optional properties that enable the OS running on the
|
||||
host processor (Arm CorePac) to perform the device management of the remote
|
||||
processor and to communicate with the remote processor.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,j721e-c66-dsp
|
||||
- ti,j721e-c71-dsp
|
||||
description:
|
||||
Use "ti,j721e-c66-dsp" for C66x DSPs on K3 J721E SoCs
|
||||
Use "ti,j721e-c71-dsp" for C71x DSPs on K3 J721E SoCs
|
||||
|
||||
resets:
|
||||
description: |
|
||||
Should contain the phandle to the reset controller node managing the
|
||||
local resets for this device, and a reset specifier.
|
||||
maxItems: 1
|
||||
|
||||
firmware-name:
|
||||
description: |
|
||||
Should contain the name of the default firmware image
|
||||
file located on the firmware search path
|
||||
|
||||
mboxes:
|
||||
description: |
|
||||
OMAP Mailbox specifier denoting the sub-mailbox, to be used for
|
||||
communication with the remote processor. This property should match
|
||||
with the sub-mailbox node used in the firmware image.
|
||||
maxItems: 1
|
||||
|
||||
memory-region:
|
||||
minItems: 2
|
||||
maxItems: 8
|
||||
description: |
|
||||
phandle to the reserved memory nodes to be associated with the remoteproc
|
||||
device. There should be at least two reserved memory nodes defined. The
|
||||
reserved memory nodes should be carveout nodes, and should be defined as
|
||||
per the bindings in
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
items:
|
||||
- description: region used for dynamic DMA allocations like vrings and
|
||||
vring buffers
|
||||
- description: region reserved for firmware image sections
|
||||
additionalItems: true
|
||||
|
||||
# Optional properties:
|
||||
# --------------------
|
||||
|
||||
sram:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description: |
|
||||
phandles to one or more reserved on-chip SRAM regions. The regions
|
||||
should be defined as child nodes of the respective SRAM node, and
|
||||
should be defined as per the generic bindings in,
|
||||
Documentation/devicetree/bindings/sram/sram.yaml
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,j721e-c66-dsp
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Address and Size of the L2 SRAM internal memory region
|
||||
- description: Address and Size of the L1 PRAM internal memory region
|
||||
- description: Address and Size of the L1 DRAM internal memory region
|
||||
reg-names:
|
||||
items:
|
||||
- const: l2sram
|
||||
- const: l1pram
|
||||
- const: l1dram
|
||||
else:
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,j721e-c71-dsp
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Address and Size of the L2 SRAM internal memory region
|
||||
- description: Address and Size of the L1 DRAM internal memory region
|
||||
reg-names:
|
||||
items:
|
||||
- const: l2sram
|
||||
- const: l1dram
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- ti,sci
|
||||
- ti,sci-dev-id
|
||||
- ti,sci-proc-ids
|
||||
- resets
|
||||
- firmware-name
|
||||
- mboxes
|
||||
- memory-region
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
/ {
|
||||
model = "Texas Instruments K3 J721E SoC";
|
||||
compatible = "ti,j721e";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
bus@100000 {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges = <0x00 0x00100000 0x00 0x00100000 0x00 0x00020000>, /* ctrl mmr */
|
||||
<0x00 0x64800000 0x00 0x64800000 0x00 0x00800000>, /* C71_0 */
|
||||
<0x4d 0x80800000 0x4d 0x80800000 0x00 0x00800000>, /* C66_0 */
|
||||
<0x4d 0x81800000 0x4d 0x81800000 0x00 0x00800000>; /* C66_1 */
|
||||
|
||||
/* J721E C66_0 DSP node */
|
||||
dsp@4d80800000 {
|
||||
compatible = "ti,j721e-c66-dsp";
|
||||
reg = <0x4d 0x80800000 0x00 0x00048000>,
|
||||
<0x4d 0x80e00000 0x00 0x00008000>,
|
||||
<0x4d 0x80f00000 0x00 0x00008000>;
|
||||
reg-names = "l2sram", "l1pram", "l1dram";
|
||||
ti,sci = <&dmsc>;
|
||||
ti,sci-dev-id = <142>;
|
||||
ti,sci-proc-ids = <0x03 0xFF>;
|
||||
resets = <&k3_reset 142 1>;
|
||||
firmware-name = "j7-c66_0-fw";
|
||||
memory-region = <&c66_0_dma_memory_region>,
|
||||
<&c66_0_memory_region>;
|
||||
mboxes = <&mailbox0_cluster3 &mbox_c66_0>;
|
||||
};
|
||||
|
||||
/* J721E C71_0 DSP node */
|
||||
c71_0: dsp@64800000 {
|
||||
compatible = "ti,j721e-c71-dsp";
|
||||
reg = <0x00 0x64800000 0x00 0x00080000>,
|
||||
<0x00 0x64e00000 0x00 0x0000c000>;
|
||||
reg-names = "l2sram", "l1dram";
|
||||
ti,sci = <&dmsc>;
|
||||
ti,sci-dev-id = <15>;
|
||||
ti,sci-proc-ids = <0x30 0xFF>;
|
||||
resets = <&k3_reset 15 1>;
|
||||
firmware-name = "j7-c71_0-fw";
|
||||
memory-region = <&c71_0_dma_memory_region>,
|
||||
<&c71_0_memory_region>;
|
||||
mboxes = <&mailbox0_cluster4 &mbox_c71_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
86
Documentation/driver-api/nvdimm/firmware-activate.rst
Normal file
86
Documentation/driver-api/nvdimm/firmware-activate.rst
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==================================
|
||||
NVDIMM Runtime Firmware Activation
|
||||
==================================
|
||||
|
||||
Some persistent memory devices run a firmware locally on the device /
|
||||
"DIMM" to perform tasks like media management, capacity provisioning,
|
||||
and health monitoring. The process of updating that firmware typically
|
||||
involves a reboot because it has implications for in-flight memory
|
||||
transactions. However, reboots are disruptive and at least the Intel
|
||||
persistent memory platform implementation, described by the Intel ACPI
|
||||
DSM specification [1], has added support for activating firmware at
|
||||
runtime.
|
||||
|
||||
A native sysfs interface is implemented in libnvdimm to allow platform
|
||||
to advertise and control their local runtime firmware activation
|
||||
capability.
|
||||
|
||||
The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate
|
||||
attribute that shows the state of the firmware activation as one of 'idle',
|
||||
'armed', 'overflow', and 'busy'.
|
||||
|
||||
- idle:
|
||||
No devices are set / armed to activate firmware
|
||||
|
||||
- armed:
|
||||
At least one device is armed
|
||||
|
||||
- busy:
|
||||
In the busy state armed devices are in the process of transitioning
|
||||
back to idle and completing an activation cycle.
|
||||
|
||||
- overflow:
|
||||
If the platform has a concept of incremental work needed to perform
|
||||
the activation it could be the case that too many DIMMs are armed for
|
||||
activation. In that scenario the potential for firmware activation to
|
||||
timeout is indicated by the 'overflow' state.
|
||||
|
||||
The 'ndbusX/firmware/activate' property can be written with a value of
|
||||
either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to
|
||||
run firmware activation from within the equivalent of the hibernation
|
||||
'freeze' state where drivers and applications are notified to stop their
|
||||
modifications of system memory. A value of 'live' attempts
|
||||
firmware activation without this hibernation cycle. The
|
||||
'ndbusX/firmware/activate' property will be elided completely if no
|
||||
firmware activation capability is detected.
|
||||
|
||||
Another property 'ndbusX/firmware/capability' indicates a value of
|
||||
'live' or 'quiesce', where 'live' indicates that the firmware
|
||||
does not require or inflict any quiesce period on the system to update
|
||||
firmware. A capability value of 'quiesce' indicates that firmware does
|
||||
expect and injects a quiet period for the memory controller, but 'live'
|
||||
may still be written to 'ndbusX/firmware/activate' as an override to
|
||||
assume the risk of racing firmware update with in-flight device and
|
||||
application activity. The 'ndbusX/firmware/capability' property will be
|
||||
elided completely if no firmware activation capability is detected.
|
||||
|
||||
The libnvdimm memory-device / DIMM object, nmemX, implements
|
||||
'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to
|
||||
communicate the per-device firmware activation state. Similar to the
|
||||
'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate'
|
||||
attribute indicates 'idle', 'armed', or 'busy'. The state transitions
|
||||
from 'armed' to 'idle' when the system is prepared to activate firmware,
|
||||
firmware staged + state set to armed, and 'ndbusX/firmware/activate' is
|
||||
triggered. After that activation event the nmemX/firmware/result
|
||||
attribute reflects the state of the last activation as one of:
|
||||
|
||||
- none:
|
||||
No runtime activation triggered since the last time the device was reset
|
||||
|
||||
- success:
|
||||
The last runtime activation completed successfully.
|
||||
|
||||
- fail:
|
||||
The last runtime activation failed for device-specific reasons.
|
||||
|
||||
- not_staged:
|
||||
The last runtime activation failed due to a sequencing error of the
|
||||
firmware image not being staged.
|
||||
|
||||
- need_reset:
|
||||
Runtime firmware activation failed, but the firmware can still be
|
||||
activated via the legacy method of power-cycling the system.
|
||||
|
||||
[1]: https://docs.pmem.io/persistent-memory/
|
||||
|
|
@ -110,14 +110,14 @@ contain files named "0", "1", "2", ... The file numbers also represent
|
|||
increasing zone start sector on the device.
|
||||
|
||||
All read and write operations to zone files are not allowed beyond the file
|
||||
maximum size, that is, beyond the zone size. Any access exceeding the zone
|
||||
size is failed with the -EFBIG error.
|
||||
maximum size, that is, beyond the zone capacity. Any access exceeding the zone
|
||||
capacity is failed with the -EFBIG error.
|
||||
|
||||
Creating, deleting, renaming or modifying any attribute of files and
|
||||
sub-directories is not allowed.
|
||||
|
||||
The number of blocks of a file as reported by stat() and fstat() indicates the
|
||||
size of the file zone, or in other words, the maximum file size.
|
||||
capacity of the zone file, or in other words, the maximum file size.
|
||||
|
||||
Conventional zone files
|
||||
-----------------------
|
||||
|
|
@ -156,8 +156,8 @@ all accepted.
|
|||
|
||||
Truncating sequential zone files is allowed only down to 0, in which case, the
|
||||
zone is reset to rewind the file zone write pointer position to the start of
|
||||
the zone, or up to the zone size, in which case the file's zone is transitioned
|
||||
to the FULL state (finish zone operation).
|
||||
the zone, or up to the zone capacity, in which case the file's zone is
|
||||
transitioned to the FULL state (finish zone operation).
|
||||
|
||||
Format options
|
||||
--------------
|
||||
|
|
@ -324,7 +324,7 @@ file size set to 0. This is necessary as the write pointer of read-only zones
|
|||
is defined as invalib by the ZBC and ZAC standards, making it impossible to
|
||||
discover the amount of data that has been written to the zone. In the case of a
|
||||
read-only zone discovered at run-time, as indicated in the previous section.
|
||||
the size of the zone file is left unchanged from its last updated value.
|
||||
The size of the zone file is left unchanged from its last updated value.
|
||||
|
||||
Zonefs User Space Tools
|
||||
=======================
|
||||
|
|
@ -401,8 +401,9 @@ append-writes to the file::
|
|||
# ls -l /mnt/seq/0
|
||||
-rw-r----- 1 root root 0 Nov 25 13:49 /mnt/seq/0
|
||||
|
||||
Since files are statically mapped to zones on the disk, the number of blocks of
|
||||
a file as reported by stat() and fstat() indicates the size of the file zone::
|
||||
Since files are statically mapped to zones on the disk, the number of blocks
|
||||
of a file as reported by stat() and fstat() indicates the capacity of the file
|
||||
zone::
|
||||
|
||||
# stat /mnt/seq/0
|
||||
File: /mnt/seq/0
|
||||
|
|
@ -416,5 +417,6 @@ a file as reported by stat() and fstat() indicates the size of the file zone::
|
|||
|
||||
The number of blocks of the file ("Blocks") in units of 512B blocks gives the
|
||||
maximum file size of 524288 * 512 B = 256 MB, corresponding to the device zone
|
||||
size in this example. Of note is that the "IO block" field always indicates the
|
||||
minimum I/O size for writes and corresponds to the device physical sector size.
|
||||
capacity in this example. Of note is that the "IO block" field always
|
||||
indicates the minimum I/O size for writes and corresponds to the device
|
||||
physical sector size.
|
||||
|
|
|
|||
12
Documentation/gpu/backlight.rst
Normal file
12
Documentation/gpu/backlight.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
=================
|
||||
Backlight support
|
||||
=================
|
||||
|
||||
.. kernel-doc:: drivers/video/backlight/backlight.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/linux/backlight.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/video/backlight/backlight.c
|
||||
:export:
|
||||
|
|
@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
|
|||
drm-uapi
|
||||
drm-client
|
||||
drivers
|
||||
backlight
|
||||
vga-switcheroo
|
||||
vgaarbiter
|
||||
todo
|
||||
|
|
|
|||
|
|
@ -192,9 +192,9 @@ Returns 0 on success and an appropriate error value on failure.
|
|||
|
||||
::
|
||||
|
||||
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
|
||||
void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
|
||||
void *priv, u32 addr);
|
||||
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
|
||||
rpmsg_rx_cb_t cb, void *priv,
|
||||
struct rpmsg_channel_info chinfo);
|
||||
|
||||
every rpmsg address in the system is bound to an rx callback (so when
|
||||
inbound messages arrive, they are dispatched by the rpmsg bus using the
|
||||
|
|
|
|||
|
|
@ -339,6 +339,7 @@ Code Seq# Include File Comments
|
|||
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
|
||||
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
|
||||
0xB6 all linux/fpga-dfl.h
|
||||
0xB7 all uapi/linux/remoteproc_cdev.h <mailto:linux-remoteproc@vger.kernel.org>
|
||||
0xC0 00-0F linux/usb/iowarrior.h
|
||||
0xCA 00-0F uapi/misc/cxl.h
|
||||
0xCA 10-2F uapi/misc/ocxl.h
|
||||
|
|
|
|||
|
|
@ -1505,7 +1505,7 @@ R: Robin Murphy <robin.murphy@arm.com>
|
|||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iommu/arm,smmu*
|
||||
F: drivers/iommu/arm-smmu*
|
||||
F: drivers/iommu/arm/
|
||||
F: drivers/iommu/io-pgtable-arm-v7s.c
|
||||
F: drivers/iommu/io-pgtable-arm.c
|
||||
|
||||
|
|
@ -9109,6 +9109,7 @@ F: drivers/iommu/
|
|||
F: include/linux/iommu.h
|
||||
F: include/linux/iova.h
|
||||
F: include/linux/of_iommu.h
|
||||
F: include/uapi/linux/iommu.h
|
||||
|
||||
IO_URING
|
||||
M: Jens Axboe <axboe@kernel.dk>
|
||||
|
|
@ -16986,8 +16987,10 @@ F: drivers/i2c/busses/i2c-tegra.c
|
|||
|
||||
TEGRA IOMMU DRIVERS
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
R: Krishna Reddy <vdumpa@nvidia.com>
|
||||
L: linux-tegra@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c
|
||||
F: drivers/iommu/tegra*
|
||||
|
||||
TEGRA KBC DRIVER
|
||||
|
|
@ -17078,6 +17081,7 @@ M: Tero Kristo <t-kristo@ti.com>
|
|||
M: Santosh Shilimkar <ssantosh@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/arm/keystone/ti,k3-sci-common.yaml
|
||||
F: Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
|
||||
F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ struct dev_archdata {
|
|||
#ifdef CONFIG_DMABOUNCE
|
||||
struct dmabounce_device_info *dmabounce;
|
||||
#endif
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
void *iommu; /* private IOMMU data */
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_DMA_USE_IOMMU
|
||||
struct dma_iommu_mapping *mapping;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
#define __ASM_DEVICE_H
|
||||
|
||||
struct dev_archdata {
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
void *iommu; /* private IOMMU data */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
#define _ASM_IA64_DEVICE_H
|
||||
|
||||
struct dev_archdata {
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
void *iommu; /* hook for IOMMU specific extension */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ struct dev_archdata {
|
|||
struct iommu_table *iommu_table_base;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
void *iommu_domain;
|
||||
#endif
|
||||
#ifdef CONFIG_PPC64
|
||||
struct pci_dn *pci_data;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
|
|||
}
|
||||
break;
|
||||
case VHOST_USER_SLAVE_IOTLB_MSG:
|
||||
/* not supported - VIRTIO_F_IOMMU_PLATFORM */
|
||||
/* not supported - VIRTIO_F_ACCESS_PLATFORM */
|
||||
case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
|
||||
/* not supported - VHOST_USER_PROTOCOL_F_HOST_NOTIFIER */
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
#define _ASM_X86_DEVICE_H
|
||||
|
||||
struct dev_archdata {
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
void *iommu; /* hook for IOMMU specific extension */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
|
|
|
|||
|
|
@ -10667,11 +10667,17 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
|
|||
{
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(cons, struct kvm_kernel_irqfd, consumer);
|
||||
int ret;
|
||||
|
||||
irqfd->producer = prod;
|
||||
kvm_arch_start_assignment(irqfd->kvm);
|
||||
ret = kvm_x86_ops.update_pi_irte(irqfd->kvm,
|
||||
prod->irq, irqfd->gsi, 1);
|
||||
|
||||
return kvm_x86_ops.update_pi_irte(irqfd->kvm,
|
||||
prod->irq, irqfd->gsi, 1);
|
||||
if (ret)
|
||||
kvm_arch_end_assignment(irqfd->kvm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
|
||||
|
|
@ -10694,6 +10700,8 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
|
|||
if (ret)
|
||||
printk(KERN_INFO "irq bypass consumer (token %p) unregistration"
|
||||
" fails: %d\n", irqfd->consumer.token, ret);
|
||||
|
||||
kvm_arch_end_assignment(irqfd->kvm);
|
||||
}
|
||||
|
||||
int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,18 @@ const guid_t *to_nfit_uuid(enum nfit_uuids id)
|
|||
}
|
||||
EXPORT_SYMBOL(to_nfit_uuid);
|
||||
|
||||
static const guid_t *to_nfit_bus_uuid(int family)
|
||||
{
|
||||
if (WARN_ONCE(family == NVDIMM_BUS_FAMILY_NFIT,
|
||||
"only secondary bus families can be translated\n"))
|
||||
return NULL;
|
||||
/*
|
||||
* The index of bus UUIDs starts immediately following the last
|
||||
* NVDIMM/leaf family.
|
||||
*/
|
||||
return to_nfit_uuid(family + NVDIMM_FAMILY_MAX);
|
||||
}
|
||||
|
||||
static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
|
||||
|
|
@ -362,24 +374,8 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func)
|
|||
{
|
||||
static const u8 revid_table[NVDIMM_FAMILY_MAX+1][NVDIMM_CMD_MAX+1] = {
|
||||
[NVDIMM_FAMILY_INTEL] = {
|
||||
[NVDIMM_INTEL_GET_MODES] = 2,
|
||||
[NVDIMM_INTEL_GET_FWINFO] = 2,
|
||||
[NVDIMM_INTEL_START_FWUPDATE] = 2,
|
||||
[NVDIMM_INTEL_SEND_FWUPDATE] = 2,
|
||||
[NVDIMM_INTEL_FINISH_FWUPDATE] = 2,
|
||||
[NVDIMM_INTEL_QUERY_FWUPDATE] = 2,
|
||||
[NVDIMM_INTEL_SET_THRESHOLD] = 2,
|
||||
[NVDIMM_INTEL_INJECT_ERROR] = 2,
|
||||
[NVDIMM_INTEL_GET_SECURITY_STATE] = 2,
|
||||
[NVDIMM_INTEL_SET_PASSPHRASE] = 2,
|
||||
[NVDIMM_INTEL_DISABLE_PASSPHRASE] = 2,
|
||||
[NVDIMM_INTEL_UNLOCK_UNIT] = 2,
|
||||
[NVDIMM_INTEL_FREEZE_LOCK] = 2,
|
||||
[NVDIMM_INTEL_SECURE_ERASE] = 2,
|
||||
[NVDIMM_INTEL_OVERWRITE] = 2,
|
||||
[NVDIMM_INTEL_QUERY_OVERWRITE] = 2,
|
||||
[NVDIMM_INTEL_SET_MASTER_PASSPHRASE] = 2,
|
||||
[NVDIMM_INTEL_MASTER_SECURE_ERASE] = 2,
|
||||
[NVDIMM_INTEL_GET_MODES ...
|
||||
NVDIMM_INTEL_FW_ACTIVATE_ARM] = 2,
|
||||
},
|
||||
};
|
||||
u8 id;
|
||||
|
|
@ -406,7 +402,7 @@ static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func)
|
|||
}
|
||||
|
||||
static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
|
||||
struct nd_cmd_pkg *call_pkg)
|
||||
struct nd_cmd_pkg *call_pkg, int *family)
|
||||
{
|
||||
if (call_pkg) {
|
||||
int i;
|
||||
|
|
@ -417,6 +413,7 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
|
|||
for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
|
||||
if (call_pkg->nd_reserved2[i])
|
||||
return -EINVAL;
|
||||
*family = call_pkg->nd_family;
|
||||
return call_pkg->nd_command;
|
||||
}
|
||||
|
||||
|
|
@ -450,13 +447,14 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|||
acpi_handle handle;
|
||||
const guid_t *guid;
|
||||
int func, rc, i;
|
||||
int family = 0;
|
||||
|
||||
if (cmd_rc)
|
||||
*cmd_rc = -EINVAL;
|
||||
|
||||
if (cmd == ND_CMD_CALL)
|
||||
call_pkg = buf;
|
||||
func = cmd_to_func(nfit_mem, cmd, call_pkg);
|
||||
func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
|
||||
if (func < 0)
|
||||
return func;
|
||||
|
||||
|
|
@ -478,9 +476,17 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|||
|
||||
cmd_name = nvdimm_bus_cmd_name(cmd);
|
||||
cmd_mask = nd_desc->cmd_mask;
|
||||
dsm_mask = nd_desc->bus_dsm_mask;
|
||||
if (cmd == ND_CMD_CALL && call_pkg->nd_family) {
|
||||
family = call_pkg->nd_family;
|
||||
if (!test_bit(family, &nd_desc->bus_family_mask))
|
||||
return -EINVAL;
|
||||
dsm_mask = acpi_desc->family_dsm_mask[family];
|
||||
guid = to_nfit_bus_uuid(family);
|
||||
} else {
|
||||
dsm_mask = acpi_desc->bus_dsm_mask;
|
||||
guid = to_nfit_uuid(NFIT_DEV_BUS);
|
||||
}
|
||||
desc = nd_cmd_bus_desc(cmd);
|
||||
guid = to_nfit_uuid(NFIT_DEV_BUS);
|
||||
handle = adev->handle;
|
||||
dimm_name = "bus";
|
||||
}
|
||||
|
|
@ -516,8 +522,8 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|||
in_buf.buffer.length = call_pkg->nd_size_in;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n",
|
||||
dimm_name, cmd, func, in_buf.buffer.length);
|
||||
dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n",
|
||||
dimm_name, cmd, family, func, in_buf.buffer.length);
|
||||
if (payload_dumpable(nvdimm, func))
|
||||
print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
|
||||
in_buf.buffer.pointer,
|
||||
|
|
@ -1238,8 +1244,9 @@ static ssize_t bus_dsm_mask_show(struct device *dev,
|
|||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
|
||||
return sprintf(buf, "%#lx\n", nd_desc->bus_dsm_mask);
|
||||
return sprintf(buf, "%#lx\n", acpi_desc->bus_dsm_mask);
|
||||
}
|
||||
static struct device_attribute dev_attr_bus_dsm_mask =
|
||||
__ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL);
|
||||
|
|
@ -1385,8 +1392,12 @@ static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n)
|
|||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
|
||||
if (a == &dev_attr_scrub.attr && !ars_supported(nvdimm_bus))
|
||||
return 0;
|
||||
if (a == &dev_attr_scrub.attr)
|
||||
return ars_supported(nvdimm_bus) ? a->mode : 0;
|
||||
|
||||
if (a == &dev_attr_firmware_activate_noidle.attr)
|
||||
return intel_fwa_supported(nvdimm_bus) ? a->mode : 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
|
|
@ -1395,6 +1406,7 @@ static struct attribute *acpi_nfit_attributes[] = {
|
|||
&dev_attr_scrub.attr,
|
||||
&dev_attr_hw_error_scrub.attr,
|
||||
&dev_attr_bus_dsm_mask.attr,
|
||||
&dev_attr_firmware_activate_noidle.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -1823,6 +1835,7 @@ static void populate_shutdown_status(struct nfit_mem *nfit_mem)
|
|||
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
||||
struct nfit_mem *nfit_mem, u32 device_handle)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
|
||||
struct acpi_device *adev, *adev_dimm;
|
||||
struct device *dev = acpi_desc->dev;
|
||||
unsigned long dsm_mask, label_mask;
|
||||
|
|
@ -1834,6 +1847,7 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|||
/* nfit test assumes 1:1 relationship between commands and dsms */
|
||||
nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
|
||||
nfit_mem->family = NVDIMM_FAMILY_INTEL;
|
||||
set_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
|
||||
|
||||
if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID)
|
||||
sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x",
|
||||
|
|
@ -1886,10 +1900,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|||
* Note, that checking for function0 (bit0) tells us if any commands
|
||||
* are reachable through this GUID.
|
||||
*/
|
||||
clear_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
|
||||
for (i = 0; i <= NVDIMM_FAMILY_MAX; i++)
|
||||
if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
|
||||
if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) {
|
||||
set_bit(i, &nd_desc->dimm_family_mask);
|
||||
if (family < 0 || i == default_dsm_family)
|
||||
family = i;
|
||||
}
|
||||
|
||||
/* limit the supported commands to those that are publicly documented */
|
||||
nfit_mem->family = family;
|
||||
|
|
@ -2007,6 +2024,26 @@ static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family)
|
|||
}
|
||||
}
|
||||
|
||||
static const struct nvdimm_fw_ops *acpi_nfit_get_fw_ops(
|
||||
struct nfit_mem *nfit_mem)
|
||||
{
|
||||
unsigned long mask;
|
||||
struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
|
||||
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
|
||||
|
||||
if (!nd_desc->fw_ops)
|
||||
return NULL;
|
||||
|
||||
if (nfit_mem->family != NVDIMM_FAMILY_INTEL)
|
||||
return NULL;
|
||||
|
||||
mask = nfit_mem->dsm_mask & NVDIMM_INTEL_FW_ACTIVATE_CMDMASK;
|
||||
if (mask != NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
|
||||
return NULL;
|
||||
|
||||
return intel_fw_ops;
|
||||
}
|
||||
|
||||
static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
|
||||
{
|
||||
struct nfit_mem *nfit_mem;
|
||||
|
|
@ -2083,7 +2120,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
|
|||
acpi_nfit_dimm_attribute_groups,
|
||||
flags, cmd_mask, flush ? flush->hint_count : 0,
|
||||
nfit_mem->flush_wpq, &nfit_mem->id[0],
|
||||
acpi_nfit_get_security_ops(nfit_mem->family));
|
||||
acpi_nfit_get_security_ops(nfit_mem->family),
|
||||
acpi_nfit_get_fw_ops(nfit_mem));
|
||||
if (!nvdimm)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -2147,12 +2185,23 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
|
|||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
|
||||
const guid_t *guid = to_nfit_uuid(NFIT_DEV_BUS);
|
||||
unsigned long dsm_mask, *mask;
|
||||
struct acpi_device *adev;
|
||||
unsigned long dsm_mask;
|
||||
int i;
|
||||
|
||||
nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
|
||||
nd_desc->bus_dsm_mask = acpi_desc->bus_nfit_cmd_force_en;
|
||||
set_bit(ND_CMD_CALL, &nd_desc->cmd_mask);
|
||||
set_bit(NVDIMM_BUS_FAMILY_NFIT, &nd_desc->bus_family_mask);
|
||||
|
||||
/* enable nfit_test to inject bus command emulation */
|
||||
if (acpi_desc->bus_cmd_force_en) {
|
||||
nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
|
||||
mask = &nd_desc->bus_family_mask;
|
||||
if (acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]) {
|
||||
set_bit(NVDIMM_BUS_FAMILY_INTEL, mask);
|
||||
nd_desc->fw_ops = intel_bus_fw_ops;
|
||||
}
|
||||
}
|
||||
|
||||
adev = to_acpi_dev(acpi_desc);
|
||||
if (!adev)
|
||||
return;
|
||||
|
|
@ -2160,7 +2209,6 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
|
|||
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
|
||||
if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
|
||||
set_bit(i, &nd_desc->cmd_mask);
|
||||
set_bit(ND_CMD_CALL, &nd_desc->cmd_mask);
|
||||
|
||||
dsm_mask =
|
||||
(1 << ND_CMD_ARS_CAP) |
|
||||
|
|
@ -2173,7 +2221,20 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
|
|||
(1 << NFIT_CMD_ARS_INJECT_GET);
|
||||
for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
|
||||
if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
|
||||
set_bit(i, &nd_desc->bus_dsm_mask);
|
||||
set_bit(i, &acpi_desc->bus_dsm_mask);
|
||||
|
||||
/* Enumerate allowed NVDIMM_BUS_FAMILY_INTEL commands */
|
||||
dsm_mask = NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
|
||||
guid = to_nfit_bus_uuid(NVDIMM_BUS_FAMILY_INTEL);
|
||||
mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
|
||||
for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
|
||||
if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
|
||||
set_bit(i, mask);
|
||||
|
||||
if (*mask == dsm_mask) {
|
||||
set_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask);
|
||||
nd_desc->fw_ops = intel_bus_fw_ops;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t range_index_show(struct device *dev,
|
||||
|
|
@ -3273,7 +3334,7 @@ static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc,
|
|||
static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
|
||||
{
|
||||
struct nfit_spa *nfit_spa;
|
||||
int rc;
|
||||
int rc, do_sched_ars = 0;
|
||||
|
||||
set_bit(ARS_VALID, &acpi_desc->scrub_flags);
|
||||
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
|
||||
|
|
@ -3285,7 +3346,7 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
|
|||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
|
||||
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
|
||||
switch (nfit_spa_type(nfit_spa->spa)) {
|
||||
case NFIT_SPA_VOLATILE:
|
||||
case NFIT_SPA_PM:
|
||||
|
|
@ -3293,6 +3354,13 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
|
|||
rc = ars_register(acpi_desc, nfit_spa);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Kick off background ARS if at least one
|
||||
* region successfully registered ARS
|
||||
*/
|
||||
if (!test_bit(ARS_FAILED, &nfit_spa->ars_state))
|
||||
do_sched_ars++;
|
||||
break;
|
||||
case NFIT_SPA_BDW:
|
||||
/* nothing to register */
|
||||
|
|
@ -3311,8 +3379,10 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
|
|||
/* don't register unknown regions */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sched_ars(acpi_desc);
|
||||
if (do_sched_ars)
|
||||
sched_ars(acpi_desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3485,7 +3555,10 @@ static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* prevent security commands from being issued via ioctl */
|
||||
/*
|
||||
* Prevent security and firmware activate commands from being issued via
|
||||
* ioctl.
|
||||
*/
|
||||
static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
|
||||
struct nvdimm *nvdimm, unsigned int cmd, void *buf)
|
||||
{
|
||||
|
|
@ -3496,10 +3569,15 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
|
|||
call_pkg->nd_family == NVDIMM_FAMILY_INTEL) {
|
||||
func = call_pkg->nd_command;
|
||||
if (func > NVDIMM_CMD_MAX ||
|
||||
(1 << func) & NVDIMM_INTEL_SECURITY_CMDMASK)
|
||||
(1 << func) & NVDIMM_INTEL_DENY_CMDMASK)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* block all non-nfit bus commands */
|
||||
if (!nvdimm && cmd == ND_CMD_CALL &&
|
||||
call_pkg->nd_family != NVDIMM_BUS_FAMILY_NFIT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd);
|
||||
}
|
||||
|
||||
|
|
@ -3791,6 +3869,7 @@ static __init int nfit_init(void)
|
|||
guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
|
||||
guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]);
|
||||
guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]);
|
||||
guid_parse(UUID_INTEL_BUS, &nfit_uuid[NFIT_BUS_INTEL]);
|
||||
|
||||
nfit_wq = create_singlethread_workqueue("nfit");
|
||||
if (!nfit_wq)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,48 @@
|
|||
#include "intel.h"
|
||||
#include "nfit.h"
|
||||
|
||||
static ssize_t firmware_activate_noidle_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
|
||||
return sprintf(buf, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N");
|
||||
}
|
||||
|
||||
static ssize_t firmware_activate_noidle_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
ssize_t rc;
|
||||
bool val;
|
||||
|
||||
rc = kstrtobool(buf, &val);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (val != acpi_desc->fwa_noidle)
|
||||
acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID;
|
||||
acpi_desc->fwa_noidle = val;
|
||||
return size;
|
||||
}
|
||||
DEVICE_ATTR_RW(firmware_activate_noidle);
|
||||
|
||||
bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
unsigned long *mask;
|
||||
|
||||
if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask))
|
||||
return false;
|
||||
|
||||
mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
|
||||
return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
|
||||
}
|
||||
|
||||
static unsigned long intel_security_flags(struct nvdimm *nvdimm,
|
||||
enum nvdimm_passphrase_type ptype)
|
||||
{
|
||||
|
|
@ -389,3 +431,347 @@ static const struct nvdimm_security_ops __intel_security_ops = {
|
|||
};
|
||||
|
||||
const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;
|
||||
|
||||
static int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc,
|
||||
struct nd_intel_bus_fw_activate_businfo *info)
|
||||
{
|
||||
struct {
|
||||
struct nd_cmd_pkg pkg;
|
||||
struct nd_intel_bus_fw_activate_businfo cmd;
|
||||
} nd_cmd = {
|
||||
.pkg = {
|
||||
.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
|
||||
.nd_family = NVDIMM_BUS_FAMILY_INTEL,
|
||||
.nd_size_out =
|
||||
sizeof(struct nd_intel_bus_fw_activate_businfo),
|
||||
.nd_fw_size =
|
||||
sizeof(struct nd_intel_bus_fw_activate_businfo),
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
|
||||
NULL);
|
||||
*info = nd_cmd.cmd;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The fw_ops expect to be called with the nvdimm_bus_lock() held */
|
||||
static enum nvdimm_fwa_state intel_bus_fwa_state(
|
||||
struct nvdimm_bus_descriptor *nd_desc)
|
||||
{
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
struct nd_intel_bus_fw_activate_businfo info;
|
||||
struct device *dev = acpi_desc->dev;
|
||||
enum nvdimm_fwa_state state;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* It should not be possible for platform firmware to return
|
||||
* busy because activate is a synchronous operation. Treat it
|
||||
* similar to invalid, i.e. always refresh / poll the status.
|
||||
*/
|
||||
switch (acpi_desc->fwa_state) {
|
||||
case NVDIMM_FWA_INVALID:
|
||||
case NVDIMM_FWA_BUSY:
|
||||
break;
|
||||
default:
|
||||
/* check if capability needs to be refreshed */
|
||||
if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID)
|
||||
break;
|
||||
return acpi_desc->fwa_state;
|
||||
}
|
||||
|
||||
/* Refresh with platform firmware */
|
||||
rc = intel_bus_fwa_businfo(nd_desc, &info);
|
||||
if (rc)
|
||||
return NVDIMM_FWA_INVALID;
|
||||
|
||||
switch (info.state) {
|
||||
case ND_INTEL_FWA_IDLE:
|
||||
state = NVDIMM_FWA_IDLE;
|
||||
break;
|
||||
case ND_INTEL_FWA_BUSY:
|
||||
state = NVDIMM_FWA_BUSY;
|
||||
break;
|
||||
case ND_INTEL_FWA_ARMED:
|
||||
if (info.activate_tmo > info.max_quiesce_tmo)
|
||||
state = NVDIMM_FWA_ARM_OVERFLOW;
|
||||
else
|
||||
state = NVDIMM_FWA_ARMED;
|
||||
break;
|
||||
default:
|
||||
dev_err_once(dev, "invalid firmware activate state %d\n",
|
||||
info.state);
|
||||
return NVDIMM_FWA_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Capability data is available in the same payload as state. It
|
||||
* is expected to be static.
|
||||
*/
|
||||
if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) {
|
||||
if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE)
|
||||
acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE;
|
||||
else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) {
|
||||
/*
|
||||
* Skip hibernate cycle by default if platform
|
||||
* indicates that it does not need devices to be
|
||||
* quiesced.
|
||||
*/
|
||||
acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE;
|
||||
} else
|
||||
acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE;
|
||||
}
|
||||
|
||||
acpi_desc->fwa_state = state;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static enum nvdimm_fwa_capability intel_bus_fwa_capability(
|
||||
struct nvdimm_bus_descriptor *nd_desc)
|
||||
{
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
|
||||
if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID)
|
||||
return acpi_desc->fwa_cap;
|
||||
|
||||
if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID)
|
||||
return acpi_desc->fwa_cap;
|
||||
|
||||
return NVDIMM_FWA_CAP_INVALID;
|
||||
}
|
||||
|
||||
static int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc)
|
||||
{
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
struct {
|
||||
struct nd_cmd_pkg pkg;
|
||||
struct nd_intel_bus_fw_activate cmd;
|
||||
} nd_cmd = {
|
||||
.pkg = {
|
||||
.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,
|
||||
.nd_family = NVDIMM_BUS_FAMILY_INTEL,
|
||||
.nd_size_in = sizeof(nd_cmd.cmd.iodev_state),
|
||||
.nd_size_out =
|
||||
sizeof(struct nd_intel_bus_fw_activate),
|
||||
.nd_fw_size =
|
||||
sizeof(struct nd_intel_bus_fw_activate),
|
||||
},
|
||||
/*
|
||||
* Even though activate is run from a suspended context,
|
||||
* for safety, still ask platform firmware to force
|
||||
* quiesce devices by default. Let a module
|
||||
* parameter override that policy.
|
||||
*/
|
||||
.cmd = {
|
||||
.iodev_state = acpi_desc->fwa_noidle
|
||||
? ND_INTEL_BUS_FWA_IODEV_OS_IDLE
|
||||
: ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
|
||||
switch (intel_bus_fwa_state(nd_desc)) {
|
||||
case NVDIMM_FWA_ARMED:
|
||||
case NVDIMM_FWA_ARM_OVERFLOW:
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* Whether the command succeeded, or failed, the agent checking
|
||||
* for the result needs to query the DIMMs individually.
|
||||
* Increment the activation count to invalidate all the DIMM
|
||||
* states at once (it's otherwise not possible to take
|
||||
* acpi_desc->init_mutex in this context)
|
||||
*/
|
||||
acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
|
||||
acpi_desc->fwa_count++;
|
||||
|
||||
dev_dbg(acpi_desc->dev, "result: %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = {
|
||||
.activate_state = intel_bus_fwa_state,
|
||||
.capability = intel_bus_fwa_capability,
|
||||
.activate = intel_bus_fwa_activate,
|
||||
};
|
||||
|
||||
const struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops;
|
||||
|
||||
static int intel_fwa_dimminfo(struct nvdimm *nvdimm,
|
||||
struct nd_intel_fw_activate_dimminfo *info)
|
||||
{
|
||||
struct {
|
||||
struct nd_cmd_pkg pkg;
|
||||
struct nd_intel_fw_activate_dimminfo cmd;
|
||||
} nd_cmd = {
|
||||
.pkg = {
|
||||
.nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,
|
||||
.nd_family = NVDIMM_FAMILY_INTEL,
|
||||
.nd_size_out =
|
||||
sizeof(struct nd_intel_fw_activate_dimminfo),
|
||||
.nd_fw_size =
|
||||
sizeof(struct nd_intel_fw_activate_dimminfo),
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
|
||||
*info = nd_cmd.cmd;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm)
|
||||
{
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
|
||||
struct nd_intel_fw_activate_dimminfo info;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Similar to the bus state, since activate is synchronous the
|
||||
* busy state should resolve within the context of 'activate'.
|
||||
*/
|
||||
switch (nfit_mem->fwa_state) {
|
||||
case NVDIMM_FWA_INVALID:
|
||||
case NVDIMM_FWA_BUSY:
|
||||
break;
|
||||
default:
|
||||
/* If no activations occurred the old state is still valid */
|
||||
if (nfit_mem->fwa_count == acpi_desc->fwa_count)
|
||||
return nfit_mem->fwa_state;
|
||||
}
|
||||
|
||||
rc = intel_fwa_dimminfo(nvdimm, &info);
|
||||
if (rc)
|
||||
return NVDIMM_FWA_INVALID;
|
||||
|
||||
switch (info.state) {
|
||||
case ND_INTEL_FWA_IDLE:
|
||||
nfit_mem->fwa_state = NVDIMM_FWA_IDLE;
|
||||
break;
|
||||
case ND_INTEL_FWA_BUSY:
|
||||
nfit_mem->fwa_state = NVDIMM_FWA_BUSY;
|
||||
break;
|
||||
case ND_INTEL_FWA_ARMED:
|
||||
nfit_mem->fwa_state = NVDIMM_FWA_ARMED;
|
||||
break;
|
||||
default:
|
||||
nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (info.result) {
|
||||
case ND_INTEL_DIMM_FWA_NONE:
|
||||
nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE;
|
||||
break;
|
||||
case ND_INTEL_DIMM_FWA_SUCCESS:
|
||||
nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS;
|
||||
break;
|
||||
case ND_INTEL_DIMM_FWA_NOTSTAGED:
|
||||
nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED;
|
||||
break;
|
||||
case ND_INTEL_DIMM_FWA_NEEDRESET:
|
||||
nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET;
|
||||
break;
|
||||
case ND_INTEL_DIMM_FWA_MEDIAFAILED:
|
||||
case ND_INTEL_DIMM_FWA_ABORT:
|
||||
case ND_INTEL_DIMM_FWA_NOTSUPP:
|
||||
case ND_INTEL_DIMM_FWA_ERROR:
|
||||
default:
|
||||
nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
nfit_mem->fwa_count = acpi_desc->fwa_count;
|
||||
|
||||
return nfit_mem->fwa_state;
|
||||
}
|
||||
|
||||
static enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm)
|
||||
{
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
|
||||
|
||||
if (nfit_mem->fwa_count == acpi_desc->fwa_count
|
||||
&& nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID)
|
||||
return nfit_mem->fwa_result;
|
||||
|
||||
if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID)
|
||||
return nfit_mem->fwa_result;
|
||||
|
||||
return NVDIMM_FWA_RESULT_INVALID;
|
||||
}
|
||||
|
||||
static int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm)
|
||||
{
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
|
||||
struct {
|
||||
struct nd_cmd_pkg pkg;
|
||||
struct nd_intel_fw_activate_arm cmd;
|
||||
} nd_cmd = {
|
||||
.pkg = {
|
||||
.nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,
|
||||
.nd_family = NVDIMM_FAMILY_INTEL,
|
||||
.nd_size_in = sizeof(nd_cmd.cmd.activate_arm),
|
||||
.nd_size_out =
|
||||
sizeof(struct nd_intel_fw_activate_arm),
|
||||
.nd_fw_size =
|
||||
sizeof(struct nd_intel_fw_activate_arm),
|
||||
},
|
||||
.cmd = {
|
||||
.activate_arm = arm == NVDIMM_FWA_ARM
|
||||
? ND_INTEL_DIMM_FWA_ARM
|
||||
: ND_INTEL_DIMM_FWA_DISARM,
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
|
||||
switch (intel_fwa_state(nvdimm)) {
|
||||
case NVDIMM_FWA_INVALID:
|
||||
return -ENXIO;
|
||||
case NVDIMM_FWA_BUSY:
|
||||
return -EBUSY;
|
||||
case NVDIMM_FWA_IDLE:
|
||||
if (arm == NVDIMM_FWA_DISARM)
|
||||
return 0;
|
||||
break;
|
||||
case NVDIMM_FWA_ARMED:
|
||||
if (arm == NVDIMM_FWA_ARM)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate the bus-level state, now that we're committed to
|
||||
* changing the 'arm' state.
|
||||
*/
|
||||
acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
|
||||
nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
|
||||
|
||||
rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
|
||||
|
||||
dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM
|
||||
? "arm" : "disarm", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct nvdimm_fw_ops __intel_fw_ops = {
|
||||
.activate_state = intel_fwa_state,
|
||||
.activate_result = intel_fwa_result,
|
||||
.arm = intel_fwa_arm,
|
||||
};
|
||||
|
||||
const struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops;
|
||||
|
|
|
|||
|
|
@ -111,4 +111,65 @@ struct nd_intel_master_secure_erase {
|
|||
u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
|
||||
u32 status;
|
||||
} __packed;
|
||||
|
||||
#define ND_INTEL_FWA_IDLE 0
|
||||
#define ND_INTEL_FWA_ARMED 1
|
||||
#define ND_INTEL_FWA_BUSY 2
|
||||
|
||||
#define ND_INTEL_DIMM_FWA_NONE 0
|
||||
#define ND_INTEL_DIMM_FWA_NOTSTAGED 1
|
||||
#define ND_INTEL_DIMM_FWA_SUCCESS 2
|
||||
#define ND_INTEL_DIMM_FWA_NEEDRESET 3
|
||||
#define ND_INTEL_DIMM_FWA_MEDIAFAILED 4
|
||||
#define ND_INTEL_DIMM_FWA_ABORT 5
|
||||
#define ND_INTEL_DIMM_FWA_NOTSUPP 6
|
||||
#define ND_INTEL_DIMM_FWA_ERROR 7
|
||||
|
||||
struct nd_intel_fw_activate_dimminfo {
|
||||
u32 status;
|
||||
u16 result;
|
||||
u8 state;
|
||||
u8 reserved[7];
|
||||
} __packed;
|
||||
|
||||
#define ND_INTEL_DIMM_FWA_ARM 1
|
||||
#define ND_INTEL_DIMM_FWA_DISARM 0
|
||||
|
||||
struct nd_intel_fw_activate_arm {
|
||||
u8 activate_arm;
|
||||
u32 status;
|
||||
} __packed;
|
||||
|
||||
/* Root device command payloads */
|
||||
#define ND_INTEL_BUS_FWA_CAP_FWQUIESCE (1 << 0)
|
||||
#define ND_INTEL_BUS_FWA_CAP_OSQUIESCE (1 << 1)
|
||||
#define ND_INTEL_BUS_FWA_CAP_RESET (1 << 2)
|
||||
|
||||
struct nd_intel_bus_fw_activate_businfo {
|
||||
u32 status;
|
||||
u16 reserved;
|
||||
u8 state;
|
||||
u8 capability;
|
||||
u64 activate_tmo;
|
||||
u64 cpu_quiesce_tmo;
|
||||
u64 io_quiesce_tmo;
|
||||
u64 max_quiesce_tmo;
|
||||
} __packed;
|
||||
|
||||
#define ND_INTEL_BUS_FWA_STATUS_NOARM (6 | 1 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_BUSY (6 | 2 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_NOFW (6 | 3 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_TMO (6 | 4 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_NOIDLE (6 | 5 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_ABORT (6 | 6 << 16)
|
||||
|
||||
#define ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE (0)
|
||||
#define ND_INTEL_BUS_FWA_IODEV_OS_IDLE (1)
|
||||
struct nd_intel_bus_fw_activate {
|
||||
u8 iodev_state;
|
||||
u32 status;
|
||||
} __packed;
|
||||
|
||||
extern const struct nvdimm_fw_ops *intel_fw_ops;
|
||||
extern const struct nvdimm_bus_fw_ops *intel_bus_fw_ops;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
/* https://pmem.io/documents/NVDIMM_DSM_Interface-V1.6.pdf */
|
||||
#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
|
||||
#define UUID_INTEL_BUS "c7d8acd4-2df8-4b82-9f65-a325335af149"
|
||||
|
||||
/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */
|
||||
#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6"
|
||||
|
|
@ -33,7 +34,6 @@
|
|||
| ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
|
||||
| ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED)
|
||||
|
||||
#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_HYPERV
|
||||
#define NVDIMM_CMD_MAX 31
|
||||
|
||||
#define NVDIMM_STANDARD_CMDMASK \
|
||||
|
|
@ -66,6 +66,13 @@ enum nvdimm_family_cmds {
|
|||
NVDIMM_INTEL_QUERY_OVERWRITE = 26,
|
||||
NVDIMM_INTEL_SET_MASTER_PASSPHRASE = 27,
|
||||
NVDIMM_INTEL_MASTER_SECURE_ERASE = 28,
|
||||
NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO = 29,
|
||||
NVDIMM_INTEL_FW_ACTIVATE_ARM = 30,
|
||||
};
|
||||
|
||||
enum nvdimm_bus_family_cmds {
|
||||
NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO = 1,
|
||||
NVDIMM_BUS_INTEL_FW_ACTIVATE = 2,
|
||||
};
|
||||
|
||||
#define NVDIMM_INTEL_SECURITY_CMDMASK \
|
||||
|
|
@ -76,13 +83,22 @@ enum nvdimm_family_cmds {
|
|||
| 1 << NVDIMM_INTEL_SET_MASTER_PASSPHRASE \
|
||||
| 1 << NVDIMM_INTEL_MASTER_SECURE_ERASE)
|
||||
|
||||
#define NVDIMM_INTEL_FW_ACTIVATE_CMDMASK \
|
||||
(1 << NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO | 1 << NVDIMM_INTEL_FW_ACTIVATE_ARM)
|
||||
|
||||
#define NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK \
|
||||
(1 << NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO | 1 << NVDIMM_BUS_INTEL_FW_ACTIVATE)
|
||||
|
||||
#define NVDIMM_INTEL_CMDMASK \
|
||||
(NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \
|
||||
| 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \
|
||||
| 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \
|
||||
| 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \
|
||||
| 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN \
|
||||
| NVDIMM_INTEL_SECURITY_CMDMASK)
|
||||
| NVDIMM_INTEL_SECURITY_CMDMASK | NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
|
||||
|
||||
#define NVDIMM_INTEL_DENY_CMDMASK \
|
||||
(NVDIMM_INTEL_SECURITY_CMDMASK | NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
|
||||
|
||||
enum nfit_uuids {
|
||||
/* for simplicity alias the uuid index with the family id */
|
||||
|
|
@ -91,6 +107,11 @@ enum nfit_uuids {
|
|||
NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
|
||||
NFIT_DEV_DIMM_N_MSFT = NVDIMM_FAMILY_MSFT,
|
||||
NFIT_DEV_DIMM_N_HYPERV = NVDIMM_FAMILY_HYPERV,
|
||||
/*
|
||||
* to_nfit_bus_uuid() expects to translate bus uuid family ids
|
||||
* to a UUID index using NVDIMM_FAMILY_MAX as an offset
|
||||
*/
|
||||
NFIT_BUS_INTEL = NVDIMM_FAMILY_MAX + NVDIMM_BUS_FAMILY_INTEL,
|
||||
NFIT_SPA_VOLATILE,
|
||||
NFIT_SPA_PM,
|
||||
NFIT_SPA_DCR,
|
||||
|
|
@ -199,6 +220,9 @@ struct nfit_mem {
|
|||
struct list_head list;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
enum nvdimm_fwa_state fwa_state;
|
||||
enum nvdimm_fwa_result fwa_result;
|
||||
int fwa_count;
|
||||
char id[NFIT_DIMM_ID_LEN+1];
|
||||
struct resource *flush_wpq;
|
||||
unsigned long dsm_mask;
|
||||
|
|
@ -238,11 +262,17 @@ struct acpi_nfit_desc {
|
|||
unsigned long scrub_flags;
|
||||
unsigned long dimm_cmd_force_en;
|
||||
unsigned long bus_cmd_force_en;
|
||||
unsigned long bus_nfit_cmd_force_en;
|
||||
unsigned long bus_dsm_mask;
|
||||
unsigned long family_dsm_mask[NVDIMM_BUS_FAMILY_MAX + 1];
|
||||
unsigned int platform_cap;
|
||||
unsigned int scrub_tmo;
|
||||
int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
|
||||
void *iobuf, u64 len, int rw);
|
||||
enum nvdimm_fwa_state fwa_state;
|
||||
enum nvdimm_fwa_capability fwa_cap;
|
||||
int fwa_count;
|
||||
bool fwa_noidle;
|
||||
bool fwa_nosuspend;
|
||||
};
|
||||
|
||||
enum scrub_mode {
|
||||
|
|
@ -345,4 +375,6 @@ void __acpi_nvdimm_notify(struct device *dev, u32 event);
|
|||
int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
||||
unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc);
|
||||
void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev);
|
||||
bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus);
|
||||
extern struct device_attribute dev_attr_firmware_activate_noidle;
|
||||
#endif /* __NFIT_H__ */
|
||||
|
|
|
|||
|
|
@ -204,8 +204,8 @@ static int virtcrypto_update_status(struct virtio_crypto *vcrypto)
|
|||
u32 status;
|
||||
int err;
|
||||
|
||||
virtio_cread(vcrypto->vdev,
|
||||
struct virtio_crypto_config, status, &status);
|
||||
virtio_cread_le(vcrypto->vdev,
|
||||
struct virtio_crypto_config, status, &status);
|
||||
|
||||
/*
|
||||
* Unknown status bits would be a host error and the driver
|
||||
|
|
@ -323,31 +323,31 @@ static int virtcrypto_probe(struct virtio_device *vdev)
|
|||
if (!vcrypto)
|
||||
return -ENOMEM;
|
||||
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
max_dataqueues, &max_data_queues);
|
||||
if (max_data_queues < 1)
|
||||
max_data_queues = 1;
|
||||
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
max_cipher_key_len, &max_cipher_key_len);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
max_auth_key_len, &max_auth_key_len);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
max_size, &max_size);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
crypto_services, &crypto_services);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
cipher_algo_l, &cipher_algo_l);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
cipher_algo_h, &cipher_algo_h);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
hash_algo, &hash_algo);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
mac_algo_l, &mac_algo_l);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
mac_algo_h, &mac_algo_h);
|
||||
virtio_cread(vdev, struct virtio_crypto_config,
|
||||
aead_algo, &aead_algo);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
max_cipher_key_len, &max_cipher_key_len);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
max_auth_key_len, &max_auth_key_len);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
max_size, &max_size);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
crypto_services, &crypto_services);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
cipher_algo_l, &cipher_algo_l);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
cipher_algo_h, &cipher_algo_h);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
hash_algo, &hash_algo);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
mac_algo_l, &mac_algo_l);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
mac_algo_h, &mac_algo_h);
|
||||
virtio_cread_le(vdev, struct virtio_crypto_config,
|
||||
aead_algo, &aead_algo);
|
||||
|
||||
/* Add virtio crypto device to global table */
|
||||
err = virtcrypto_devmgr_add_dev(vcrypto);
|
||||
|
|
|
|||
|
|
@ -80,14 +80,14 @@ bool __generic_fsdax_supported(struct dax_device *dax_dev,
|
|||
int err, id;
|
||||
|
||||
if (blocksize != PAGE_SIZE) {
|
||||
pr_debug("%s: error: unsupported blocksize for dax\n",
|
||||
pr_info("%s: error: unsupported blocksize for dax\n",
|
||||
bdevname(bdev, buf));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = bdev_dax_pgoff(bdev, start, PAGE_SIZE, &pgoff);
|
||||
if (err) {
|
||||
pr_debug("%s: error: unaligned partition for dax\n",
|
||||
pr_info("%s: error: unaligned partition for dax\n",
|
||||
bdevname(bdev, buf));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ bool __generic_fsdax_supported(struct dax_device *dax_dev,
|
|||
last_page = PFN_DOWN((start + sectors - 1) * 512) * PAGE_SIZE / 512;
|
||||
err = bdev_dax_pgoff(bdev, last_page, PAGE_SIZE, &pgoff_end);
|
||||
if (err) {
|
||||
pr_debug("%s: error: unaligned partition for dax\n",
|
||||
pr_info("%s: error: unaligned partition for dax\n",
|
||||
bdevname(bdev, buf));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -103,11 +103,11 @@ bool __generic_fsdax_supported(struct dax_device *dax_dev,
|
|||
id = dax_read_lock();
|
||||
len = dax_direct_access(dax_dev, pgoff, 1, &kaddr, &pfn);
|
||||
len2 = dax_direct_access(dax_dev, pgoff_end, 1, &end_kaddr, &end_pfn);
|
||||
dax_read_unlock(id);
|
||||
|
||||
if (len < 1 || len2 < 1) {
|
||||
pr_debug("%s: error: dax access failed (%ld)\n",
|
||||
pr_info("%s: error: dax access failed (%ld)\n",
|
||||
bdevname(bdev, buf), len < 1 ? len : len2);
|
||||
dax_read_unlock(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -137,9 +137,10 @@ bool __generic_fsdax_supported(struct dax_device *dax_dev,
|
|||
put_dev_pagemap(end_pgmap);
|
||||
|
||||
}
|
||||
dax_read_unlock(id);
|
||||
|
||||
if (!dax_enabled) {
|
||||
pr_debug("%s: error: dax support not enabled\n",
|
||||
pr_info("%s: error: dax support not enabled\n",
|
||||
bdevname(bdev, buf));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iommu.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
|
|
@ -118,6 +119,9 @@ struct drm_i915_private *mock_gem_device(void)
|
|||
{
|
||||
struct drm_i915_private *i915;
|
||||
struct pci_dev *pdev;
|
||||
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
|
||||
struct dev_iommu iommu;
|
||||
#endif
|
||||
int err;
|
||||
|
||||
pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
|
||||
|
|
@ -136,8 +140,10 @@ struct drm_i915_private *mock_gem_device(void)
|
|||
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
|
||||
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
|
||||
/* hack to disable iommu for the fake device; force identity mapping */
|
||||
pdev->dev.archdata.iommu = (void *)-1;
|
||||
/* HACK HACK HACK to disable iommu for the fake device; force identity mapping */
|
||||
memset(&iommu, 0, sizeof(iommu));
|
||||
iommu.priv = (void *)-1;
|
||||
pdev->dev.iommu = &iommu;
|
||||
#endif
|
||||
|
||||
pci_set_drvdata(pdev, i915);
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
|
|||
while (len) {
|
||||
size_t pgsize = get_pgsize(iova | paddr, len);
|
||||
|
||||
ops->map(ops, iova, paddr, pgsize, prot);
|
||||
ops->map(ops, iova, paddr, pgsize, prot, GFP_KERNEL);
|
||||
iova += pgsize;
|
||||
paddr += pgsize;
|
||||
len -= pgsize;
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
|
|||
u32 events_read, events_clear = 0;
|
||||
|
||||
/* read the config space */
|
||||
virtio_cread(vgdev->vdev, struct virtio_gpu_config,
|
||||
events_read, &events_read);
|
||||
virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
|
||||
events_read, &events_read);
|
||||
if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
|
||||
if (vgdev->has_edid)
|
||||
virtio_gpu_cmd_get_edids(vgdev);
|
||||
|
|
@ -49,8 +49,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
|
|||
drm_helper_hpd_irq_event(vgdev->ddev);
|
||||
events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
|
||||
}
|
||||
virtio_cwrite(vgdev->vdev, struct virtio_gpu_config,
|
||||
events_clear, &events_clear);
|
||||
virtio_cwrite_le(vgdev->vdev, struct virtio_gpu_config,
|
||||
events_clear, &events_clear);
|
||||
}
|
||||
|
||||
static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
|
||||
|
|
@ -165,8 +165,8 @@ int virtio_gpu_init(struct drm_device *dev)
|
|||
}
|
||||
|
||||
/* get display info */
|
||||
virtio_cread(vgdev->vdev, struct virtio_gpu_config,
|
||||
num_scanouts, &num_scanouts);
|
||||
virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
|
||||
num_scanouts, &num_scanouts);
|
||||
vgdev->num_scanouts = min_t(uint32_t, num_scanouts,
|
||||
VIRTIO_GPU_MAX_SCANOUTS);
|
||||
if (!vgdev->num_scanouts) {
|
||||
|
|
@ -176,8 +176,8 @@ int virtio_gpu_init(struct drm_device *dev)
|
|||
}
|
||||
DRM_INFO("number of scanouts: %d\n", num_scanouts);
|
||||
|
||||
virtio_cread(vgdev->vdev, struct virtio_gpu_config,
|
||||
num_capsets, &num_capsets);
|
||||
virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
|
||||
num_capsets, &num_capsets);
|
||||
DRM_INFO("number of cap sets: %d\n", num_capsets);
|
||||
|
||||
virtio_gpu_modeset_init(vgdev);
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_mem_entry **ents,
|
||||
unsigned int *nents)
|
||||
{
|
||||
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev);
|
||||
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
|
||||
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
|
||||
struct scatterlist *sg;
|
||||
int si, ret;
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
|
||||
struct virtio_gpu_transfer_to_host_2d *cmd_p;
|
||||
struct virtio_gpu_vbuffer *vbuf;
|
||||
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev);
|
||||
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
|
||||
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
|
||||
|
||||
if (use_dma_api)
|
||||
|
|
@ -1015,7 +1015,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
|
||||
struct virtio_gpu_transfer_host_3d *cmd_p;
|
||||
struct virtio_gpu_vbuffer *vbuf;
|
||||
bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev);
|
||||
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
|
||||
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
|
||||
|
||||
if (use_dma_api)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
menuconfig HWSPINLOCK
|
||||
bool "Hardware Spinlock drivers"
|
||||
|
||||
if HWSPINLOCK
|
||||
|
||||
config HWSPINLOCK_OMAP
|
||||
tristate "OMAP Hardware Spinlock device"
|
||||
depends on HWSPINLOCK
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX || SOC_AM33XX || SOC_AM43XX || ARCH_K3 || COMPILE_TEST
|
||||
help
|
||||
Say y here to support the OMAP Hardware Spinlock device (firstly
|
||||
|
|
@ -18,7 +19,6 @@ config HWSPINLOCK_OMAP
|
|||
|
||||
config HWSPINLOCK_QCOM
|
||||
tristate "Qualcomm Hardware Spinlock device"
|
||||
depends on HWSPINLOCK
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
|
|
@ -30,7 +30,6 @@ config HWSPINLOCK_QCOM
|
|||
|
||||
config HWSPINLOCK_SIRF
|
||||
tristate "SIRF Hardware Spinlock device"
|
||||
depends on HWSPINLOCK
|
||||
depends on ARCH_SIRF || COMPILE_TEST
|
||||
help
|
||||
Say y here to support the SIRF Hardware Spinlock device, which
|
||||
|
|
@ -43,7 +42,6 @@ config HWSPINLOCK_SIRF
|
|||
config HWSPINLOCK_SPRD
|
||||
tristate "SPRD Hardware Spinlock device"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on HWSPINLOCK
|
||||
help
|
||||
Say y here to support the SPRD Hardware Spinlock device.
|
||||
|
||||
|
|
@ -52,7 +50,6 @@ config HWSPINLOCK_SPRD
|
|||
config HWSPINLOCK_STM32
|
||||
tristate "STM32 Hardware Spinlock device"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
depends on HWSPINLOCK
|
||||
help
|
||||
Say y here to support the STM32 Hardware Spinlock device.
|
||||
|
||||
|
|
@ -60,7 +57,6 @@ config HWSPINLOCK_STM32
|
|||
|
||||
config HSEM_U8500
|
||||
tristate "STE Hardware Semaphore functionality"
|
||||
depends on HWSPINLOCK
|
||||
depends on ARCH_U8500 || COMPILE_TEST
|
||||
help
|
||||
Say y here to support the STE Hardware Semaphore functionality, which
|
||||
|
|
@ -68,3 +64,5 @@ config HSEM_U8500
|
|||
SoC.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif # HWSPINLOCK
|
||||
|
|
|
|||
|
|
@ -70,41 +70,79 @@ static const struct of_device_id qcom_hwspinlock_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
|
||||
|
||||
static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev,
|
||||
u32 *base, u32 *stride)
|
||||
{
|
||||
struct device_node *syscon;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
|
||||
if (!syscon)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
regmap = syscon_node_to_regmap(syscon);
|
||||
of_node_put(syscon);
|
||||
if (IS_ERR(regmap))
|
||||
return regmap;
|
||||
|
||||
ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no offset in syscon\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no stride syscon\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return regmap;
|
||||
}
|
||||
|
||||
static const struct regmap_config tcsr_mutex_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = 0x40000,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev,
|
||||
u32 *offset, u32 *stride)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
|
||||
/* All modern platform has offset 0 and stride of 4k */
|
||||
*offset = 0;
|
||||
*stride = 0x1000;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return ERR_CAST(base);
|
||||
|
||||
return devm_regmap_init_mmio(dev, base, &tcsr_mutex_config);
|
||||
}
|
||||
|
||||
static int qcom_hwspinlock_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hwspinlock_device *bank;
|
||||
struct device_node *syscon;
|
||||
struct reg_field field;
|
||||
struct regmap *regmap;
|
||||
size_t array_size;
|
||||
u32 stride;
|
||||
u32 base;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
|
||||
if (!syscon) {
|
||||
dev_err(&pdev->dev, "no syscon property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride);
|
||||
if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV)
|
||||
regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride);
|
||||
|
||||
regmap = syscon_node_to_regmap(syscon);
|
||||
of_node_put(syscon);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no offset in syscon\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no stride syscon\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
|
||||
bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
|
||||
if (!bank)
|
||||
|
|
|
|||
|
|
@ -129,140 +129,8 @@ config MSM_IOMMU
|
|||
|
||||
If unsure, say N here.
|
||||
|
||||
config IOMMU_PGTABLES_L2
|
||||
def_bool y
|
||||
depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
|
||||
|
||||
# AMD IOMMU support
|
||||
config AMD_IOMMU
|
||||
bool "AMD IOMMU support"
|
||||
select SWIOTLB
|
||||
select PCI_MSI
|
||||
select PCI_ATS
|
||||
select PCI_PRI
|
||||
select PCI_PASID
|
||||
select IOMMU_API
|
||||
select IOMMU_IOVA
|
||||
select IOMMU_DMA
|
||||
depends on X86_64 && PCI && ACPI
|
||||
help
|
||||
With this option you can enable support for AMD IOMMU hardware in
|
||||
your system. An IOMMU is a hardware component which provides
|
||||
remapping of DMA memory accesses from devices. With an AMD IOMMU you
|
||||
can isolate the DMA memory of different devices and protect the
|
||||
system from misbehaving device drivers or hardware.
|
||||
|
||||
You can find out if your system has an AMD IOMMU if you look into
|
||||
your BIOS for an option to enable it or if you have an IVRS ACPI
|
||||
table.
|
||||
|
||||
config AMD_IOMMU_V2
|
||||
tristate "AMD IOMMU Version 2 driver"
|
||||
depends on AMD_IOMMU
|
||||
select MMU_NOTIFIER
|
||||
help
|
||||
This option enables support for the AMD IOMMUv2 features of the IOMMU
|
||||
hardware. Select this option if you want to use devices that support
|
||||
the PCI PRI and PASID interface.
|
||||
|
||||
config AMD_IOMMU_DEBUGFS
|
||||
bool "Enable AMD IOMMU internals in DebugFS"
|
||||
depends on AMD_IOMMU && IOMMU_DEBUGFS
|
||||
help
|
||||
!!!WARNING!!! !!!WARNING!!! !!!WARNING!!! !!!WARNING!!!
|
||||
|
||||
DO NOT ENABLE THIS OPTION UNLESS YOU REALLY, -REALLY- KNOW WHAT YOU ARE DOING!!!
|
||||
Exposes AMD IOMMU device internals in DebugFS.
|
||||
|
||||
This option is -NOT- intended for production environments, and should
|
||||
not generally be enabled.
|
||||
|
||||
# Intel IOMMU support
|
||||
config DMAR_TABLE
|
||||
bool
|
||||
|
||||
config INTEL_IOMMU
|
||||
bool "Support for Intel IOMMU using DMA Remapping Devices"
|
||||
depends on PCI_MSI && ACPI && (X86 || IA64)
|
||||
select DMA_OPS
|
||||
select IOMMU_API
|
||||
select IOMMU_IOVA
|
||||
select NEED_DMA_MAP_STATE
|
||||
select DMAR_TABLE
|
||||
select SWIOTLB
|
||||
select IOASID
|
||||
help
|
||||
DMA remapping (DMAR) devices support enables independent address
|
||||
translations for Direct Memory Access (DMA) from devices.
|
||||
These DMA remapping devices are reported via ACPI tables
|
||||
and include PCI device scope covered by these DMA
|
||||
remapping devices.
|
||||
|
||||
config INTEL_IOMMU_DEBUGFS
|
||||
bool "Export Intel IOMMU internals in Debugfs"
|
||||
depends on INTEL_IOMMU && IOMMU_DEBUGFS
|
||||
help
|
||||
!!!WARNING!!!
|
||||
|
||||
DO NOT ENABLE THIS OPTION UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!!!
|
||||
|
||||
Expose Intel IOMMU internals in Debugfs.
|
||||
|
||||
This option is -NOT- intended for production environments, and should
|
||||
only be enabled for debugging Intel IOMMU.
|
||||
|
||||
config INTEL_IOMMU_SVM
|
||||
bool "Support for Shared Virtual Memory with Intel IOMMU"
|
||||
depends on INTEL_IOMMU && X86_64
|
||||
select PCI_PASID
|
||||
select PCI_PRI
|
||||
select MMU_NOTIFIER
|
||||
select IOASID
|
||||
help
|
||||
Shared Virtual Memory (SVM) provides a facility for devices
|
||||
to access DMA resources through process address space by
|
||||
means of a Process Address Space ID (PASID).
|
||||
|
||||
config INTEL_IOMMU_DEFAULT_ON
|
||||
def_bool y
|
||||
prompt "Enable Intel DMA Remapping Devices by default"
|
||||
depends on INTEL_IOMMU
|
||||
help
|
||||
Selecting this option will enable a DMAR device at boot time if
|
||||
one is found. If this option is not selected, DMAR support can
|
||||
be enabled by passing intel_iommu=on to the kernel.
|
||||
|
||||
config INTEL_IOMMU_BROKEN_GFX_WA
|
||||
bool "Workaround broken graphics drivers (going away soon)"
|
||||
depends on INTEL_IOMMU && BROKEN && X86
|
||||
help
|
||||
Current Graphics drivers tend to use physical address
|
||||
for DMA and avoid using DMA APIs. Setting this config
|
||||
option permits the IOMMU driver to set a unity map for
|
||||
all the OS-visible memory. Hence the driver can continue
|
||||
to use physical addresses for DMA, at least until this
|
||||
option is removed in the 2.6.32 kernel.
|
||||
|
||||
config INTEL_IOMMU_FLOPPY_WA
|
||||
def_bool y
|
||||
depends on INTEL_IOMMU && X86
|
||||
help
|
||||
Floppy disk drivers are known to bypass DMA API calls
|
||||
thereby failing to work when IOMMU is enabled. This
|
||||
workaround will setup a 1:1 mapping for the first
|
||||
16MiB to make floppy (an ISA device) work.
|
||||
|
||||
config INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON
|
||||
bool "Enable Intel IOMMU scalable mode by default"
|
||||
depends on INTEL_IOMMU
|
||||
help
|
||||
Selecting this option will enable by default the scalable mode if
|
||||
hardware presents the capability. The scalable mode is defined in
|
||||
VT-d 3.0. The scalable mode capability could be checked by reading
|
||||
/sys/devices/virtual/iommu/dmar*/intel-iommu/ecap. If this option
|
||||
is not selected, scalable mode support could also be enabled by
|
||||
passing intel_iommu=sm_on to the kernel. If not sure, please use
|
||||
the default value.
|
||||
source "drivers/iommu/amd/Kconfig"
|
||||
source "drivers/iommu/intel/Kconfig"
|
||||
|
||||
config IRQ_REMAP
|
||||
bool "Support for Interrupt Remapping"
|
||||
|
|
@ -276,7 +144,6 @@ config IRQ_REMAP
|
|||
# OMAP IOMMU support
|
||||
config OMAP_IOMMU
|
||||
bool "OMAP IOMMU Support"
|
||||
depends on ARM && MMU || (COMPILE_TEST && (ARM || ARM64 || IA64 || SPARC))
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
select IOMMU_API
|
||||
help
|
||||
|
|
@ -294,7 +161,6 @@ config OMAP_IOMMU_DEBUG
|
|||
|
||||
config ROCKCHIP_IOMMU
|
||||
bool "Rockchip IOMMU Support"
|
||||
depends on ARM || ARM64 || (COMPILE_TEST && (ARM64 || IA64 || SPARC))
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
select IOMMU_API
|
||||
select ARM_DMA_USE_IOMMU
|
||||
|
|
@ -311,7 +177,6 @@ config SUN50I_IOMMU
|
|||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
select ARM_DMA_USE_IOMMU
|
||||
select IOMMU_API
|
||||
select IOMMU_DMA
|
||||
help
|
||||
Support for the IOMMU introduced in the Allwinner H6 SoCs.
|
||||
|
||||
|
|
@ -338,7 +203,7 @@ config TEGRA_IOMMU_SMMU
|
|||
|
||||
config EXYNOS_IOMMU
|
||||
bool "Exynos IOMMU Support"
|
||||
depends on ARCH_EXYNOS && MMU || (COMPILE_TEST && (ARM || ARM64 || IA64 || SPARC))
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
depends on !CPU_BIG_ENDIAN # revisit driver if we can enable big-endian ptes
|
||||
select IOMMU_API
|
||||
select ARM_DMA_USE_IOMMU
|
||||
|
|
@ -361,7 +226,6 @@ config EXYNOS_IOMMU_DEBUG
|
|||
|
||||
config IPMMU_VMSA
|
||||
bool "Renesas VMSA-compatible IPMMU"
|
||||
depends on ARM || IOMMU_DMA
|
||||
depends on ARCH_RENESAS || (COMPILE_TEST && !GENERIC_ATOMIC64)
|
||||
select IOMMU_API
|
||||
select IOMMU_IO_PGTABLE_LPAE
|
||||
|
|
@ -383,7 +247,7 @@ config SPAPR_TCE_IOMMU
|
|||
# ARM IOMMU support
|
||||
config ARM_SMMU
|
||||
tristate "ARM Ltd. System MMU (SMMU) Support"
|
||||
depends on (ARM64 || ARM || (COMPILE_TEST && !GENERIC_ATOMIC64)) && MMU
|
||||
depends on ARM64 || ARM || (COMPILE_TEST && !GENERIC_ATOMIC64)
|
||||
depends on QCOM_SCM || !QCOM_SCM #if QCOM_SCM=m this can't be =y
|
||||
select IOMMU_API
|
||||
select IOMMU_IO_PGTABLE_LPAE
|
||||
|
|
@ -470,11 +334,9 @@ config S390_AP_IOMMU
|
|||
|
||||
config MTK_IOMMU
|
||||
bool "MTK IOMMU Support"
|
||||
depends on HAS_DMA
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
select ARM_DMA_USE_IOMMU
|
||||
select IOMMU_API
|
||||
select IOMMU_DMA
|
||||
select IOMMU_IO_PGTABLE_ARMV7S
|
||||
select MEMORY
|
||||
select MTK_SMI
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += amd/ intel/ arm/
|
||||
obj-$(CONFIG_IOMMU_API) += iommu.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
|
||||
|
|
@ -11,19 +12,8 @@ obj-$(CONFIG_IOASID) += ioasid.o
|
|||
obj-$(CONFIG_IOMMU_IOVA) += iova.o
|
||||
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
|
||||
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
|
||||
obj-$(CONFIG_AMD_IOMMU) += amd/iommu.o amd/init.o amd/quirks.o
|
||||
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd/debugfs.o
|
||||
obj-$(CONFIG_AMD_IOMMU_V2) += amd/iommu_v2.o
|
||||
obj-$(CONFIG_ARM_SMMU) += arm_smmu.o
|
||||
arm_smmu-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-qcom.o
|
||||
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
||||
obj-$(CONFIG_DMAR_TABLE) += intel/dmar.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += intel/iommu.o intel/pasid.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += intel/trace.o
|
||||
obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += intel/debugfs.o
|
||||
obj-$(CONFIG_INTEL_IOMMU_SVM) += intel/svm.o
|
||||
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += intel/irq_remapping.o irq_remapping.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o
|
||||
obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o
|
||||
obj-$(CONFIG_MTK_IOMMU_V1) += mtk_iommu_v1.o
|
||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||
|
|
@ -35,6 +25,5 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
|
|||
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
|
||||
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
|
||||
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
|
||||
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
|
||||
obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
|
||||
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
|
||||
|
|
|
|||
44
drivers/iommu/amd/Kconfig
Normal file
44
drivers/iommu/amd/Kconfig
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# AMD IOMMU support
|
||||
config AMD_IOMMU
|
||||
bool "AMD IOMMU support"
|
||||
select SWIOTLB
|
||||
select PCI_MSI
|
||||
select PCI_ATS
|
||||
select PCI_PRI
|
||||
select PCI_PASID
|
||||
select IOMMU_API
|
||||
select IOMMU_IOVA
|
||||
select IOMMU_DMA
|
||||
depends on X86_64 && PCI && ACPI
|
||||
help
|
||||
With this option you can enable support for AMD IOMMU hardware in
|
||||
your system. An IOMMU is a hardware component which provides
|
||||
remapping of DMA memory accesses from devices. With an AMD IOMMU you
|
||||
can isolate the DMA memory of different devices and protect the
|
||||
system from misbehaving device drivers or hardware.
|
||||
|
||||
You can find out if your system has an AMD IOMMU if you look into
|
||||
your BIOS for an option to enable it or if you have an IVRS ACPI
|
||||
table.
|
||||
|
||||
config AMD_IOMMU_V2
|
||||
tristate "AMD IOMMU Version 2 driver"
|
||||
depends on AMD_IOMMU
|
||||
select MMU_NOTIFIER
|
||||
help
|
||||
This option enables support for the AMD IOMMUv2 features of the IOMMU
|
||||
hardware. Select this option if you want to use devices that support
|
||||
the PCI PRI and PASID interface.
|
||||
|
||||
config AMD_IOMMU_DEBUGFS
|
||||
bool "Enable AMD IOMMU internals in DebugFS"
|
||||
depends on AMD_IOMMU && IOMMU_DEBUGFS
|
||||
help
|
||||
!!!WARNING!!! !!!WARNING!!! !!!WARNING!!! !!!WARNING!!!
|
||||
|
||||
DO NOT ENABLE THIS OPTION UNLESS YOU REALLY, -REALLY- KNOW WHAT YOU ARE DOING!!!
|
||||
Exposes AMD IOMMU device internals in DebugFS.
|
||||
|
||||
This option is -NOT- intended for production environments, and should
|
||||
not generally be enabled.
|
||||
4
drivers/iommu/amd/Makefile
Normal file
4
drivers/iommu/amd/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_AMD_IOMMU) += iommu.o init.o quirks.o
|
||||
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += debugfs.o
|
||||
obj-$(CONFIG_AMD_IOMMU_V2) += iommu_v2.o
|
||||
|
|
@ -720,21 +720,14 @@ static void iommu_enable_ppr_log(struct amd_iommu *iommu)
|
|||
|
||||
static void __init free_ppr_log(struct amd_iommu *iommu)
|
||||
{
|
||||
if (iommu->ppr_log == NULL)
|
||||
return;
|
||||
|
||||
free_pages((unsigned long)iommu->ppr_log, get_order(PPR_LOG_SIZE));
|
||||
}
|
||||
|
||||
static void free_ga_log(struct amd_iommu *iommu)
|
||||
{
|
||||
#ifdef CONFIG_IRQ_REMAP
|
||||
if (iommu->ga_log)
|
||||
free_pages((unsigned long)iommu->ga_log,
|
||||
get_order(GA_LOG_SIZE));
|
||||
if (iommu->ga_log_tail)
|
||||
free_pages((unsigned long)iommu->ga_log_tail,
|
||||
get_order(8));
|
||||
free_pages((unsigned long)iommu->ga_log, get_order(GA_LOG_SIZE));
|
||||
free_pages((unsigned long)iommu->ga_log_tail, get_order(8));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -1842,7 +1835,7 @@ static void print_iommu_info(void)
|
|||
pci_info(pdev, "Found IOMMU cap 0x%hx\n", iommu->cap_ptr);
|
||||
|
||||
if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
|
||||
pci_info(pdev, "Extended features (%#llx):\n",
|
||||
pci_info(pdev, "Extended features (%#llx):",
|
||||
iommu->features);
|
||||
for (i = 0; i < ARRAY_SIZE(feat_str); ++i) {
|
||||
if (iommu_feature(iommu, (1ULL << i)))
|
||||
|
|
|
|||
|
|
@ -162,7 +162,18 @@ static void amd_iommu_domain_get_pgtable(struct protection_domain *domain,
|
|||
pgtable->mode = pt_root & 7; /* lowest 3 bits encode pgtable mode */
|
||||
}
|
||||
|
||||
static u64 amd_iommu_domain_encode_pgtable(u64 *root, int mode)
|
||||
static void amd_iommu_domain_set_pt_root(struct protection_domain *domain, u64 root)
|
||||
{
|
||||
atomic64_set(&domain->pt_root, root);
|
||||
}
|
||||
|
||||
static void amd_iommu_domain_clr_pt_root(struct protection_domain *domain)
|
||||
{
|
||||
amd_iommu_domain_set_pt_root(domain, 0);
|
||||
}
|
||||
|
||||
static void amd_iommu_domain_set_pgtable(struct protection_domain *domain,
|
||||
u64 *root, int mode)
|
||||
{
|
||||
u64 pt_root;
|
||||
|
||||
|
|
@ -170,7 +181,7 @@ static u64 amd_iommu_domain_encode_pgtable(u64 *root, int mode)
|
|||
pt_root = mode & 7;
|
||||
pt_root |= (u64)root;
|
||||
|
||||
return pt_root;
|
||||
amd_iommu_domain_set_pt_root(domain, pt_root);
|
||||
}
|
||||
|
||||
static struct iommu_dev_data *alloc_dev_data(u16 devid)
|
||||
|
|
@ -1410,7 +1421,7 @@ static bool increase_address_space(struct protection_domain *domain,
|
|||
struct domain_pgtable pgtable;
|
||||
unsigned long flags;
|
||||
bool ret = true;
|
||||
u64 *pte, root;
|
||||
u64 *pte;
|
||||
|
||||
spin_lock_irqsave(&domain->lock, flags);
|
||||
|
||||
|
|
@ -1438,8 +1449,7 @@ static bool increase_address_space(struct protection_domain *domain,
|
|||
* Device Table needs to be updated and flushed before the new root can
|
||||
* be published.
|
||||
*/
|
||||
root = amd_iommu_domain_encode_pgtable(pte, pgtable.mode);
|
||||
atomic64_set(&domain->pt_root, root);
|
||||
amd_iommu_domain_set_pgtable(domain, pte, pgtable.mode);
|
||||
|
||||
ret = true;
|
||||
|
||||
|
|
@ -2319,7 +2329,7 @@ static void protection_domain_free(struct protection_domain *domain)
|
|||
domain_id_free(domain->id);
|
||||
|
||||
amd_iommu_domain_get_pgtable(domain, &pgtable);
|
||||
atomic64_set(&domain->pt_root, 0);
|
||||
amd_iommu_domain_clr_pt_root(domain);
|
||||
free_pagetable(&pgtable);
|
||||
|
||||
kfree(domain);
|
||||
|
|
@ -2327,7 +2337,7 @@ static void protection_domain_free(struct protection_domain *domain)
|
|||
|
||||
static int protection_domain_init(struct protection_domain *domain, int mode)
|
||||
{
|
||||
u64 *pt_root = NULL, root;
|
||||
u64 *pt_root = NULL;
|
||||
|
||||
BUG_ON(mode < PAGE_MODE_NONE || mode > PAGE_MODE_6_LEVEL);
|
||||
|
||||
|
|
@ -2343,8 +2353,7 @@ static int protection_domain_init(struct protection_domain *domain, int mode)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
root = amd_iommu_domain_encode_pgtable(pt_root, mode);
|
||||
atomic64_set(&domain->pt_root, root);
|
||||
amd_iommu_domain_set_pgtable(domain, pt_root, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2713,8 +2722,8 @@ void amd_iommu_domain_direct_map(struct iommu_domain *dom)
|
|||
/* First save pgtable configuration*/
|
||||
amd_iommu_domain_get_pgtable(domain, &pgtable);
|
||||
|
||||
/* Update data structure */
|
||||
atomic64_set(&domain->pt_root, 0);
|
||||
/* Remove page-table from domain */
|
||||
amd_iommu_domain_clr_pt_root(domain);
|
||||
|
||||
/* Make changes visible to IOMMUs */
|
||||
update_domain(domain);
|
||||
|
|
|
|||
2
drivers/iommu/arm/Makefile
Normal file
2
drivers/iommu/arm/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += arm-smmu/ arm-smmu-v3/
|
||||
2
drivers/iommu/arm/arm-smmu-v3/Makefile
Normal file
2
drivers/iommu/arm/arm-smmu-v3/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
||||
|
|
@ -1479,7 +1479,7 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
|
|||
}
|
||||
|
||||
/*
|
||||
* Try to unlock the cmq lock. This will fail if we're the last
|
||||
* Try to unlock the cmdq lock. This will fail if we're the last
|
||||
* reader, in which case we can safely update cmdq->q.llq.cons
|
||||
*/
|
||||
if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) {
|
||||
|
|
@ -2850,7 +2850,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
if (!ops)
|
||||
return -ENODEV;
|
||||
|
||||
return ops->map(ops, iova, paddr, size, prot);
|
||||
return ops->map(ops, iova, paddr, size, prot, gfp);
|
||||
}
|
||||
|
||||
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
4
drivers/iommu/arm/arm-smmu/Makefile
Normal file
4
drivers/iommu/arm/arm-smmu/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
|
||||
obj-$(CONFIG_ARM_SMMU) += arm_smmu.o
|
||||
arm_smmu-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-nvidia.o arm-smmu-qcom.o
|
||||
|
|
@ -147,16 +147,57 @@ static const struct arm_smmu_impl arm_mmu500_impl = {
|
|||
.reset = arm_mmu500_reset,
|
||||
};
|
||||
|
||||
static u64 mrvl_mmu500_readq(struct arm_smmu_device *smmu, int page, int off)
|
||||
{
|
||||
/*
|
||||
* Marvell Armada-AP806 erratum #582743.
|
||||
* Split all the readq to double readl
|
||||
*/
|
||||
return hi_lo_readq_relaxed(arm_smmu_page(smmu, page) + off);
|
||||
}
|
||||
|
||||
static void mrvl_mmu500_writeq(struct arm_smmu_device *smmu, int page, int off,
|
||||
u64 val)
|
||||
{
|
||||
/*
|
||||
* Marvell Armada-AP806 erratum #582743.
|
||||
* Split all the writeq to double writel
|
||||
*/
|
||||
hi_lo_writeq_relaxed(val, arm_smmu_page(smmu, page) + off);
|
||||
}
|
||||
|
||||
static int mrvl_mmu500_cfg_probe(struct arm_smmu_device *smmu)
|
||||
{
|
||||
|
||||
/*
|
||||
* Armada-AP806 erratum #582743.
|
||||
* Hide the SMMU_IDR2.PTFSv8 fields to sidestep the AArch64
|
||||
* formats altogether and allow using 32 bits access on the
|
||||
* interconnect.
|
||||
*/
|
||||
smmu->features &= ~(ARM_SMMU_FEAT_FMT_AARCH64_4K |
|
||||
ARM_SMMU_FEAT_FMT_AARCH64_16K |
|
||||
ARM_SMMU_FEAT_FMT_AARCH64_64K);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct arm_smmu_impl mrvl_mmu500_impl = {
|
||||
.read_reg64 = mrvl_mmu500_readq,
|
||||
.write_reg64 = mrvl_mmu500_writeq,
|
||||
.cfg_probe = mrvl_mmu500_cfg_probe,
|
||||
.reset = arm_mmu500_reset,
|
||||
};
|
||||
|
||||
|
||||
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
|
||||
{
|
||||
const struct device_node *np = smmu->dev->of_node;
|
||||
|
||||
/*
|
||||
* We will inevitably have to combine model-specific implementation
|
||||
* quirks with platform-specific integration quirks, but everything
|
||||
* we currently support happens to work out as straightforward
|
||||
* mutually-exclusive assignments.
|
||||
* Set the impl for model-specific implementation quirks first,
|
||||
* such that platform integration quirks can pick it up and
|
||||
* inherit from it if necessary.
|
||||
*/
|
||||
switch (smmu->model) {
|
||||
case ARM_MMU500:
|
||||
|
|
@ -168,12 +209,21 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
|
|||
break;
|
||||
}
|
||||
|
||||
/* This is implicitly MMU-400 */
|
||||
if (of_property_read_bool(np, "calxeda,smmu-secure-config-access"))
|
||||
smmu->impl = &calxeda_impl;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra194-smmu"))
|
||||
return nvidia_smmu_impl_init(smmu);
|
||||
|
||||
if (of_device_is_compatible(np, "qcom,sdm845-smmu-500") ||
|
||||
of_device_is_compatible(np, "qcom,sc7180-smmu-500"))
|
||||
of_device_is_compatible(np, "qcom,sc7180-smmu-500") ||
|
||||
of_device_is_compatible(np, "qcom,sm8150-smmu-500") ||
|
||||
of_device_is_compatible(np, "qcom,sm8250-smmu-500"))
|
||||
return qcom_smmu_impl_init(smmu);
|
||||
|
||||
if (of_device_is_compatible(np, "marvell,ap806-smmu-500"))
|
||||
smmu->impl = &mrvl_mmu500_impl;
|
||||
|
||||
return smmu;
|
||||
}
|
||||
278
drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c
Normal file
278
drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (C) 2019-2020 NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "arm-smmu.h"
|
||||
|
||||
/*
|
||||
* Tegra194 has three ARM MMU-500 Instances.
|
||||
* Two of them are used together and must be programmed identically for
|
||||
* interleaved IOVA accesses across them and translates accesses from
|
||||
* non-isochronous HW devices.
|
||||
* Third one is used for translating accesses from isochronous HW devices.
|
||||
* This implementation supports programming of the two instances that must
|
||||
* be programmed identically.
|
||||
* The third instance usage is through standard arm-smmu driver itself and
|
||||
* is out of scope of this implementation.
|
||||
*/
|
||||
#define NUM_SMMU_INSTANCES 2
|
||||
|
||||
struct nvidia_smmu {
|
||||
struct arm_smmu_device smmu;
|
||||
void __iomem *bases[NUM_SMMU_INSTANCES];
|
||||
};
|
||||
|
||||
static inline void __iomem *nvidia_smmu_page(struct arm_smmu_device *smmu,
|
||||
unsigned int inst, int page)
|
||||
{
|
||||
struct nvidia_smmu *nvidia_smmu;
|
||||
|
||||
nvidia_smmu = container_of(smmu, struct nvidia_smmu, smmu);
|
||||
return nvidia_smmu->bases[inst] + (page << smmu->pgshift);
|
||||
}
|
||||
|
||||
static u32 nvidia_smmu_read_reg(struct arm_smmu_device *smmu,
|
||||
int page, int offset)
|
||||
{
|
||||
void __iomem *reg = nvidia_smmu_page(smmu, 0, page) + offset;
|
||||
|
||||
return readl_relaxed(reg);
|
||||
}
|
||||
|
||||
static void nvidia_smmu_write_reg(struct arm_smmu_device *smmu,
|
||||
int page, int offset, u32 val)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_SMMU_INSTANCES; i++) {
|
||||
void __iomem *reg = nvidia_smmu_page(smmu, i, page) + offset;
|
||||
|
||||
writel_relaxed(val, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static u64 nvidia_smmu_read_reg64(struct arm_smmu_device *smmu,
|
||||
int page, int offset)
|
||||
{
|
||||
void __iomem *reg = nvidia_smmu_page(smmu, 0, page) + offset;
|
||||
|
||||
return readq_relaxed(reg);
|
||||
}
|
||||
|
||||
static void nvidia_smmu_write_reg64(struct arm_smmu_device *smmu,
|
||||
int page, int offset, u64 val)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_SMMU_INSTANCES; i++) {
|
||||
void __iomem *reg = nvidia_smmu_page(smmu, i, page) + offset;
|
||||
|
||||
writeq_relaxed(val, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void nvidia_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
|
||||
int sync, int status)
|
||||
{
|
||||
unsigned int delay;
|
||||
|
||||
arm_smmu_writel(smmu, page, sync, 0);
|
||||
|
||||
for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) {
|
||||
unsigned int spin_cnt;
|
||||
|
||||
for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) {
|
||||
u32 val = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_SMMU_INSTANCES; i++) {
|
||||
void __iomem *reg;
|
||||
|
||||
reg = nvidia_smmu_page(smmu, i, page) + status;
|
||||
val |= readl_relaxed(reg);
|
||||
}
|
||||
|
||||
if (!(val & ARM_SMMU_sTLBGSTATUS_GSACTIVE))
|
||||
return;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"TLB sync timed out -- SMMU may be deadlocked\n");
|
||||
}
|
||||
|
||||
static int nvidia_smmu_reset(struct arm_smmu_device *smmu)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_SMMU_INSTANCES; i++) {
|
||||
u32 val;
|
||||
void __iomem *reg = nvidia_smmu_page(smmu, i, ARM_SMMU_GR0) +
|
||||
ARM_SMMU_GR0_sGFSR;
|
||||
|
||||
/* clear global FSR */
|
||||
val = readl_relaxed(reg);
|
||||
writel_relaxed(val, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t nvidia_smmu_global_fault_inst(int irq,
|
||||
struct arm_smmu_device *smmu,
|
||||
int inst)
|
||||
{
|
||||
u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
|
||||
void __iomem *gr0_base = nvidia_smmu_page(smmu, inst, 0);
|
||||
|
||||
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
|
||||
if (!gfsr)
|
||||
return IRQ_NONE;
|
||||
|
||||
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
|
||||
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
|
||||
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
|
||||
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"Unexpected global fault, this could be serious\n");
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
|
||||
gfsr, gfsynr0, gfsynr1, gfsynr2);
|
||||
|
||||
writel_relaxed(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t nvidia_smmu_global_fault(int irq, void *dev)
|
||||
{
|
||||
unsigned int inst;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct arm_smmu_device *smmu = dev;
|
||||
|
||||
for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) {
|
||||
irqreturn_t irq_ret;
|
||||
|
||||
irq_ret = nvidia_smmu_global_fault_inst(irq, smmu, inst);
|
||||
if (irq_ret == IRQ_HANDLED)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t nvidia_smmu_context_fault_bank(int irq,
|
||||
struct arm_smmu_device *smmu,
|
||||
int idx, int inst)
|
||||
{
|
||||
u32 fsr, fsynr, cbfrsynra;
|
||||
unsigned long iova;
|
||||
void __iomem *gr1_base = nvidia_smmu_page(smmu, inst, 1);
|
||||
void __iomem *cb_base = nvidia_smmu_page(smmu, inst, smmu->numpage + idx);
|
||||
|
||||
fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
|
||||
if (!(fsr & ARM_SMMU_FSR_FAULT))
|
||||
return IRQ_NONE;
|
||||
|
||||
fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
|
||||
iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR);
|
||||
cbfrsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(idx));
|
||||
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n",
|
||||
fsr, iova, fsynr, cbfrsynra, idx);
|
||||
|
||||
writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t nvidia_smmu_context_fault(int irq, void *dev)
|
||||
{
|
||||
int idx;
|
||||
unsigned int inst;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct arm_smmu_device *smmu;
|
||||
struct iommu_domain *domain = dev;
|
||||
struct arm_smmu_domain *smmu_domain;
|
||||
|
||||
smmu_domain = container_of(domain, struct arm_smmu_domain, domain);
|
||||
smmu = smmu_domain->smmu;
|
||||
|
||||
for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) {
|
||||
irqreturn_t irq_ret;
|
||||
|
||||
/*
|
||||
* Interrupt line is shared between all contexts.
|
||||
* Check for faults across all contexts.
|
||||
*/
|
||||
for (idx = 0; idx < smmu->num_context_banks; idx++) {
|
||||
irq_ret = nvidia_smmu_context_fault_bank(irq, smmu,
|
||||
idx, inst);
|
||||
if (irq_ret == IRQ_HANDLED)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct arm_smmu_impl nvidia_smmu_impl = {
|
||||
.read_reg = nvidia_smmu_read_reg,
|
||||
.write_reg = nvidia_smmu_write_reg,
|
||||
.read_reg64 = nvidia_smmu_read_reg64,
|
||||
.write_reg64 = nvidia_smmu_write_reg64,
|
||||
.reset = nvidia_smmu_reset,
|
||||
.tlb_sync = nvidia_smmu_tlb_sync,
|
||||
.global_fault = nvidia_smmu_global_fault,
|
||||
.context_fault = nvidia_smmu_context_fault,
|
||||
};
|
||||
|
||||
struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = smmu->dev;
|
||||
struct nvidia_smmu *nvidia_smmu;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
nvidia_smmu = devm_kzalloc(dev, sizeof(*nvidia_smmu), GFP_KERNEL);
|
||||
if (!nvidia_smmu)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Copy the data from struct arm_smmu_device *smmu allocated in
|
||||
* arm-smmu.c. The smmu from struct nvidia_smmu replaces the smmu
|
||||
* pointer used in arm-smmu.c once this function returns.
|
||||
* This is necessary to derive nvidia_smmu from smmu pointer passed
|
||||
* through arm_smmu_impl function calls subsequently.
|
||||
*/
|
||||
nvidia_smmu->smmu = *smmu;
|
||||
/* Instance 0 is ioremapped by arm-smmu.c. */
|
||||
nvidia_smmu->bases[0] = smmu->base;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
nvidia_smmu->bases[1] = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(nvidia_smmu->bases[1]))
|
||||
return ERR_CAST(nvidia_smmu->bases[1]);
|
||||
|
||||
nvidia_smmu->smmu.impl = &nvidia_smmu_impl;
|
||||
|
||||
/*
|
||||
* Free the struct arm_smmu_device *smmu allocated in arm-smmu.c.
|
||||
* Once this function returns, arm-smmu.c would use arm_smmu_device
|
||||
* allocated as part of struct nvidia_smmu.
|
||||
*/
|
||||
devm_kfree(dev, smmu);
|
||||
|
||||
return &nvidia_smmu->smmu;
|
||||
}
|
||||
|
|
@ -52,9 +52,6 @@
|
|||
*/
|
||||
#define QCOM_DUMMY_VAL -1
|
||||
|
||||
#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
|
||||
#define TLB_SPIN_COUNT 10
|
||||
|
||||
#define MSI_IOVA_BASE 0x8000000
|
||||
#define MSI_IOVA_LENGTH 0x100000
|
||||
|
||||
|
|
@ -671,6 +668,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
enum io_pgtable_fmt fmt;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
irqreturn_t (*context_fault)(int irq, void *dev);
|
||||
|
||||
mutex_lock(&smmu_domain->init_mutex);
|
||||
if (smmu_domain->smmu)
|
||||
|
|
@ -832,7 +830,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
* handler seeing a half-initialised domain state.
|
||||
*/
|
||||
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
|
||||
ret = devm_request_irq(smmu->dev, irq, arm_smmu_context_fault,
|
||||
|
||||
if (smmu->impl && smmu->impl->context_fault)
|
||||
context_fault = smmu->impl->context_fault;
|
||||
else
|
||||
context_fault = arm_smmu_context_fault;
|
||||
|
||||
ret = devm_request_irq(smmu->dev, irq, context_fault,
|
||||
IRQF_SHARED, "arm-smmu-context-fault", domain);
|
||||
if (ret < 0) {
|
||||
dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
|
||||
|
|
@ -1235,7 +1239,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
return -ENODEV;
|
||||
|
||||
arm_smmu_rpm_get(smmu);
|
||||
ret = ops->map(ops, iova, paddr, size, prot);
|
||||
ret = ops->map(ops, iova, paddr, size, prot, gfp);
|
||||
arm_smmu_rpm_put(smmu);
|
||||
|
||||
return ret;
|
||||
|
|
@ -1736,7 +1740,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
unsigned int size;
|
||||
u32 id;
|
||||
bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
dev_notice(smmu->dev, "probing hardware configuration...\n");
|
||||
dev_notice(smmu->dev, "SMMUv%d with:\n",
|
||||
|
|
@ -1899,6 +1903,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K;
|
||||
}
|
||||
|
||||
if (smmu->impl && smmu->impl->cfg_probe) {
|
||||
ret = smmu->impl->cfg_probe(smmu);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Now we've corralled the various formats, what'll it do? */
|
||||
if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S)
|
||||
smmu->pgsize_bitmap |= SZ_4K | SZ_64K | SZ_1M | SZ_16M;
|
||||
|
|
@ -1926,9 +1936,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n",
|
||||
smmu->ipa_size, smmu->pa_size);
|
||||
|
||||
if (smmu->impl && smmu->impl->cfg_probe)
|
||||
return smmu->impl->cfg_probe(smmu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1954,6 +1961,7 @@ static const struct of_device_id arm_smmu_of_match[] = {
|
|||
{ .compatible = "arm,mmu-401", .data = &arm_mmu401 },
|
||||
{ .compatible = "arm,mmu-500", .data = &arm_mmu500 },
|
||||
{ .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 },
|
||||
{ .compatible = "nvidia,smmu-500", .data = &arm_mmu500 },
|
||||
{ .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 },
|
||||
{ },
|
||||
};
|
||||
|
|
@ -2115,6 +2123,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
|||
struct arm_smmu_device *smmu;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num_irqs, i, err;
|
||||
irqreturn_t (*global_fault)(int irq, void *dev);
|
||||
|
||||
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
|
||||
if (!smmu) {
|
||||
|
|
@ -2131,10 +2140,6 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
smmu = arm_smmu_impl_init(smmu);
|
||||
if (IS_ERR(smmu))
|
||||
return PTR_ERR(smmu);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ioaddr = res->start;
|
||||
smmu->base = devm_ioremap_resource(dev, res);
|
||||
|
|
@ -2146,6 +2151,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
|||
*/
|
||||
smmu->numpage = resource_size(res);
|
||||
|
||||
smmu = arm_smmu_impl_init(smmu);
|
||||
if (IS_ERR(smmu))
|
||||
return PTR_ERR(smmu);
|
||||
|
||||
num_irqs = 0;
|
||||
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) {
|
||||
num_irqs++;
|
||||
|
|
@ -2201,9 +2210,14 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
|||
smmu->num_context_irqs = smmu->num_context_banks;
|
||||
}
|
||||
|
||||
if (smmu->impl && smmu->impl->global_fault)
|
||||
global_fault = smmu->impl->global_fault;
|
||||
else
|
||||
global_fault = arm_smmu_global_fault;
|
||||
|
||||
for (i = 0; i < smmu->num_global_irqs; ++i) {
|
||||
err = devm_request_irq(smmu->dev, smmu->irqs[i],
|
||||
arm_smmu_global_fault,
|
||||
global_fault,
|
||||
IRQF_SHARED,
|
||||
"arm-smmu global fault",
|
||||
smmu);
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/io-pgtable.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -236,6 +237,8 @@ enum arm_smmu_cbar_type {
|
|||
/* Maximum number of context banks per SMMU */
|
||||
#define ARM_SMMU_MAX_CBS 128
|
||||
|
||||
#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
|
||||
#define TLB_SPIN_COUNT 10
|
||||
|
||||
/* Shared driver definitions */
|
||||
enum arm_smmu_arch_version {
|
||||
|
|
@ -402,6 +405,8 @@ struct arm_smmu_impl {
|
|||
void (*tlb_sync)(struct arm_smmu_device *smmu, int page, int sync,
|
||||
int status);
|
||||
int (*def_domain_type)(struct device *dev);
|
||||
irqreturn_t (*global_fault)(int irq, void *dev);
|
||||
irqreturn_t (*context_fault)(int irq, void *dev);
|
||||
};
|
||||
|
||||
static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)
|
||||
|
|
@ -465,6 +470,7 @@ static inline void arm_smmu_writeq(struct arm_smmu_device *smmu, int page,
|
|||
arm_smmu_writeq((s), ARM_SMMU_CB((s), (n)), (o), (v))
|
||||
|
||||
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu);
|
||||
struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu);
|
||||
struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu);
|
||||
|
||||
int arm_mmu500_reset(struct arm_smmu_device *smmu);
|
||||
|
|
@ -37,14 +37,20 @@
|
|||
|
||||
#define SMMU_INTR_SEL_NS 0x2000
|
||||
|
||||
enum qcom_iommu_clk {
|
||||
CLK_IFACE,
|
||||
CLK_BUS,
|
||||
CLK_TBU,
|
||||
CLK_NUM,
|
||||
};
|
||||
|
||||
struct qcom_iommu_ctx;
|
||||
|
||||
struct qcom_iommu_dev {
|
||||
/* IOMMU core code handle */
|
||||
struct iommu_device iommu;
|
||||
struct device *dev;
|
||||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
struct clk_bulk_data clks[CLK_NUM];
|
||||
void __iomem *local_base;
|
||||
u32 sec_id;
|
||||
u8 num_ctxs;
|
||||
|
|
@ -301,7 +307,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
|
|||
ARM_SMMU_SCTLR_M | ARM_SMMU_SCTLR_S1_ASIDPNE |
|
||||
ARM_SMMU_SCTLR_CFCFG;
|
||||
|
||||
if (IS_ENABLED(CONFIG_BIG_ENDIAN))
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
|
||||
reg |= ARM_SMMU_SCTLR_E;
|
||||
|
||||
iommu_writel(ctx, ARM_SMMU_CB_SCTLR, reg);
|
||||
|
|
@ -438,7 +444,7 @@ static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
||||
ret = ops->map(ops, iova, paddr, size, prot);
|
||||
ret = ops->map(ops, iova, paddr, size, prot, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -613,32 +619,6 @@ static const struct iommu_ops qcom_iommu_ops = {
|
|||
.pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
|
||||
};
|
||||
|
||||
static int qcom_iommu_enable_clocks(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(qcom_iommu->iface_clk);
|
||||
if (ret) {
|
||||
dev_err(qcom_iommu->dev, "Couldn't enable iface_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qcom_iommu->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(qcom_iommu->dev, "Couldn't enable bus_clk\n");
|
||||
clk_disable_unprepare(qcom_iommu->iface_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_iommu_disable_clocks(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
clk_disable_unprepare(qcom_iommu->bus_clk);
|
||||
clk_disable_unprepare(qcom_iommu->iface_clk);
|
||||
}
|
||||
|
||||
static int qcom_iommu_sec_ptbl_init(struct device *dev)
|
||||
{
|
||||
size_t psize = 0;
|
||||
|
|
@ -795,6 +775,7 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
|||
struct qcom_iommu_dev *qcom_iommu;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
int ret, max_asid = 0;
|
||||
|
||||
/* find the max asid (which is 1:1 to ctx bank idx), so we know how
|
||||
|
|
@ -817,17 +798,26 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(qcom_iommu->local_base);
|
||||
}
|
||||
|
||||
qcom_iommu->iface_clk = devm_clk_get(dev, "iface");
|
||||
if (IS_ERR(qcom_iommu->iface_clk)) {
|
||||
clk = devm_clk_get(dev, "iface");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get iface clock\n");
|
||||
return PTR_ERR(qcom_iommu->iface_clk);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
qcom_iommu->clks[CLK_IFACE].clk = clk;
|
||||
|
||||
qcom_iommu->bus_clk = devm_clk_get(dev, "bus");
|
||||
if (IS_ERR(qcom_iommu->bus_clk)) {
|
||||
clk = devm_clk_get(dev, "bus");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get bus clock\n");
|
||||
return PTR_ERR(qcom_iommu->bus_clk);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
qcom_iommu->clks[CLK_BUS].clk = clk;
|
||||
|
||||
clk = devm_clk_get_optional(dev, "tbu");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get tbu clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
qcom_iommu->clks[CLK_TBU].clk = clk;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "qcom,iommu-secure-id",
|
||||
&qcom_iommu->sec_id)) {
|
||||
|
|
@ -899,14 +889,14 @@ static int __maybe_unused qcom_iommu_resume(struct device *dev)
|
|||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev);
|
||||
|
||||
return qcom_iommu_enable_clocks(qcom_iommu);
|
||||
return clk_bulk_prepare_enable(CLK_NUM, qcom_iommu->clks);
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_iommu_suspend(struct device *dev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev);
|
||||
|
||||
qcom_iommu_disable_clocks(qcom_iommu);
|
||||
clk_bulk_disable_unprepare(CLK_NUM, qcom_iommu->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ static u32 lv2ent_offset(sysmmu_iova_t iova)
|
|||
#define REG_V5_FAULT_AR_VA 0x070
|
||||
#define REG_V5_FAULT_AW_VA 0x080
|
||||
|
||||
#define has_sysmmu(dev) (dev->archdata.iommu != NULL)
|
||||
#define has_sysmmu(dev) (dev_iommu_priv_get(dev) != NULL)
|
||||
|
||||
static struct device *dma_dev;
|
||||
static struct kmem_cache *lv2table_kmem_cache;
|
||||
|
|
@ -226,7 +226,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = {
|
|||
};
|
||||
|
||||
/*
|
||||
* This structure is attached to dev.archdata.iommu of the master device
|
||||
* This structure is attached to dev->iommu->priv of the master device
|
||||
* on device add, contains a list of SYSMMU controllers defined by device tree,
|
||||
* which are bound to given master device. It is usually referenced by 'owner'
|
||||
* pointer.
|
||||
|
|
@ -670,7 +670,7 @@ static int __maybe_unused exynos_sysmmu_suspend(struct device *dev)
|
|||
struct device *master = data->master;
|
||||
|
||||
if (master) {
|
||||
struct exynos_iommu_owner *owner = master->archdata.iommu;
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(master);
|
||||
|
||||
mutex_lock(&owner->rpm_lock);
|
||||
if (data->domain) {
|
||||
|
|
@ -688,7 +688,7 @@ static int __maybe_unused exynos_sysmmu_resume(struct device *dev)
|
|||
struct device *master = data->master;
|
||||
|
||||
if (master) {
|
||||
struct exynos_iommu_owner *owner = master->archdata.iommu;
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(master);
|
||||
|
||||
mutex_lock(&owner->rpm_lock);
|
||||
if (data->domain) {
|
||||
|
|
@ -721,7 +721,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = {
|
|||
}
|
||||
};
|
||||
|
||||
static inline void update_pte(sysmmu_pte_t *ent, sysmmu_pte_t val)
|
||||
static inline void exynos_iommu_set_pte(sysmmu_pte_t *ent, sysmmu_pte_t val)
|
||||
{
|
||||
dma_sync_single_for_cpu(dma_dev, virt_to_phys(ent), sizeof(*ent),
|
||||
DMA_TO_DEVICE);
|
||||
|
|
@ -837,8 +837,8 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
|
|||
static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct exynos_iommu_owner *owner = dev->archdata.iommu;
|
||||
struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
|
||||
phys_addr_t pagetable = virt_to_phys(domain->pgtable);
|
||||
struct sysmmu_drvdata *data, *next;
|
||||
unsigned long flags;
|
||||
|
|
@ -875,8 +875,8 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
|
|||
static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct exynos_iommu_owner *owner = dev->archdata.iommu;
|
||||
struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
|
||||
struct sysmmu_drvdata *data;
|
||||
phys_addr_t pagetable = virt_to_phys(domain->pgtable);
|
||||
unsigned long flags;
|
||||
|
|
@ -933,7 +933,7 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,
|
|||
if (!pent)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
update_pte(sent, mk_lv1ent_page(virt_to_phys(pent)));
|
||||
exynos_iommu_set_pte(sent, mk_lv1ent_page(virt_to_phys(pent)));
|
||||
kmemleak_ignore(pent);
|
||||
*pgcounter = NUM_LV2ENTRIES;
|
||||
handle = dma_map_single(dma_dev, pent, LV2TABLE_SIZE,
|
||||
|
|
@ -994,7 +994,7 @@ static int lv1set_section(struct exynos_iommu_domain *domain,
|
|||
*pgcnt = 0;
|
||||
}
|
||||
|
||||
update_pte(sent, mk_lv1ent_sect(paddr, prot));
|
||||
exynos_iommu_set_pte(sent, mk_lv1ent_sect(paddr, prot));
|
||||
|
||||
spin_lock(&domain->lock);
|
||||
if (lv1ent_page_zero(sent)) {
|
||||
|
|
@ -1018,7 +1018,7 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size,
|
|||
if (WARN_ON(!lv2ent_fault(pent)))
|
||||
return -EADDRINUSE;
|
||||
|
||||
update_pte(pent, mk_lv2ent_spage(paddr, prot));
|
||||
exynos_iommu_set_pte(pent, mk_lv2ent_spage(paddr, prot));
|
||||
*pgcnt -= 1;
|
||||
} else { /* size == LPAGE_SIZE */
|
||||
int i;
|
||||
|
|
@ -1150,7 +1150,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
|
|||
}
|
||||
|
||||
/* workaround for h/w bug in System MMU v3.3 */
|
||||
update_pte(ent, ZERO_LV2LINK);
|
||||
exynos_iommu_set_pte(ent, ZERO_LV2LINK);
|
||||
size = SECT_SIZE;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1171,7 +1171,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
|
|||
}
|
||||
|
||||
if (lv2ent_small(ent)) {
|
||||
update_pte(ent, 0);
|
||||
exynos_iommu_set_pte(ent, 0);
|
||||
size = SPAGE_SIZE;
|
||||
domain->lv2entcnt[lv1ent_offset(iova)] += 1;
|
||||
goto done;
|
||||
|
|
@ -1237,7 +1237,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
|
|||
|
||||
static struct iommu_device *exynos_iommu_probe_device(struct device *dev)
|
||||
{
|
||||
struct exynos_iommu_owner *owner = dev->archdata.iommu;
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
|
||||
struct sysmmu_drvdata *data;
|
||||
|
||||
if (!has_sysmmu(dev))
|
||||
|
|
@ -1263,7 +1263,7 @@ static struct iommu_device *exynos_iommu_probe_device(struct device *dev)
|
|||
|
||||
static void exynos_iommu_release_device(struct device *dev)
|
||||
{
|
||||
struct exynos_iommu_owner *owner = dev->archdata.iommu;
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
|
||||
struct sysmmu_drvdata *data;
|
||||
|
||||
if (!has_sysmmu(dev))
|
||||
|
|
@ -1287,8 +1287,8 @@ static void exynos_iommu_release_device(struct device *dev)
|
|||
static int exynos_iommu_of_xlate(struct device *dev,
|
||||
struct of_phandle_args *spec)
|
||||
{
|
||||
struct exynos_iommu_owner *owner = dev->archdata.iommu;
|
||||
struct platform_device *sysmmu = of_find_device_by_node(spec->np);
|
||||
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
|
||||
struct sysmmu_drvdata *data, *entry;
|
||||
|
||||
if (!sysmmu)
|
||||
|
|
@ -1305,7 +1305,7 @@ static int exynos_iommu_of_xlate(struct device *dev,
|
|||
|
||||
INIT_LIST_HEAD(&owner->controllers);
|
||||
mutex_init(&owner->rpm_lock);
|
||||
dev->archdata.iommu = owner;
|
||||
dev_iommu_priv_set(dev, owner);
|
||||
}
|
||||
|
||||
list_for_each_entry(entry, &owner->controllers, owner_node)
|
||||
|
|
|
|||
|
|
@ -1174,10 +1174,7 @@ static int fsl_pamu_probe(struct platform_device *pdev)
|
|||
if (irq != NO_IRQ)
|
||||
free_irq(irq, data);
|
||||
|
||||
if (data) {
|
||||
memset(data, 0, sizeof(struct pamu_isr_data));
|
||||
kfree(data);
|
||||
}
|
||||
kzfree(data);
|
||||
|
||||
if (pamu_regs)
|
||||
iounmap(pamu_regs);
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ static void remove_device_ref(struct device_domain_info *info, u32 win_cnt)
|
|||
pamu_disable_liodn(info->liodn);
|
||||
spin_unlock_irqrestore(&iommu_lock, flags);
|
||||
spin_lock_irqsave(&device_domain_lock, flags);
|
||||
info->dev->archdata.iommu_domain = NULL;
|
||||
dev_iommu_priv_set(info->dev, NULL);
|
||||
kmem_cache_free(iommu_devinfo_cache, info);
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
}
|
||||
|
|
@ -352,7 +352,7 @@ static void attach_device(struct fsl_dma_domain *dma_domain, int liodn, struct d
|
|||
* Check here if the device is already attached to domain or not.
|
||||
* If the device is already attached to a domain detach it.
|
||||
*/
|
||||
old_domain_info = dev->archdata.iommu_domain;
|
||||
old_domain_info = dev_iommu_priv_get(dev);
|
||||
if (old_domain_info && old_domain_info->domain != dma_domain) {
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
detach_device(dev, old_domain_info->domain);
|
||||
|
|
@ -371,8 +371,8 @@ static void attach_device(struct fsl_dma_domain *dma_domain, int liodn, struct d
|
|||
* the info for the first LIODN as all
|
||||
* LIODNs share the same domain
|
||||
*/
|
||||
if (!dev->archdata.iommu_domain)
|
||||
dev->archdata.iommu_domain = info;
|
||||
if (!dev_iommu_priv_get(dev))
|
||||
dev_iommu_priv_set(dev, info);
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
}
|
||||
|
||||
|
|
|
|||
87
drivers/iommu/intel/Kconfig
Normal file
87
drivers/iommu/intel/Kconfig
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Intel IOMMU support
|
||||
config DMAR_TABLE
|
||||
bool
|
||||
|
||||
config INTEL_IOMMU
|
||||
bool "Support for Intel IOMMU using DMA Remapping Devices"
|
||||
depends on PCI_MSI && ACPI && (X86 || IA64)
|
||||
select DMA_OPS
|
||||
select IOMMU_API
|
||||
select IOMMU_IOVA
|
||||
select NEED_DMA_MAP_STATE
|
||||
select DMAR_TABLE
|
||||
select SWIOTLB
|
||||
select IOASID
|
||||
help
|
||||
DMA remapping (DMAR) devices support enables independent address
|
||||
translations for Direct Memory Access (DMA) from devices.
|
||||
These DMA remapping devices are reported via ACPI tables
|
||||
and include PCI device scope covered by these DMA
|
||||
remapping devices.
|
||||
|
||||
config INTEL_IOMMU_DEBUGFS
|
||||
bool "Export Intel IOMMU internals in Debugfs"
|
||||
depends on INTEL_IOMMU && IOMMU_DEBUGFS
|
||||
help
|
||||
!!!WARNING!!!
|
||||
|
||||
DO NOT ENABLE THIS OPTION UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!!!
|
||||
|
||||
Expose Intel IOMMU internals in Debugfs.
|
||||
|
||||
This option is -NOT- intended for production environments, and should
|
||||
only be enabled for debugging Intel IOMMU.
|
||||
|
||||
config INTEL_IOMMU_SVM
|
||||
bool "Support for Shared Virtual Memory with Intel IOMMU"
|
||||
depends on INTEL_IOMMU && X86_64
|
||||
select PCI_PASID
|
||||
select PCI_PRI
|
||||
select MMU_NOTIFIER
|
||||
select IOASID
|
||||
help
|
||||
Shared Virtual Memory (SVM) provides a facility for devices
|
||||
to access DMA resources through process address space by
|
||||
means of a Process Address Space ID (PASID).
|
||||
|
||||
config INTEL_IOMMU_DEFAULT_ON
|
||||
def_bool y
|
||||
prompt "Enable Intel DMA Remapping Devices by default"
|
||||
depends on INTEL_IOMMU
|
||||
help
|
||||
Selecting this option will enable a DMAR device at boot time if
|
||||
one is found. If this option is not selected, DMAR support can
|
||||
be enabled by passing intel_iommu=on to the kernel.
|
||||
|
||||
config INTEL_IOMMU_BROKEN_GFX_WA
|
||||
bool "Workaround broken graphics drivers (going away soon)"
|
||||
depends on INTEL_IOMMU && BROKEN && X86
|
||||
help
|
||||
Current Graphics drivers tend to use physical address
|
||||
for DMA and avoid using DMA APIs. Setting this config
|
||||
option permits the IOMMU driver to set a unity map for
|
||||
all the OS-visible memory. Hence the driver can continue
|
||||
to use physical addresses for DMA, at least until this
|
||||
option is removed in the 2.6.32 kernel.
|
||||
|
||||
config INTEL_IOMMU_FLOPPY_WA
|
||||
def_bool y
|
||||
depends on INTEL_IOMMU && X86
|
||||
help
|
||||
Floppy disk drivers are known to bypass DMA API calls
|
||||
thereby failing to work when IOMMU is enabled. This
|
||||
workaround will setup a 1:1 mapping for the first
|
||||
16MiB to make floppy (an ISA device) work.
|
||||
|
||||
config INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON
|
||||
bool "Enable Intel IOMMU scalable mode by default"
|
||||
depends on INTEL_IOMMU
|
||||
help
|
||||
Selecting this option will enable by default the scalable mode if
|
||||
hardware presents the capability. The scalable mode is defined in
|
||||
VT-d 3.0. The scalable mode capability could be checked by reading
|
||||
/sys/devices/virtual/iommu/dmar*/intel-iommu/ecap. If this option
|
||||
is not selected, scalable mode support could also be enabled by
|
||||
passing intel_iommu=sm_on to the kernel. If not sure, please use
|
||||
the default value.
|
||||
7
drivers/iommu/intel/Makefile
Normal file
7
drivers/iommu/intel/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += iommu.o pasid.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += trace.o
|
||||
obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += debugfs.o
|
||||
obj-$(CONFIG_INTEL_IOMMU_SVM) += svm.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include <asm/irq_remapping.h>
|
||||
|
||||
#include "intel-pasid.h"
|
||||
#include "pasid.h"
|
||||
|
||||
struct tbl_walk {
|
||||
u16 bus;
|
||||
|
|
|
|||
|
|
@ -1102,6 +1102,7 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
|
|||
}
|
||||
|
||||
drhd->iommu = iommu;
|
||||
iommu->drhd = drhd;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -1438,8 +1439,7 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
|
|||
|
||||
/* PASID-based device IOTLB Invalidate */
|
||||
void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
|
||||
u32 pasid, u16 qdep, u64 addr,
|
||||
unsigned int size_order, u64 granu)
|
||||
u32 pasid, u16 qdep, u64 addr, unsigned int size_order)
|
||||
{
|
||||
unsigned long mask = 1UL << (VTD_PAGE_SHIFT + size_order - 1);
|
||||
struct qi_desc desc = {.qw1 = 0, .qw2 = 0, .qw3 = 0};
|
||||
|
|
@ -1447,7 +1447,6 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
|
|||
desc.qw0 = QI_DEV_EIOTLB_PASID(pasid) | QI_DEV_EIOTLB_SID(sid) |
|
||||
QI_DEV_EIOTLB_QDEP(qdep) | QI_DEIOTLB_TYPE |
|
||||
QI_DEV_IOTLB_PFSID(pfsid);
|
||||
desc.qw1 = QI_DEV_EIOTLB_GLOB(granu);
|
||||
|
||||
/*
|
||||
* If S bit is 0, we only flush a single page. If S bit is set,
|
||||
|
|
@ -1458,9 +1457,26 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
|
|||
* Max Invs Pending (MIP) is set to 0 for now until we have DIT in
|
||||
* ECAP.
|
||||
*/
|
||||
desc.qw1 |= addr & ~mask;
|
||||
if (size_order)
|
||||
if (addr & GENMASK_ULL(size_order + VTD_PAGE_SHIFT, 0))
|
||||
pr_warn_ratelimited("Invalidate non-aligned address %llx, order %d\n",
|
||||
addr, size_order);
|
||||
|
||||
/* Take page address */
|
||||
desc.qw1 = QI_DEV_EIOTLB_ADDR(addr);
|
||||
|
||||
if (size_order) {
|
||||
/*
|
||||
* Existing 0s in address below size_order may be the least
|
||||
* significant bit, we must set them to 1s to avoid having
|
||||
* smaller size than desired.
|
||||
*/
|
||||
desc.qw1 |= GENMASK_ULL(size_order + VTD_PAGE_SHIFT - 1,
|
||||
VTD_PAGE_SHIFT);
|
||||
/* Clear size_order bit to indicate size */
|
||||
desc.qw1 &= ~mask;
|
||||
/* Set the S bit to indicate flushing more than 1 page */
|
||||
desc.qw1 |= QI_DEV_EIOTLB_SIZE;
|
||||
}
|
||||
|
||||
qi_submit_sync(iommu, &desc, 1, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
#include <trace/events/intel_iommu.h>
|
||||
|
||||
#include "../irq_remapping.h"
|
||||
#include "intel-pasid.h"
|
||||
#include "pasid.h"
|
||||
|
||||
#define ROOT_SIZE VTD_PAGE_SIZE
|
||||
#define CONTEXT_SIZE VTD_PAGE_SIZE
|
||||
|
|
@ -356,6 +356,7 @@ static int intel_iommu_strict;
|
|||
static int intel_iommu_superpage = 1;
|
||||
static int iommu_identity_mapping;
|
||||
static int intel_no_bounce;
|
||||
static int iommu_skip_te_disable;
|
||||
|
||||
#define IDENTMAP_GFX 2
|
||||
#define IDENTMAP_AZALIA 4
|
||||
|
|
@ -372,7 +373,7 @@ struct device_domain_info *get_domain_info(struct device *dev)
|
|||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
info = dev->archdata.iommu;
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (unlikely(info == DUMMY_DEVICE_DOMAIN_INFO ||
|
||||
info == DEFER_DEVICE_DOMAIN_INFO))
|
||||
return NULL;
|
||||
|
|
@ -743,12 +744,12 @@ struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
|
|||
|
||||
static int iommu_dummy(struct device *dev)
|
||||
{
|
||||
return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
|
||||
return dev_iommu_priv_get(dev) == DUMMY_DEVICE_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
static bool attach_deferred(struct device *dev)
|
||||
{
|
||||
return dev->archdata.iommu == DEFER_DEVICE_DOMAIN_INFO;
|
||||
return dev_iommu_priv_get(dev) == DEFER_DEVICE_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -778,16 +779,16 @@ is_downstream_to_pci_bridge(struct device *dev, struct device *bridge)
|
|||
return false;
|
||||
}
|
||||
|
||||
static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
||||
struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd = NULL;
|
||||
struct pci_dev *pdev = NULL;
|
||||
struct intel_iommu *iommu;
|
||||
struct device *tmp;
|
||||
struct pci_dev *pdev = NULL;
|
||||
u16 segment = 0;
|
||||
int i;
|
||||
|
||||
if (iommu_dummy(dev))
|
||||
if (!dev || iommu_dummy(dev))
|
||||
return NULL;
|
||||
|
||||
if (dev_is_pci(dev)) {
|
||||
|
|
@ -818,8 +819,10 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
|
|||
if (pdev && pdev->is_virtfn)
|
||||
goto got_pdev;
|
||||
|
||||
*bus = drhd->devices[i].bus;
|
||||
*devfn = drhd->devices[i].devfn;
|
||||
if (bus && devfn) {
|
||||
*bus = drhd->devices[i].bus;
|
||||
*devfn = drhd->devices[i].devfn;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -829,8 +832,10 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
|
|||
|
||||
if (pdev && drhd->include_all) {
|
||||
got_pdev:
|
||||
*bus = pdev->bus->number;
|
||||
*devfn = pdev->devfn;
|
||||
if (bus && devfn) {
|
||||
*bus = pdev->bus->number;
|
||||
*devfn = pdev->devfn;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -1629,6 +1634,10 @@ static void iommu_disable_translation(struct intel_iommu *iommu)
|
|||
u32 sts;
|
||||
unsigned long flag;
|
||||
|
||||
if (iommu_skip_te_disable && iommu->drhd->gfx_dedicated &&
|
||||
(cap_read_drain(iommu->cap) || cap_write_drain(iommu->cap)))
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&iommu->register_lock, flag);
|
||||
iommu->gcmd &= ~DMA_GCMD_TE;
|
||||
writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
|
||||
|
|
@ -2420,7 +2429,7 @@ static inline void unlink_domain_info(struct device_domain_info *info)
|
|||
list_del(&info->link);
|
||||
list_del(&info->global);
|
||||
if (info->dev)
|
||||
info->dev->archdata.iommu = NULL;
|
||||
dev_iommu_priv_set(info->dev, NULL);
|
||||
}
|
||||
|
||||
static void domain_remove_dev_info(struct dmar_domain *domain)
|
||||
|
|
@ -2453,7 +2462,7 @@ static void do_deferred_attach(struct device *dev)
|
|||
{
|
||||
struct iommu_domain *domain;
|
||||
|
||||
dev->archdata.iommu = NULL;
|
||||
dev_iommu_priv_set(dev, NULL);
|
||||
domain = iommu_get_domain_for_dev(dev);
|
||||
if (domain)
|
||||
intel_iommu_attach_device(domain, dev);
|
||||
|
|
@ -2599,7 +2608,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
|
|||
list_add(&info->link, &domain->devices);
|
||||
list_add(&info->global, &device_domain_list);
|
||||
if (dev)
|
||||
dev->archdata.iommu = info;
|
||||
dev_iommu_priv_set(dev, info);
|
||||
spin_unlock_irqrestore(&device_domain_lock, flags);
|
||||
|
||||
/* PASID table is mandatory for a PCI device in scalable mode. */
|
||||
|
|
@ -4004,7 +4013,7 @@ static void quirk_ioat_snb_local_iommu(struct pci_dev *pdev)
|
|||
if (!drhd || drhd->reg_base_addr - vtbar != 0xa000) {
|
||||
pr_warn_once(FW_BUG "BIOS assigned incorrect VT-d unit for Intel(R) QuickData Technology device\n");
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
|
||||
pdev->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
|
||||
dev_iommu_priv_set(&pdev->dev, DUMMY_DEVICE_DOMAIN_INFO);
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB, quirk_ioat_snb_local_iommu);
|
||||
|
|
@ -4039,11 +4048,12 @@ static void __init init_no_remapping_devices(void)
|
|||
|
||||
/* This IOMMU has *only* gfx devices. Either bypass it or
|
||||
set the gfx_mapped flag, as appropriate */
|
||||
drhd->gfx_dedicated = 1;
|
||||
if (!dmar_map_gfx) {
|
||||
drhd->ignored = 1;
|
||||
for_each_active_dev_scope(drhd->devices,
|
||||
drhd->devices_cnt, i, dev)
|
||||
dev->archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
|
||||
dev_iommu_priv_set(dev, DUMMY_DEVICE_DOMAIN_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5146,11 +5156,10 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
|
|||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
u8 bus, devfn;
|
||||
unsigned long flags;
|
||||
struct intel_iommu *iommu;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -5236,9 +5245,8 @@ static int prepare_domain_attach_device(struct iommu_domain *domain,
|
|||
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
|
||||
struct intel_iommu *iommu;
|
||||
int addr_width;
|
||||
u8 bus, devfn;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -5416,7 +5424,7 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
|
|||
sid = PCI_DEVID(bus, devfn);
|
||||
|
||||
/* Size is only valid in address selective invalidation */
|
||||
if (inv_info->granularity != IOMMU_INV_GRANU_PASID)
|
||||
if (inv_info->granularity == IOMMU_INV_GRANU_ADDR)
|
||||
size = to_vtd_size(inv_info->addr_info.granule_size,
|
||||
inv_info->addr_info.nb_granules);
|
||||
|
||||
|
|
@ -5425,6 +5433,7 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
|
|||
IOMMU_CACHE_INV_TYPE_NR) {
|
||||
int granu = 0;
|
||||
u64 pasid = 0;
|
||||
u64 addr = 0;
|
||||
|
||||
granu = to_vtd_granularity(cache_type, inv_info->granularity);
|
||||
if (granu == -EINVAL) {
|
||||
|
|
@ -5446,13 +5455,12 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
|
|||
|
||||
switch (BIT(cache_type)) {
|
||||
case IOMMU_CACHE_INV_TYPE_IOTLB:
|
||||
/* HW will ignore LSB bits based on address mask */
|
||||
if (inv_info->granularity == IOMMU_INV_GRANU_ADDR &&
|
||||
size &&
|
||||
(inv_info->addr_info.addr & ((BIT(VTD_PAGE_SHIFT + size)) - 1))) {
|
||||
pr_err_ratelimited("Address out of range, 0x%llx, size order %llu\n",
|
||||
pr_err_ratelimited("User address not aligned, 0x%llx, size order %llu\n",
|
||||
inv_info->addr_info.addr, size);
|
||||
ret = -ERANGE;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -5464,25 +5472,35 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
|
|||
(granu == QI_GRAN_NONG_PASID) ? -1 : 1 << size,
|
||||
inv_info->addr_info.flags & IOMMU_INV_ADDR_FLAGS_LEAF);
|
||||
|
||||
if (!info->ats_enabled)
|
||||
break;
|
||||
/*
|
||||
* Always flush device IOTLB if ATS is enabled. vIOMMU
|
||||
* in the guest may assume IOTLB flush is inclusive,
|
||||
* which is more efficient.
|
||||
*/
|
||||
if (info->ats_enabled)
|
||||
qi_flush_dev_iotlb_pasid(iommu, sid,
|
||||
info->pfsid, pasid,
|
||||
info->ats_qdep,
|
||||
inv_info->addr_info.addr,
|
||||
size, granu);
|
||||
break;
|
||||
fallthrough;
|
||||
case IOMMU_CACHE_INV_TYPE_DEV_IOTLB:
|
||||
/*
|
||||
* PASID based device TLB invalidation does not support
|
||||
* IOMMU_INV_GRANU_PASID granularity but only supports
|
||||
* IOMMU_INV_GRANU_ADDR.
|
||||
* The equivalent of that is we set the size to be the
|
||||
* entire range of 64 bit. User only provides PASID info
|
||||
* without address info. So we set addr to 0.
|
||||
*/
|
||||
if (inv_info->granularity == IOMMU_INV_GRANU_PASID) {
|
||||
size = 64 - VTD_PAGE_SHIFT;
|
||||
addr = 0;
|
||||
} else if (inv_info->granularity == IOMMU_INV_GRANU_ADDR) {
|
||||
addr = inv_info->addr_info.addr;
|
||||
}
|
||||
|
||||
if (info->ats_enabled)
|
||||
qi_flush_dev_iotlb_pasid(iommu, sid,
|
||||
info->pfsid, pasid,
|
||||
info->ats_qdep,
|
||||
inv_info->addr_info.addr,
|
||||
size, granu);
|
||||
info->ats_qdep, addr,
|
||||
size);
|
||||
else
|
||||
pr_warn_ratelimited("Passdown device IOTLB flush w/o ATS!\n");
|
||||
break;
|
||||
|
|
@ -5658,14 +5676,13 @@ static bool intel_iommu_capable(enum iommu_cap cap)
|
|||
static struct iommu_device *intel_iommu_probe_device(struct device *dev)
|
||||
{
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (translation_pre_enabled(iommu))
|
||||
dev->archdata.iommu = DEFER_DEVICE_DOMAIN_INFO;
|
||||
dev_iommu_priv_set(dev, DEFER_DEVICE_DOMAIN_INFO);
|
||||
|
||||
return &iommu->iommu;
|
||||
}
|
||||
|
|
@ -5673,9 +5690,8 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
|
|||
static void intel_iommu_release_device(struct device *dev)
|
||||
{
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu)
|
||||
return;
|
||||
|
||||
|
|
@ -5825,37 +5841,14 @@ static struct iommu_group *intel_iommu_device_group(struct device *dev)
|
|||
return generic_device_group(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||
struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)
|
||||
{
|
||||
struct intel_iommu *iommu;
|
||||
u8 bus, devfn;
|
||||
|
||||
if (iommu_dummy(dev)) {
|
||||
dev_warn(dev,
|
||||
"No IOMMU translation for device; cannot enable SVM\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if ((!iommu)) {
|
||||
dev_err(dev, "No IOMMU for device; cannot enable SVM\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iommu;
|
||||
}
|
||||
#endif /* CONFIG_INTEL_IOMMU_SVM */
|
||||
|
||||
static int intel_iommu_enable_auxd(struct device *dev)
|
||||
{
|
||||
struct device_domain_info *info;
|
||||
struct intel_iommu *iommu;
|
||||
unsigned long flags;
|
||||
u8 bus, devfn;
|
||||
int ret;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu || dmar_disabled)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -6080,6 +6073,7 @@ const struct iommu_ops intel_iommu_ops = {
|
|||
.sva_bind = intel_svm_bind,
|
||||
.sva_unbind = intel_svm_unbind,
|
||||
.sva_get_pasid = intel_svm_get_pasid,
|
||||
.page_response = intel_svm_page_response,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -6182,6 +6176,27 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_calpella_no_shadow_g
|
|||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0062, quirk_calpella_no_shadow_gtt);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x006a, quirk_calpella_no_shadow_gtt);
|
||||
|
||||
static void quirk_igfx_skip_te_disable(struct pci_dev *dev)
|
||||
{
|
||||
unsigned short ver;
|
||||
|
||||
if (!IS_GFX_DEVICE(dev))
|
||||
return;
|
||||
|
||||
ver = (dev->device >> 8) & 0xff;
|
||||
if (ver != 0x45 && ver != 0x46 && ver != 0x4c &&
|
||||
ver != 0x4e && ver != 0x8a && ver != 0x98 &&
|
||||
ver != 0x9a)
|
||||
return;
|
||||
|
||||
if (risky_device(dev))
|
||||
return;
|
||||
|
||||
pci_info(dev, "Skip IOMMU disabling for graphics\n");
|
||||
iommu_skip_te_disable = 1;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_igfx_skip_te_disable);
|
||||
|
||||
/* On Tylersburg chipsets, some BIOSes have been known to enable the
|
||||
ISOCH DMAR unit for the Azalia sound device, but not give it any
|
||||
TLB entries, which causes it to deadlock. Check for that. We do
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#include <linux/pci-ats.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "intel-pasid.h"
|
||||
#include "pasid.h"
|
||||
|
||||
/*
|
||||
* Intel IOMMU system wide PASID name space:
|
||||
|
|
@ -486,7 +486,16 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,
|
|||
qdep = info->ats_qdep;
|
||||
pfsid = info->pfsid;
|
||||
|
||||
qi_flush_dev_iotlb(iommu, sid, pfsid, qdep, 0, 64 - VTD_PAGE_SHIFT);
|
||||
/*
|
||||
* When PASID 0 is used, it indicates RID2PASID(DMA request w/o PASID),
|
||||
* devTLB flush w/o PASID should be used. For non-zero PASID under
|
||||
* SVA usage, device could do DMA with multiple PASIDs. It is more
|
||||
* efficient to flush devTLB specific to the PASID.
|
||||
*/
|
||||
if (pasid == PASID_RID2PASID)
|
||||
qi_flush_dev_iotlb(iommu, sid, pfsid, qdep, 0, 64 - VTD_PAGE_SHIFT);
|
||||
else
|
||||
qi_flush_dev_iotlb_pasid(iommu, sid, pfsid, pasid, qdep, 0, 64 - VTD_PAGE_SHIFT);
|
||||
}
|
||||
|
||||
void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* intel-pasid.h - PASID idr, table and entry header
|
||||
* pasid.h - PASID idr, table and entry header
|
||||
*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/ioasid.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include "intel-pasid.h"
|
||||
#include "pasid.h"
|
||||
|
||||
static irqreturn_t prq_event_thread(int irq, void *d);
|
||||
static void intel_svm_drain_prq(struct device *dev, int pasid);
|
||||
|
|
@ -228,13 +228,57 @@ static LIST_HEAD(global_svm_list);
|
|||
list_for_each_entry((sdev), &(svm)->devs, list) \
|
||||
if ((d) != (sdev)->dev) {} else
|
||||
|
||||
static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
|
||||
struct intel_svm **rsvm,
|
||||
struct intel_svm_dev **rsdev)
|
||||
{
|
||||
struct intel_svm_dev *d, *sdev = NULL;
|
||||
struct intel_svm *svm;
|
||||
|
||||
/* The caller should hold the pasid_mutex lock */
|
||||
if (WARN_ON(!mutex_is_locked(&pasid_mutex)))
|
||||
return -EINVAL;
|
||||
|
||||
if (pasid == INVALID_IOASID || pasid >= PASID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
svm = ioasid_find(NULL, pasid, NULL);
|
||||
if (IS_ERR(svm))
|
||||
return PTR_ERR(svm);
|
||||
|
||||
if (!svm)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If we found svm for the PASID, there must be at least one device
|
||||
* bond.
|
||||
*/
|
||||
if (WARN_ON(list_empty(&svm->devs)))
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(d, &svm->devs, list) {
|
||||
if (d->dev == dev) {
|
||||
sdev = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
out:
|
||||
*rsvm = svm;
|
||||
*rsdev = sdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
|
||||
struct iommu_gpasid_bind_data *data)
|
||||
{
|
||||
struct intel_iommu *iommu = intel_svm_device_to_iommu(dev);
|
||||
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
|
||||
struct intel_svm_dev *sdev = NULL;
|
||||
struct dmar_domain *dmar_domain;
|
||||
struct intel_svm_dev *sdev;
|
||||
struct intel_svm *svm;
|
||||
struct intel_svm *svm = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(!iommu) || !data)
|
||||
|
|
@ -261,39 +305,23 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
|
|||
dmar_domain = to_dmar_domain(domain);
|
||||
|
||||
mutex_lock(&pasid_mutex);
|
||||
svm = ioasid_find(NULL, data->hpasid, NULL);
|
||||
if (IS_ERR(svm)) {
|
||||
ret = PTR_ERR(svm);
|
||||
ret = pasid_to_svm_sdev(dev, data->hpasid, &svm, &sdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (sdev) {
|
||||
/*
|
||||
* Do not allow multiple bindings of the same device-PASID since
|
||||
* there is only one SL page tables per PASID. We may revisit
|
||||
* once sharing PGD across domains are supported.
|
||||
*/
|
||||
dev_warn_ratelimited(dev, "Already bound with PASID %u\n",
|
||||
svm->pasid);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (svm) {
|
||||
/*
|
||||
* If we found svm for the PASID, there must be at
|
||||
* least one device bond, otherwise svm should be freed.
|
||||
*/
|
||||
if (WARN_ON(list_empty(&svm->devs))) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_svm_dev(sdev, svm, dev) {
|
||||
/*
|
||||
* For devices with aux domains, we should allow
|
||||
* multiple bind calls with the same PASID and pdev.
|
||||
*/
|
||||
if (iommu_dev_feature_enabled(dev,
|
||||
IOMMU_DEV_FEAT_AUX)) {
|
||||
sdev->users++;
|
||||
} else {
|
||||
dev_warn_ratelimited(dev,
|
||||
"Already bound with PASID %u\n",
|
||||
svm->pasid);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (!svm) {
|
||||
/* We come here when PASID has never been bond to a device. */
|
||||
svm = kzalloc(sizeof(*svm), GFP_KERNEL);
|
||||
if (!svm) {
|
||||
|
|
@ -373,28 +401,20 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
|
|||
|
||||
int intel_svm_unbind_gpasid(struct device *dev, int pasid)
|
||||
{
|
||||
struct intel_iommu *iommu = intel_svm_device_to_iommu(dev);
|
||||
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
|
||||
struct intel_svm_dev *sdev;
|
||||
struct intel_svm *svm;
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!iommu))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pasid_mutex);
|
||||
svm = ioasid_find(NULL, pasid, NULL);
|
||||
if (!svm) {
|
||||
ret = -EINVAL;
|
||||
ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (IS_ERR(svm)) {
|
||||
ret = PTR_ERR(svm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_svm_dev(sdev, svm, dev) {
|
||||
ret = 0;
|
||||
if (sdev) {
|
||||
if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
|
||||
sdev->users--;
|
||||
if (!sdev->users) {
|
||||
|
|
@ -418,7 +438,6 @@ int intel_svm_unbind_gpasid(struct device *dev, int pasid)
|
|||
kfree(svm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&pasid_mutex);
|
||||
|
|
@ -430,7 +449,7 @@ static int
|
|||
intel_svm_bind_mm(struct device *dev, int flags, struct svm_dev_ops *ops,
|
||||
struct mm_struct *mm, struct intel_svm_dev **sd)
|
||||
{
|
||||
struct intel_iommu *iommu = intel_svm_device_to_iommu(dev);
|
||||
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
|
||||
struct device_domain_info *info;
|
||||
struct intel_svm_dev *sdev;
|
||||
struct intel_svm *svm = NULL;
|
||||
|
|
@ -596,7 +615,7 @@ intel_svm_bind_mm(struct device *dev, int flags, struct svm_dev_ops *ops,
|
|||
if (sd)
|
||||
*sd = sdev;
|
||||
ret = 0;
|
||||
out:
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -608,21 +627,15 @@ static int intel_svm_unbind_mm(struct device *dev, int pasid)
|
|||
struct intel_svm *svm;
|
||||
int ret = -EINVAL;
|
||||
|
||||
iommu = intel_svm_device_to_iommu(dev);
|
||||
iommu = device_to_iommu(dev, NULL, NULL);
|
||||
if (!iommu)
|
||||
goto out;
|
||||
|
||||
svm = ioasid_find(NULL, pasid, NULL);
|
||||
if (!svm)
|
||||
ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (IS_ERR(svm)) {
|
||||
ret = PTR_ERR(svm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_svm_dev(sdev, svm, dev) {
|
||||
ret = 0;
|
||||
if (sdev) {
|
||||
sdev->users--;
|
||||
if (!sdev->users) {
|
||||
list_del_rcu(&sdev->list);
|
||||
|
|
@ -651,10 +664,8 @@ static int intel_svm_unbind_mm(struct device *dev, int pasid)
|
|||
kfree(svm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
out:
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -800,8 +811,63 @@ static void intel_svm_drain_prq(struct device *dev, int pasid)
|
|||
}
|
||||
}
|
||||
|
||||
static int prq_to_iommu_prot(struct page_req_dsc *req)
|
||||
{
|
||||
int prot = 0;
|
||||
|
||||
if (req->rd_req)
|
||||
prot |= IOMMU_FAULT_PERM_READ;
|
||||
if (req->wr_req)
|
||||
prot |= IOMMU_FAULT_PERM_WRITE;
|
||||
if (req->exe_req)
|
||||
prot |= IOMMU_FAULT_PERM_EXEC;
|
||||
if (req->pm_req)
|
||||
prot |= IOMMU_FAULT_PERM_PRIV;
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_svm_prq_report(struct device *dev, struct page_req_dsc *desc)
|
||||
{
|
||||
struct iommu_fault_event event;
|
||||
|
||||
if (!dev || !dev_is_pci(dev))
|
||||
return -ENODEV;
|
||||
|
||||
/* Fill in event data for device specific processing */
|
||||
memset(&event, 0, sizeof(struct iommu_fault_event));
|
||||
event.fault.type = IOMMU_FAULT_PAGE_REQ;
|
||||
event.fault.prm.addr = desc->addr;
|
||||
event.fault.prm.pasid = desc->pasid;
|
||||
event.fault.prm.grpid = desc->prg_index;
|
||||
event.fault.prm.perm = prq_to_iommu_prot(desc);
|
||||
|
||||
if (desc->lpig)
|
||||
event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
|
||||
if (desc->pasid_present) {
|
||||
event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
|
||||
event.fault.prm.flags |= IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID;
|
||||
}
|
||||
if (desc->priv_data_present) {
|
||||
/*
|
||||
* Set last page in group bit if private data is present,
|
||||
* page response is required as it does for LPIG.
|
||||
* iommu_report_device_fault() doesn't understand this vendor
|
||||
* specific requirement thus we set last_page as a workaround.
|
||||
*/
|
||||
event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
|
||||
event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA;
|
||||
memcpy(event.fault.prm.private_data, desc->priv_data,
|
||||
sizeof(desc->priv_data));
|
||||
}
|
||||
|
||||
return iommu_report_device_fault(dev, &event);
|
||||
}
|
||||
|
||||
static irqreturn_t prq_event_thread(int irq, void *d)
|
||||
{
|
||||
struct intel_svm_dev *sdev = NULL;
|
||||
struct intel_iommu *iommu = d;
|
||||
struct intel_svm *svm = NULL;
|
||||
int head, tail, handled = 0;
|
||||
|
|
@ -813,7 +879,6 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK;
|
||||
head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK;
|
||||
while (head != tail) {
|
||||
struct intel_svm_dev *sdev;
|
||||
struct vm_area_struct *vma;
|
||||
struct page_req_dsc *req;
|
||||
struct qi_desc resp;
|
||||
|
|
@ -849,6 +914,20 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
}
|
||||
}
|
||||
|
||||
if (!sdev || sdev->sid != req->rid) {
|
||||
struct intel_svm_dev *t;
|
||||
|
||||
sdev = NULL;
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(t, &svm->devs, list) {
|
||||
if (t->sid == req->rid) {
|
||||
sdev = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
result = QI_RESP_INVALID;
|
||||
/* Since we're using init_mm.pgd directly, we should never take
|
||||
* any faults on kernel addresses. */
|
||||
|
|
@ -859,6 +938,17 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
if (!is_canonical_address(address))
|
||||
goto bad_req;
|
||||
|
||||
/*
|
||||
* If prq is to be handled outside iommu driver via receiver of
|
||||
* the fault notifiers, we skip the page response here.
|
||||
*/
|
||||
if (svm->flags & SVM_FLAG_GUEST_MODE) {
|
||||
if (sdev && !intel_svm_prq_report(sdev->dev, req))
|
||||
goto prq_advance;
|
||||
else
|
||||
goto bad_req;
|
||||
}
|
||||
|
||||
/* If the mm is already defunct, don't handle faults. */
|
||||
if (!mmget_not_zero(svm->mm))
|
||||
goto bad_req;
|
||||
|
|
@ -877,24 +967,11 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
goto invalid;
|
||||
|
||||
result = QI_RESP_SUCCESS;
|
||||
invalid:
|
||||
invalid:
|
||||
mmap_read_unlock(svm->mm);
|
||||
mmput(svm->mm);
|
||||
bad_req:
|
||||
/* Accounting for major/minor faults? */
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sdev, &svm->devs, list) {
|
||||
if (sdev->sid == req->rid)
|
||||
break;
|
||||
}
|
||||
/* Other devices can go away, but the drivers are not permitted
|
||||
* to unbind while any page faults might be in flight. So it's
|
||||
* OK to drop the 'lock' here now we have it. */
|
||||
rcu_read_unlock();
|
||||
|
||||
if (WARN_ON(&sdev->list == &svm->devs))
|
||||
sdev = NULL;
|
||||
|
||||
bad_req:
|
||||
WARN_ON(!sdev);
|
||||
if (sdev && sdev->ops && sdev->ops->fault_cb) {
|
||||
int rwxp = (req->rd_req << 3) | (req->wr_req << 2) |
|
||||
(req->exe_req << 1) | (req->pm_req);
|
||||
|
|
@ -905,7 +982,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
and these can be NULL. Do not use them below this point! */
|
||||
sdev = NULL;
|
||||
svm = NULL;
|
||||
no_pasid:
|
||||
no_pasid:
|
||||
if (req->lpig || req->priv_data_present) {
|
||||
/*
|
||||
* Per VT-d spec. v3.0 ch7.7, system software must
|
||||
|
|
@ -930,6 +1007,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
resp.qw3 = 0;
|
||||
qi_submit_sync(iommu, &resp, 1, 0);
|
||||
}
|
||||
prq_advance:
|
||||
head = (head + sizeof(*req)) & PRQ_RING_MASK;
|
||||
}
|
||||
|
||||
|
|
@ -1000,3 +1078,102 @@ int intel_svm_get_pasid(struct iommu_sva *sva)
|
|||
|
||||
return pasid;
|
||||
}
|
||||
|
||||
int intel_svm_page_response(struct device *dev,
|
||||
struct iommu_fault_event *evt,
|
||||
struct iommu_page_response *msg)
|
||||
{
|
||||
struct iommu_fault_page_request *prm;
|
||||
struct intel_svm_dev *sdev = NULL;
|
||||
struct intel_svm *svm = NULL;
|
||||
struct intel_iommu *iommu;
|
||||
bool private_present;
|
||||
bool pasid_present;
|
||||
bool last_page;
|
||||
u8 bus, devfn;
|
||||
int ret = 0;
|
||||
u16 sid;
|
||||
|
||||
if (!dev || !dev_is_pci(dev))
|
||||
return -ENODEV;
|
||||
|
||||
iommu = device_to_iommu(dev, &bus, &devfn);
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
if (!msg || !evt)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pasid_mutex);
|
||||
|
||||
prm = &evt->fault.prm;
|
||||
sid = PCI_DEVID(bus, devfn);
|
||||
pasid_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
|
||||
private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA;
|
||||
last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
|
||||
|
||||
if (!pasid_present) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (prm->pasid == 0 || prm->pasid >= PASID_MAX) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pasid_to_svm_sdev(dev, prm->pasid, &svm, &sdev);
|
||||
if (ret || !sdev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For responses from userspace, need to make sure that the
|
||||
* pasid has been bound to its mm.
|
||||
*/
|
||||
if (svm->flags & SVM_FLAG_GUEST_MODE) {
|
||||
struct mm_struct *mm;
|
||||
|
||||
mm = get_task_mm(current);
|
||||
if (!mm) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mm != svm->mm) {
|
||||
ret = -ENODEV;
|
||||
mmput(mm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmput(mm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per VT-d spec. v3.0 ch7.7, system software must respond
|
||||
* with page group response if private data is present (PDP)
|
||||
* or last page in group (LPIG) bit is set. This is an
|
||||
* additional VT-d requirement beyond PCI ATS spec.
|
||||
*/
|
||||
if (last_page || private_present) {
|
||||
struct qi_desc desc;
|
||||
|
||||
desc.qw0 = QI_PGRP_PASID(prm->pasid) | QI_PGRP_DID(sid) |
|
||||
QI_PGRP_PASID_P(pasid_present) |
|
||||
QI_PGRP_PDP(private_present) |
|
||||
QI_PGRP_RESP_CODE(msg->code) |
|
||||
QI_PGRP_RESP_TYPE;
|
||||
desc.qw1 = QI_PGRP_IDX(prm->grpid) | QI_PGRP_LPIG(last_page);
|
||||
desc.qw2 = 0;
|
||||
desc.qw3 = 0;
|
||||
if (private_present)
|
||||
memcpy(&desc.qw2, prm->private_data,
|
||||
sizeof(prm->private_data));
|
||||
|
||||
qi_submit_sync(iommu, &desc, 1, 0);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&pasid_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ static arm_v7s_iopte arm_v7s_install_table(arm_v7s_iopte *table,
|
|||
|
||||
static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot,
|
||||
int lvl, arm_v7s_iopte *ptep)
|
||||
int lvl, arm_v7s_iopte *ptep, gfp_t gfp)
|
||||
{
|
||||
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
||||
arm_v7s_iopte pte, *cptep;
|
||||
|
|
@ -491,7 +491,7 @@ static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
|
|||
/* Grab a pointer to the next level */
|
||||
pte = READ_ONCE(*ptep);
|
||||
if (!pte) {
|
||||
cptep = __arm_v7s_alloc_table(lvl + 1, GFP_ATOMIC, data);
|
||||
cptep = __arm_v7s_alloc_table(lvl + 1, gfp, data);
|
||||
if (!cptep)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -512,11 +512,11 @@ static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
|
|||
}
|
||||
|
||||
/* Rinse, repeat */
|
||||
return __arm_v7s_map(data, iova, paddr, size, prot, lvl + 1, cptep);
|
||||
return __arm_v7s_map(data, iova, paddr, size, prot, lvl + 1, cptep, gfp);
|
||||
}
|
||||
|
||||
static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
|
||||
{
|
||||
struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
||||
struct io_pgtable *iop = &data->iop;
|
||||
|
|
@ -530,7 +530,7 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
|
|||
paddr >= (1ULL << data->iop.cfg.oas)))
|
||||
return -ERANGE;
|
||||
|
||||
ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd);
|
||||
ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd, gfp);
|
||||
/*
|
||||
* Synchronise all PTE updates for the new mapping before there's
|
||||
* a chance for anything to kick off a table walk for the new iova.
|
||||
|
|
@ -922,12 +922,12 @@ static int __init arm_v7s_do_selftests(void)
|
|||
if (ops->map(ops, iova, iova, size, IOMMU_READ |
|
||||
IOMMU_WRITE |
|
||||
IOMMU_NOEXEC |
|
||||
IOMMU_CACHE))
|
||||
IOMMU_CACHE, GFP_KERNEL))
|
||||
return __FAIL(ops);
|
||||
|
||||
/* Overlapping mappings */
|
||||
if (!ops->map(ops, iova, iova + size, size,
|
||||
IOMMU_READ | IOMMU_NOEXEC))
|
||||
IOMMU_READ | IOMMU_NOEXEC, GFP_KERNEL))
|
||||
return __FAIL(ops);
|
||||
|
||||
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
||||
|
|
@ -946,7 +946,7 @@ static int __init arm_v7s_do_selftests(void)
|
|||
return __FAIL(ops);
|
||||
|
||||
/* Remap of partial unmap */
|
||||
if (ops->map(ops, iova_start + size, size, size, IOMMU_READ))
|
||||
if (ops->map(ops, iova_start + size, size, size, IOMMU_READ, GFP_KERNEL))
|
||||
return __FAIL(ops);
|
||||
|
||||
if (ops->iova_to_phys(ops, iova_start + size + 42)
|
||||
|
|
@ -967,7 +967,7 @@ static int __init arm_v7s_do_selftests(void)
|
|||
return __FAIL(ops);
|
||||
|
||||
/* Remap full block */
|
||||
if (ops->map(ops, iova, iova, size, IOMMU_WRITE))
|
||||
if (ops->map(ops, iova, iova, size, IOMMU_WRITE, GFP_KERNEL))
|
||||
return __FAIL(ops);
|
||||
|
||||
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table,
|
|||
|
||||
static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, arm_lpae_iopte prot,
|
||||
int lvl, arm_lpae_iopte *ptep)
|
||||
int lvl, arm_lpae_iopte *ptep, gfp_t gfp)
|
||||
{
|
||||
arm_lpae_iopte *cptep, pte;
|
||||
size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
||||
|
|
@ -376,7 +376,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
|||
/* Grab a pointer to the next level */
|
||||
pte = READ_ONCE(*ptep);
|
||||
if (!pte) {
|
||||
cptep = __arm_lpae_alloc_pages(tblsz, GFP_ATOMIC, cfg);
|
||||
cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg);
|
||||
if (!cptep)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
|||
}
|
||||
|
||||
/* Rinse, repeat */
|
||||
return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep);
|
||||
return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep, gfp);
|
||||
}
|
||||
|
||||
static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
|
||||
|
|
@ -438,9 +438,6 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
|
|||
else if (prot & IOMMU_CACHE)
|
||||
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
|
||||
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
||||
else if (prot & IOMMU_SYS_CACHE_ONLY)
|
||||
pte |= (ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE
|
||||
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
||||
}
|
||||
|
||||
if (prot & IOMMU_CACHE)
|
||||
|
|
@ -461,7 +458,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
|
|||
}
|
||||
|
||||
static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int iommu_prot)
|
||||
phys_addr_t paddr, size_t size, int iommu_prot, gfp_t gfp)
|
||||
{
|
||||
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
||||
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
||||
|
|
@ -483,7 +480,7 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
|
|||
return -ERANGE;
|
||||
|
||||
prot = arm_lpae_prot_to_pte(data, iommu_prot);
|
||||
ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep);
|
||||
ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep, gfp);
|
||||
/*
|
||||
* Synchronise all PTE updates for the new mapping before there's
|
||||
* a chance for anything to kick off a table walk for the new iova.
|
||||
|
|
@ -1178,12 +1175,12 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|||
if (ops->map(ops, iova, iova, size, IOMMU_READ |
|
||||
IOMMU_WRITE |
|
||||
IOMMU_NOEXEC |
|
||||
IOMMU_CACHE))
|
||||
IOMMU_CACHE, GFP_KERNEL))
|
||||
return __FAIL(ops, i);
|
||||
|
||||
/* Overlapping mappings */
|
||||
if (!ops->map(ops, iova, iova + size, size,
|
||||
IOMMU_READ | IOMMU_NOEXEC))
|
||||
IOMMU_READ | IOMMU_NOEXEC, GFP_KERNEL))
|
||||
return __FAIL(ops, i);
|
||||
|
||||
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
||||
|
|
@ -1198,7 +1195,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|||
return __FAIL(ops, i);
|
||||
|
||||
/* Remap of partial unmap */
|
||||
if (ops->map(ops, SZ_1G + size, size, size, IOMMU_READ))
|
||||
if (ops->map(ops, SZ_1G + size, size, size, IOMMU_READ, GFP_KERNEL))
|
||||
return __FAIL(ops, i);
|
||||
|
||||
if (ops->iova_to_phys(ops, SZ_1G + size + 42) != (size + 42))
|
||||
|
|
@ -1216,7 +1213,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|||
return __FAIL(ops, i);
|
||||
|
||||
/* Remap full block */
|
||||
if (ops->map(ops, iova, iova, size, IOMMU_WRITE))
|
||||
if (ops->map(ops, iova, iova, size, IOMMU_WRITE, GFP_KERNEL))
|
||||
return __FAIL(ops, i);
|
||||
|
||||
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
||||
|
|
|
|||
|
|
@ -383,8 +383,8 @@ static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf)
|
|||
* Elements are sorted by start address and overlapping segments
|
||||
* of the same type are merged.
|
||||
*/
|
||||
int iommu_insert_resv_region(struct iommu_resv_region *new,
|
||||
struct list_head *regions)
|
||||
static int iommu_insert_resv_region(struct iommu_resv_region *new,
|
||||
struct list_head *regions)
|
||||
{
|
||||
struct iommu_resv_region *iter, *tmp, *nr, *top;
|
||||
LIST_HEAD(stack);
|
||||
|
|
@ -1185,11 +1185,12 @@ EXPORT_SYMBOL_GPL(iommu_report_device_fault);
|
|||
int iommu_page_response(struct device *dev,
|
||||
struct iommu_page_response *msg)
|
||||
{
|
||||
bool pasid_valid;
|
||||
bool needs_pasid;
|
||||
int ret = -EINVAL;
|
||||
struct iommu_fault_event *evt;
|
||||
struct iommu_fault_page_request *prm;
|
||||
struct dev_iommu *param = dev->iommu;
|
||||
bool has_pasid = msg->flags & IOMMU_PAGE_RESP_PASID_VALID;
|
||||
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
|
||||
|
||||
if (!domain || !domain->ops->page_response)
|
||||
|
|
@ -1214,14 +1215,24 @@ int iommu_page_response(struct device *dev,
|
|||
*/
|
||||
list_for_each_entry(evt, ¶m->fault_param->faults, list) {
|
||||
prm = &evt->fault.prm;
|
||||
pasid_valid = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
|
||||
|
||||
if ((pasid_valid && prm->pasid != msg->pasid) ||
|
||||
prm->grpid != msg->grpid)
|
||||
if (prm->grpid != msg->grpid)
|
||||
continue;
|
||||
|
||||
/* Sanitize the reply */
|
||||
msg->flags = pasid_valid ? IOMMU_PAGE_RESP_PASID_VALID : 0;
|
||||
/*
|
||||
* If the PASID is required, the corresponding request is
|
||||
* matched using the group ID, the PASID valid bit and the PASID
|
||||
* value. Otherwise only the group ID matches request and
|
||||
* response.
|
||||
*/
|
||||
needs_pasid = prm->flags & IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID;
|
||||
if (needs_pasid && (!has_pasid || msg->pasid != prm->pasid))
|
||||
continue;
|
||||
|
||||
if (!needs_pasid && has_pasid) {
|
||||
/* No big deal, just clear it. */
|
||||
msg->flags &= ~IOMMU_PAGE_RESP_PASID_VALID;
|
||||
msg->pasid = 0;
|
||||
}
|
||||
|
||||
ret = domain->ops->page_response(dev, evt, msg);
|
||||
list_del(&evt->list);
|
||||
|
|
@ -2168,8 +2179,8 @@ static size_t iommu_pgsize(struct iommu_domain *domain,
|
|||
return pgsize;
|
||||
}
|
||||
|
||||
int __iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
|
||||
static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
|
||||
{
|
||||
const struct iommu_ops *ops = domain->ops;
|
||||
unsigned long orig_iova = iova;
|
||||
|
|
@ -2319,9 +2330,9 @@ size_t iommu_unmap_fast(struct iommu_domain *domain,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_unmap_fast);
|
||||
|
||||
size_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot,
|
||||
gfp_t gfp)
|
||||
static size_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot,
|
||||
gfp_t gfp)
|
||||
{
|
||||
size_t len = 0, mapped = 0;
|
||||
phys_addr_t start;
|
||||
|
|
|
|||
|
|
@ -811,7 +811,9 @@ iova_magazine_free_pfns(struct iova_magazine *mag, struct iova_domain *iovad)
|
|||
for (i = 0 ; i < mag->size; ++i) {
|
||||
struct iova *iova = private_find_iova(iovad, mag->pfns[i]);
|
||||
|
||||
BUG_ON(!iova);
|
||||
if (WARN_ON(!iova))
|
||||
continue;
|
||||
|
||||
private_free_iova(iovad, iova);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* IOMMU API for Renesas VMSA-compatible IPMMU
|
||||
* Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014-2020 Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
|
@ -686,7 +686,7 @@ static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova,
|
|||
if (!domain)
|
||||
return -ENODEV;
|
||||
|
||||
return domain->iop->map(domain->iop, iova, paddr, size, prot);
|
||||
return domain->iop->map(domain->iop, iova, paddr, size, prot, gfp);
|
||||
}
|
||||
|
||||
static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova,
|
||||
|
|
@ -739,7 +739,9 @@ static const struct soc_device_attribute soc_rcar_gen3[] = {
|
|||
{ .soc_id = "r8a774a1", },
|
||||
{ .soc_id = "r8a774b1", },
|
||||
{ .soc_id = "r8a774c0", },
|
||||
{ .soc_id = "r8a774e1", },
|
||||
{ .soc_id = "r8a7795", },
|
||||
{ .soc_id = "r8a77961", },
|
||||
{ .soc_id = "r8a7796", },
|
||||
{ .soc_id = "r8a77965", },
|
||||
{ .soc_id = "r8a77970", },
|
||||
|
|
@ -751,7 +753,9 @@ static const struct soc_device_attribute soc_rcar_gen3[] = {
|
|||
static const struct soc_device_attribute soc_rcar_gen3_whitelist[] = {
|
||||
{ .soc_id = "r8a774b1", },
|
||||
{ .soc_id = "r8a774c0", },
|
||||
{ .soc_id = "r8a774e1", },
|
||||
{ .soc_id = "r8a7795", .revision = "ES3.*" },
|
||||
{ .soc_id = "r8a77961", },
|
||||
{ .soc_id = "r8a77965", },
|
||||
{ .soc_id = "r8a77990", },
|
||||
{ .soc_id = "r8a77995", },
|
||||
|
|
@ -962,12 +966,18 @@ static const struct of_device_id ipmmu_of_ids[] = {
|
|||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a774c0",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a774e1",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a7795",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a7796",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a77961",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
}, {
|
||||
.compatible = "renesas,ipmmu-r8a77965",
|
||||
.data = &ipmmu_features_rcar_gen3,
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->pgtlock, flags);
|
||||
ret = priv->iop->map(priv->iop, iova, pa, len, prot);
|
||||
ret = priv->iop->map(priv->iop, iova, pa, len, prot, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&priv->pgtlock, flags);
|
||||
|
||||
return ret;
|
||||
|
|
@ -593,14 +593,14 @@ static void insert_iommu_master(struct device *dev,
|
|||
struct msm_iommu_dev **iommu,
|
||||
struct of_phandle_args *spec)
|
||||
{
|
||||
struct msm_iommu_ctx_dev *master = dev->archdata.iommu;
|
||||
struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev);
|
||||
int sid;
|
||||
|
||||
if (list_empty(&(*iommu)->ctx_list)) {
|
||||
master = kzalloc(sizeof(*master), GFP_ATOMIC);
|
||||
master->of_node = dev->of_node;
|
||||
list_add(&master->list, &(*iommu)->ctx_list);
|
||||
dev->archdata.iommu = master;
|
||||
dev_iommu_priv_set(dev, master);
|
||||
}
|
||||
|
||||
for (sid = 0; sid < master->num_mids; sid++)
|
||||
|
|
|
|||
|
|
@ -37,12 +37,18 @@
|
|||
#define REG_MMU_INVLD_START_A 0x024
|
||||
#define REG_MMU_INVLD_END_A 0x028
|
||||
|
||||
#define REG_MMU_INV_SEL 0x038
|
||||
#define REG_MMU_INV_SEL_GEN2 0x02c
|
||||
#define REG_MMU_INV_SEL_GEN1 0x038
|
||||
#define F_INVLD_EN0 BIT(0)
|
||||
#define F_INVLD_EN1 BIT(1)
|
||||
|
||||
#define REG_MMU_STANDARD_AXI_MODE 0x048
|
||||
#define REG_MMU_MISC_CTRL 0x048
|
||||
#define F_MMU_IN_ORDER_WR_EN_MASK (BIT(1) | BIT(17))
|
||||
#define F_MMU_STANDARD_AXI_MODE_MASK (BIT(3) | BIT(19))
|
||||
|
||||
#define REG_MMU_DCM_DIS 0x050
|
||||
#define REG_MMU_WR_LEN_CTRL 0x054
|
||||
#define F_MMU_WR_THROT_DIS_MASK (BIT(5) | BIT(21))
|
||||
|
||||
#define REG_MMU_CTRL_REG 0x110
|
||||
#define F_MMU_TF_PROT_TO_PROGRAM_ADDR (2 << 4)
|
||||
|
|
@ -88,10 +94,12 @@
|
|||
#define REG_MMU1_INVLD_PA 0x148
|
||||
#define REG_MMU0_INT_ID 0x150
|
||||
#define REG_MMU1_INT_ID 0x154
|
||||
#define F_MMU_INT_ID_COMM_ID(a) (((a) >> 9) & 0x7)
|
||||
#define F_MMU_INT_ID_SUB_COMM_ID(a) (((a) >> 7) & 0x3)
|
||||
#define F_MMU_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7)
|
||||
#define F_MMU_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f)
|
||||
|
||||
#define MTK_PROTECT_PA_ALIGN 128
|
||||
#define MTK_PROTECT_PA_ALIGN 256
|
||||
|
||||
/*
|
||||
* Get the local arbiter ID and the portid within the larb arbiter
|
||||
|
|
@ -100,6 +108,18 @@
|
|||
#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0xf)
|
||||
#define MTK_M4U_TO_PORT(id) ((id) & 0x1f)
|
||||
|
||||
#define HAS_4GB_MODE BIT(0)
|
||||
/* HW will use the EMI clock if there isn't the "bclk". */
|
||||
#define HAS_BCLK BIT(1)
|
||||
#define HAS_VLD_PA_RNG BIT(2)
|
||||
#define RESET_AXI BIT(3)
|
||||
#define OUT_ORDER_WR_EN BIT(4)
|
||||
#define HAS_SUB_COMM BIT(5)
|
||||
#define WR_THROT_EN BIT(6)
|
||||
|
||||
#define MTK_IOMMU_HAS_FLAG(pdata, _x) \
|
||||
((((pdata)->flags) & (_x)) == (_x))
|
||||
|
||||
struct mtk_iommu_domain {
|
||||
struct io_pgtable_cfg cfg;
|
||||
struct io_pgtable_ops *iop;
|
||||
|
|
@ -165,7 +185,7 @@ static void mtk_iommu_tlb_flush_all(void *cookie)
|
|||
|
||||
for_each_m4u(data) {
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
|
||||
data->base + REG_MMU_INV_SEL);
|
||||
data->base + data->plat_data->inv_sel_reg);
|
||||
writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
|
||||
wmb(); /* Make sure the tlb flush all done */
|
||||
}
|
||||
|
|
@ -182,7 +202,7 @@ static void mtk_iommu_tlb_flush_range_sync(unsigned long iova, size_t size,
|
|||
for_each_m4u(data) {
|
||||
spin_lock_irqsave(&data->tlb_lock, flags);
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
|
||||
data->base + REG_MMU_INV_SEL);
|
||||
data->base + data->plat_data->inv_sel_reg);
|
||||
|
||||
writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A);
|
||||
writel_relaxed(iova + size - 1,
|
||||
|
|
@ -226,7 +246,7 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
|||
struct mtk_iommu_data *data = dev_id;
|
||||
struct mtk_iommu_domain *dom = data->m4u_dom;
|
||||
u32 int_state, regval, fault_iova, fault_pa;
|
||||
unsigned int fault_larb, fault_port;
|
||||
unsigned int fault_larb, fault_port, sub_comm = 0;
|
||||
bool layer, write;
|
||||
|
||||
/* Read error info from registers */
|
||||
|
|
@ -242,10 +262,14 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
|||
}
|
||||
layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
|
||||
write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
|
||||
fault_larb = F_MMU_INT_ID_LARB_ID(regval);
|
||||
fault_port = F_MMU_INT_ID_PORT_ID(regval);
|
||||
|
||||
fault_larb = data->plat_data->larbid_remap[fault_larb];
|
||||
if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_SUB_COMM)) {
|
||||
fault_larb = F_MMU_INT_ID_COMM_ID(regval);
|
||||
sub_comm = F_MMU_INT_ID_SUB_COMM_ID(regval);
|
||||
} else {
|
||||
fault_larb = F_MMU_INT_ID_LARB_ID(regval);
|
||||
}
|
||||
fault_larb = data->plat_data->larbid_remap[fault_larb][sub_comm];
|
||||
|
||||
if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
|
||||
write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
|
||||
|
|
@ -397,7 +421,7 @@ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
paddr |= BIT_ULL(32);
|
||||
|
||||
/* Synchronize with the tlb_lock */
|
||||
return dom->iop->map(dom->iop, iova, paddr, size, prot);
|
||||
return dom->iop->map(dom->iop, iova, paddr, size, prot, gfp);
|
||||
}
|
||||
|
||||
static size_t mtk_iommu_unmap(struct iommu_domain *domain,
|
||||
|
|
@ -532,11 +556,13 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (data->plat_data->m4u_plat == M4U_MT8173)
|
||||
if (data->plat_data->m4u_plat == M4U_MT8173) {
|
||||
regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
|
||||
F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173;
|
||||
else
|
||||
regval = F_MMU_TF_PROT_TO_PROGRAM_ADDR;
|
||||
} else {
|
||||
regval = readl_relaxed(data->base + REG_MMU_CTRL_REG);
|
||||
regval |= F_MMU_TF_PROT_TO_PROGRAM_ADDR;
|
||||
}
|
||||
writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
|
||||
|
||||
regval = F_L2_MULIT_HIT_EN |
|
||||
|
|
@ -563,7 +589,8 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||
upper_32_bits(data->protect_base);
|
||||
writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
|
||||
|
||||
if (data->enable_4GB && data->plat_data->has_vld_pa_rng) {
|
||||
if (data->enable_4GB &&
|
||||
MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_VLD_PA_RNG)) {
|
||||
/*
|
||||
* If 4GB mode is enabled, the validate PA range is from
|
||||
* 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30].
|
||||
|
|
@ -572,9 +599,23 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||
writel_relaxed(regval, data->base + REG_MMU_VLD_PA_RNG);
|
||||
}
|
||||
writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
|
||||
if (MTK_IOMMU_HAS_FLAG(data->plat_data, WR_THROT_EN)) {
|
||||
/* write command throttling mode */
|
||||
regval = readl_relaxed(data->base + REG_MMU_WR_LEN_CTRL);
|
||||
regval &= ~F_MMU_WR_THROT_DIS_MASK;
|
||||
writel_relaxed(regval, data->base + REG_MMU_WR_LEN_CTRL);
|
||||
}
|
||||
|
||||
if (data->plat_data->reset_axi)
|
||||
writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
|
||||
if (MTK_IOMMU_HAS_FLAG(data->plat_data, RESET_AXI)) {
|
||||
/* The register is called STANDARD_AXI_MODE in this case */
|
||||
regval = 0;
|
||||
} else {
|
||||
regval = readl_relaxed(data->base + REG_MMU_MISC_CTRL);
|
||||
regval &= ~F_MMU_STANDARD_AXI_MODE_MASK;
|
||||
if (MTK_IOMMU_HAS_FLAG(data->plat_data, OUT_ORDER_WR_EN))
|
||||
regval &= ~F_MMU_IN_ORDER_WR_EN_MASK;
|
||||
}
|
||||
writel_relaxed(regval, data->base + REG_MMU_MISC_CTRL);
|
||||
|
||||
if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
|
||||
dev_name(data->dev), (void *)data)) {
|
||||
|
|
@ -616,7 +657,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
|
||||
/* Whether the current dram is over 4GB */
|
||||
data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT));
|
||||
if (!data->plat_data->has_4gb_mode)
|
||||
if (!MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_4GB_MODE))
|
||||
data->enable_4GB = false;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
@ -629,7 +670,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
if (data->irq < 0)
|
||||
return data->irq;
|
||||
|
||||
if (data->plat_data->has_bclk) {
|
||||
if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_BCLK)) {
|
||||
data->bclk = devm_clk_get(dev, "bclk");
|
||||
if (IS_ERR(data->bclk))
|
||||
return PTR_ERR(data->bclk);
|
||||
|
|
@ -718,8 +759,8 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev)
|
|||
struct mtk_iommu_suspend_reg *reg = &data->reg;
|
||||
void __iomem *base = data->base;
|
||||
|
||||
reg->standard_axi_mode = readl_relaxed(base +
|
||||
REG_MMU_STANDARD_AXI_MODE);
|
||||
reg->wr_len_ctrl = readl_relaxed(base + REG_MMU_WR_LEN_CTRL);
|
||||
reg->misc_ctrl = readl_relaxed(base + REG_MMU_MISC_CTRL);
|
||||
reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM_DIS);
|
||||
reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
|
||||
reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
|
||||
|
|
@ -743,8 +784,8 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
|
|||
dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret);
|
||||
return ret;
|
||||
}
|
||||
writel_relaxed(reg->standard_axi_mode,
|
||||
base + REG_MMU_STANDARD_AXI_MODE);
|
||||
writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL);
|
||||
writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL);
|
||||
writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
|
||||
writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
|
||||
writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
|
||||
|
|
@ -763,28 +804,35 @@ static const struct dev_pm_ops mtk_iommu_pm_ops = {
|
|||
|
||||
static const struct mtk_iommu_plat_data mt2712_data = {
|
||||
.m4u_plat = M4U_MT2712,
|
||||
.has_4gb_mode = true,
|
||||
.has_bclk = true,
|
||||
.has_vld_pa_rng = true,
|
||||
.larbid_remap = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.flags = HAS_4GB_MODE | HAS_BCLK | HAS_VLD_PA_RNG,
|
||||
.inv_sel_reg = REG_MMU_INV_SEL_GEN1,
|
||||
.larbid_remap = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}},
|
||||
};
|
||||
|
||||
static const struct mtk_iommu_plat_data mt6779_data = {
|
||||
.m4u_plat = M4U_MT6779,
|
||||
.flags = HAS_SUB_COMM | OUT_ORDER_WR_EN | WR_THROT_EN,
|
||||
.inv_sel_reg = REG_MMU_INV_SEL_GEN2,
|
||||
.larbid_remap = {{0}, {1}, {2}, {3}, {5}, {7, 8}, {10}, {9}},
|
||||
};
|
||||
|
||||
static const struct mtk_iommu_plat_data mt8173_data = {
|
||||
.m4u_plat = M4U_MT8173,
|
||||
.has_4gb_mode = true,
|
||||
.has_bclk = true,
|
||||
.reset_axi = true,
|
||||
.larbid_remap = {0, 1, 2, 3, 4, 5}, /* Linear mapping. */
|
||||
.flags = HAS_4GB_MODE | HAS_BCLK | RESET_AXI,
|
||||
.inv_sel_reg = REG_MMU_INV_SEL_GEN1,
|
||||
.larbid_remap = {{0}, {1}, {2}, {3}, {4}, {5}}, /* Linear mapping. */
|
||||
};
|
||||
|
||||
static const struct mtk_iommu_plat_data mt8183_data = {
|
||||
.m4u_plat = M4U_MT8183,
|
||||
.reset_axi = true,
|
||||
.larbid_remap = {0, 4, 5, 6, 7, 2, 3, 1},
|
||||
.flags = RESET_AXI,
|
||||
.inv_sel_reg = REG_MMU_INV_SEL_GEN1,
|
||||
.larbid_remap = {{0}, {4}, {5}, {6}, {7}, {2}, {3}, {1}},
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_iommu_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt2712-m4u", .data = &mt2712_data},
|
||||
{ .compatible = "mediatek,mt6779-m4u", .data = &mt6779_data},
|
||||
{ .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data},
|
||||
{ .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data},
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -15,34 +15,39 @@
|
|||
#include <linux/iommu.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <soc/mediatek/smi.h>
|
||||
|
||||
#define MTK_LARB_COM_MAX 8
|
||||
#define MTK_LARB_SUBCOM_MAX 4
|
||||
|
||||
struct mtk_iommu_suspend_reg {
|
||||
u32 standard_axi_mode;
|
||||
union {
|
||||
u32 standard_axi_mode;/* v1 */
|
||||
u32 misc_ctrl;/* v2 */
|
||||
};
|
||||
u32 dcm_dis;
|
||||
u32 ctrl_reg;
|
||||
u32 int_control0;
|
||||
u32 int_main_control;
|
||||
u32 ivrp_paddr;
|
||||
u32 vld_pa_rng;
|
||||
u32 wr_len_ctrl;
|
||||
};
|
||||
|
||||
enum mtk_iommu_plat {
|
||||
M4U_MT2701,
|
||||
M4U_MT2712,
|
||||
M4U_MT6779,
|
||||
M4U_MT8173,
|
||||
M4U_MT8183,
|
||||
};
|
||||
|
||||
struct mtk_iommu_plat_data {
|
||||
enum mtk_iommu_plat m4u_plat;
|
||||
bool has_4gb_mode;
|
||||
|
||||
/* HW will use the EMI clock if there isn't the "bclk". */
|
||||
bool has_bclk;
|
||||
bool has_vld_pa_rng;
|
||||
bool reset_axi;
|
||||
unsigned char larbid_remap[MTK_LARB_NR_MAX];
|
||||
u32 flags;
|
||||
u32 inv_sel_reg;
|
||||
unsigned char larbid_remap[MTK_LARB_COM_MAX][MTK_LARB_SUBCOM_MAX];
|
||||
};
|
||||
|
||||
struct mtk_iommu_domain;
|
||||
|
|
@ -62,6 +67,8 @@ struct mtk_iommu_data {
|
|||
struct iommu_device iommu;
|
||||
const struct mtk_iommu_plat_data *plat_data;
|
||||
|
||||
struct dma_iommu_mapping *mapping; /* For mtk_iommu_v1.c */
|
||||
|
||||
struct list_head list;
|
||||
struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain,
|
|||
int ret;
|
||||
|
||||
/* Only allow the domain created internally. */
|
||||
mtk_mapping = data->dev->archdata.iommu;
|
||||
mtk_mapping = data->mapping;
|
||||
if (mtk_mapping->domain != domain)
|
||||
return 0;
|
||||
|
||||
|
|
@ -369,7 +369,6 @@ static int mtk_iommu_create_mapping(struct device *dev,
|
|||
struct mtk_iommu_data *data;
|
||||
struct platform_device *m4updev;
|
||||
struct dma_iommu_mapping *mtk_mapping;
|
||||
struct device *m4udev;
|
||||
int ret;
|
||||
|
||||
if (args->args_count != 1) {
|
||||
|
|
@ -401,8 +400,7 @@ static int mtk_iommu_create_mapping(struct device *dev,
|
|||
return ret;
|
||||
|
||||
data = dev_iommu_priv_get(dev);
|
||||
m4udev = data->dev;
|
||||
mtk_mapping = m4udev->archdata.iommu;
|
||||
mtk_mapping = data->mapping;
|
||||
if (!mtk_mapping) {
|
||||
/* MTK iommu support 4GB iova address space. */
|
||||
mtk_mapping = arm_iommu_create_mapping(&platform_bus_type,
|
||||
|
|
@ -410,7 +408,7 @@ static int mtk_iommu_create_mapping(struct device *dev,
|
|||
if (IS_ERR(mtk_mapping))
|
||||
return PTR_ERR(mtk_mapping);
|
||||
|
||||
m4udev->archdata.iommu = mtk_mapping;
|
||||
data->mapping = mtk_mapping;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -459,7 +457,7 @@ static void mtk_iommu_probe_finalize(struct device *dev)
|
|||
int err;
|
||||
|
||||
data = dev_iommu_priv_get(dev);
|
||||
mtk_mapping = data->dev->archdata.iommu;
|
||||
mtk_mapping = data->mapping;
|
||||
|
||||
err = arm_iommu_attach_device(dev, mtk_mapping);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -98,8 +98,11 @@ static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
|
|||
mutex_lock(&iommu_debug_lock);
|
||||
|
||||
bytes = omap_iommu_dump_ctx(obj, p, count);
|
||||
if (bytes < 0)
|
||||
goto err;
|
||||
bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);
|
||||
|
||||
err:
|
||||
mutex_unlock(&iommu_debug_lock);
|
||||
kfree(buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* omap iommu: tlb and pagetable primitives
|
||||
*
|
||||
* Copyright (C) 2008-2010 Nokia Corporation
|
||||
* Copyright (C) 2013-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2013-2017 Texas Instruments Incorporated - https://www.ti.com/
|
||||
*
|
||||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
|
||||
* Paul Mundt and Toshihiro Kobayashi
|
||||
|
|
@ -71,7 +71,7 @@ static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom)
|
|||
**/
|
||||
void omap_iommu_save_ctx(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
struct omap_iommu *obj;
|
||||
u32 *p;
|
||||
int i;
|
||||
|
|
@ -101,7 +101,7 @@ EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
|||
**/
|
||||
void omap_iommu_restore_ctx(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
struct omap_iommu *obj;
|
||||
u32 *p;
|
||||
int i;
|
||||
|
|
@ -1398,7 +1398,7 @@ static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
|||
|
||||
static int omap_iommu_count(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
int count = 0;
|
||||
|
||||
while (arch_data->iommu_dev) {
|
||||
|
|
@ -1459,8 +1459,8 @@ static void omap_iommu_detach_fini(struct omap_iommu_domain *odomain)
|
|||
static int
|
||||
omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_device *iommu;
|
||||
struct omap_iommu *oiommu;
|
||||
int ret = 0;
|
||||
|
|
@ -1524,7 +1524,7 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
struct omap_iommu_device *iommu = omap_domain->iommus;
|
||||
struct omap_iommu *oiommu;
|
||||
int i;
|
||||
|
|
@ -1650,7 +1650,7 @@ static struct iommu_device *omap_iommu_probe_device(struct device *dev)
|
|||
int num_iommus, i;
|
||||
|
||||
/*
|
||||
* Allocate the archdata iommu structure for DT-based devices.
|
||||
* Allocate the per-device iommu structure for DT-based devices.
|
||||
*
|
||||
* TODO: Simplify this when removing non-DT support completely from the
|
||||
* IOMMU users.
|
||||
|
|
@ -1698,7 +1698,7 @@ static struct iommu_device *omap_iommu_probe_device(struct device *dev)
|
|||
of_node_put(np);
|
||||
}
|
||||
|
||||
dev->archdata.iommu = arch_data;
|
||||
dev_iommu_priv_set(dev, arch_data);
|
||||
|
||||
/*
|
||||
* use the first IOMMU alone for the sysfs device linking.
|
||||
|
|
@ -1712,19 +1712,19 @@ static struct iommu_device *omap_iommu_probe_device(struct device *dev)
|
|||
|
||||
static void omap_iommu_release_device(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
|
||||
if (!dev->of_node || !arch_data)
|
||||
return;
|
||||
|
||||
dev->archdata.iommu = NULL;
|
||||
dev_iommu_priv_set(dev, NULL);
|
||||
kfree(arch_data);
|
||||
|
||||
}
|
||||
|
||||
static struct iommu_group *omap_iommu_device_group(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
|
||||
struct iommu_group *group = ERR_PTR(-EINVAL);
|
||||
|
||||
if (!arch_data)
|
||||
|
|
|
|||
|
|
@ -836,7 +836,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
|
|||
|
||||
static struct rk_iommu *rk_iommu_from_dev(struct device *dev)
|
||||
{
|
||||
struct rk_iommudata *data = dev->archdata.iommu;
|
||||
struct rk_iommudata *data = dev_iommu_priv_get(dev);
|
||||
|
||||
return data ? data->iommu : NULL;
|
||||
}
|
||||
|
|
@ -1059,7 +1059,7 @@ static struct iommu_device *rk_iommu_probe_device(struct device *dev)
|
|||
struct rk_iommudata *data;
|
||||
struct rk_iommu *iommu;
|
||||
|
||||
data = dev->archdata.iommu;
|
||||
data = dev_iommu_priv_get(dev);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
|
|
@ -1073,7 +1073,7 @@ static struct iommu_device *rk_iommu_probe_device(struct device *dev)
|
|||
|
||||
static void rk_iommu_release_device(struct device *dev)
|
||||
{
|
||||
struct rk_iommudata *data = dev->archdata.iommu;
|
||||
struct rk_iommudata *data = dev_iommu_priv_get(dev);
|
||||
|
||||
device_link_del(data->link);
|
||||
}
|
||||
|
|
@ -1100,7 +1100,7 @@ static int rk_iommu_of_xlate(struct device *dev,
|
|||
iommu_dev = of_find_device_by_node(args->np);
|
||||
|
||||
data->iommu = platform_get_drvdata(iommu_dev);
|
||||
dev->archdata.iommu = data;
|
||||
dev_iommu_priv_set(dev, data);
|
||||
|
||||
platform_device_put(iommu_dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain,
|
|||
|
||||
if (gart->active_domain && gart->active_domain != domain) {
|
||||
ret = -EBUSY;
|
||||
} else if (dev->archdata.iommu != domain) {
|
||||
dev->archdata.iommu = domain;
|
||||
} else if (dev_iommu_priv_get(dev) != domain) {
|
||||
dev_iommu_priv_set(dev, domain);
|
||||
gart->active_domain = domain;
|
||||
gart->active_devices++;
|
||||
}
|
||||
|
|
@ -131,8 +131,8 @@ static void gart_iommu_detach_dev(struct iommu_domain *domain,
|
|||
|
||||
spin_lock(&gart->dom_lock);
|
||||
|
||||
if (dev->archdata.iommu == domain) {
|
||||
dev->archdata.iommu = NULL;
|
||||
if (dev_iommu_priv_get(dev) == domain) {
|
||||
dev_iommu_priv_set(dev, NULL);
|
||||
|
||||
if (--gart->active_devices == 0)
|
||||
gart->active_domain = NULL;
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
|
|||
static int tegra_smmu_attach_dev(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct tegra_smmu *smmu = dev->archdata.iommu;
|
||||
struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
|
||||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_phandle_args args;
|
||||
|
|
@ -780,7 +780,7 @@ static struct iommu_device *tegra_smmu_probe_device(struct device *dev)
|
|||
* supported by the Linux kernel, so abort after the
|
||||
* first match.
|
||||
*/
|
||||
dev->archdata.iommu = smmu;
|
||||
dev_iommu_priv_set(dev, smmu);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -797,7 +797,7 @@ static struct iommu_device *tegra_smmu_probe_device(struct device *dev)
|
|||
|
||||
static void tegra_smmu_release_device(struct device *dev)
|
||||
{
|
||||
dev->archdata.iommu = NULL;
|
||||
dev_iommu_priv_set(dev, NULL);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_group_soc *
|
||||
|
|
@ -856,7 +856,7 @@ static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu,
|
|||
static struct iommu_group *tegra_smmu_device_group(struct device *dev)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
|
||||
struct tegra_smmu *smmu = dev->archdata.iommu;
|
||||
struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
|
||||
struct iommu_group *group;
|
||||
|
||||
group = tegra_smmu_group_get(smmu, fwspec->ids[0]);
|
||||
|
|
|
|||
|
|
@ -1010,8 +1010,8 @@ static int viommu_probe(struct virtio_device *vdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
virtio_cread(vdev, struct virtio_iommu_config, page_size_mask,
|
||||
&viommu->pgsize_bitmap);
|
||||
virtio_cread_le(vdev, struct virtio_iommu_config, page_size_mask,
|
||||
&viommu->pgsize_bitmap);
|
||||
|
||||
if (!viommu->pgsize_bitmap) {
|
||||
ret = -EINVAL;
|
||||
|
|
@ -1022,25 +1022,25 @@ static int viommu_probe(struct virtio_device *vdev)
|
|||
viommu->last_domain = ~0U;
|
||||
|
||||
/* Optional features */
|
||||
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
|
||||
struct virtio_iommu_config, input_range.start,
|
||||
&input_start);
|
||||
virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
|
||||
struct virtio_iommu_config, input_range.start,
|
||||
&input_start);
|
||||
|
||||
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
|
||||
struct virtio_iommu_config, input_range.end,
|
||||
&input_end);
|
||||
virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
|
||||
struct virtio_iommu_config, input_range.end,
|
||||
&input_end);
|
||||
|
||||
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
|
||||
struct virtio_iommu_config, domain_range.start,
|
||||
&viommu->first_domain);
|
||||
virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
|
||||
struct virtio_iommu_config, domain_range.start,
|
||||
&viommu->first_domain);
|
||||
|
||||
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
|
||||
struct virtio_iommu_config, domain_range.end,
|
||||
&viommu->last_domain);
|
||||
virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_RANGE,
|
||||
struct virtio_iommu_config, domain_range.end,
|
||||
&viommu->last_domain);
|
||||
|
||||
virtio_cread_feature(vdev, VIRTIO_IOMMU_F_PROBE,
|
||||
struct virtio_iommu_config, probe_size,
|
||||
&viommu->probe_size);
|
||||
virtio_cread_le_feature(vdev, VIRTIO_IOMMU_F_PROBE,
|
||||
struct virtio_iommu_config, probe_size,
|
||||
&viommu->probe_size);
|
||||
|
||||
viommu->geometry = (struct iommu_domain_geometry) {
|
||||
.aperture_start = input_start,
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@
|
|||
|
||||
#if defined(CONFIG_EXYNOS_IOMMU)
|
||||
|
||||
#include <linux/iommu.h>
|
||||
|
||||
static inline bool exynos_is_iommu_available(struct device *dev)
|
||||
{
|
||||
return dev->archdata.iommu != NULL;
|
||||
return dev_iommu_priv_get(dev) != NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -239,6 +239,13 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
|
|||
.larb_direct_to_common_mask = BIT(8) | BIT(9), /* bdpsys */
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
|
||||
.config_port = mtk_smi_larb_config_port_gen2_general,
|
||||
.larb_direct_to_common_mask =
|
||||
BIT(4) | BIT(6) | BIT(11) | BIT(12) | BIT(13),
|
||||
/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
|
||||
.has_gals = true,
|
||||
.config_port = mtk_smi_larb_config_port_gen2_general,
|
||||
|
|
@ -259,6 +266,10 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
|||
.compatible = "mediatek,mt2712-smi-larb",
|
||||
.data = &mtk_smi_larb_mt2712
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt6779-smi-larb",
|
||||
.data = &mtk_smi_larb_mt6779
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt8183-smi-larb",
|
||||
.data = &mtk_smi_larb_mt8183
|
||||
|
|
@ -388,6 +399,13 @@ static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
|
|||
.gen = MTK_SMI_GEN2,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = {
|
||||
.gen = MTK_SMI_GEN2,
|
||||
.has_gals = true,
|
||||
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
|
||||
F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
|
||||
.gen = MTK_SMI_GEN2,
|
||||
.has_gals = true,
|
||||
|
|
@ -408,6 +426,10 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
|
|||
.compatible = "mediatek,mt2712-smi-common",
|
||||
.data = &mtk_smi_common_gen2,
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt6779-smi-common",
|
||||
.data = &mtk_smi_common_mt6779,
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt8183-smi-common",
|
||||
.data = &mtk_smi_common_mt8183,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ static int sky81452_probe(struct i2c_client *client,
|
|||
memset(cells, 0, sizeof(cells));
|
||||
cells[0].name = "sky81452-backlight";
|
||||
cells[0].of_compatible = "skyworks,sky81452-backlight";
|
||||
cells[0].platform_data = pdata->bl_pdata;
|
||||
cells[0].pdata_size = sizeof(*pdata->bl_pdata);
|
||||
cells[1].name = "sky81452-regulator";
|
||||
cells[1].platform_data = pdata->regulator_init_data;
|
||||
cells[1].pdata_size = sizeof(*pdata->regulator_init_data);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include "ipa_version.h"
|
||||
#include "gsi.h"
|
||||
|
|
@ -73,6 +74,8 @@ struct ipa {
|
|||
enum ipa_version version;
|
||||
struct platform_device *pdev;
|
||||
struct rproc *modem_rproc;
|
||||
struct notifier_block nb;
|
||||
void *notifier;
|
||||
struct ipa_smp2p *smp2p;
|
||||
struct ipa_clock *clock;
|
||||
atomic_t suspend_ref;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_rmnet.h>
|
||||
#include <linux/remoteproc/qcom_q6v5_ipa_notify.h>
|
||||
#include <linux/remoteproc/qcom_rproc.h>
|
||||
|
||||
#include "ipa.h"
|
||||
#include "ipa_data.h"
|
||||
|
|
@ -311,43 +311,40 @@ static void ipa_modem_crashed(struct ipa *ipa)
|
|||
dev_err(dev, "error %d zeroing modem memory regions\n", ret);
|
||||
}
|
||||
|
||||
static void ipa_modem_notify(void *data, enum qcom_rproc_event event)
|
||||
static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct ipa *ipa = data;
|
||||
struct device *dev;
|
||||
struct ipa *ipa = container_of(nb, struct ipa, nb);
|
||||
struct qcom_ssr_notify_data *notify_data = data;
|
||||
struct device *dev = &ipa->pdev->dev;
|
||||
|
||||
dev = &ipa->pdev->dev;
|
||||
switch (event) {
|
||||
case MODEM_STARTING:
|
||||
switch (action) {
|
||||
case QCOM_SSR_BEFORE_POWERUP:
|
||||
dev_info(dev, "received modem starting event\n");
|
||||
ipa_smp2p_notify_reset(ipa);
|
||||
break;
|
||||
|
||||
case MODEM_RUNNING:
|
||||
case QCOM_SSR_AFTER_POWERUP:
|
||||
dev_info(dev, "received modem running event\n");
|
||||
break;
|
||||
|
||||
case MODEM_STOPPING:
|
||||
case MODEM_CRASHED:
|
||||
case QCOM_SSR_BEFORE_SHUTDOWN:
|
||||
dev_info(dev, "received modem %s event\n",
|
||||
event == MODEM_STOPPING ? "stopping"
|
||||
: "crashed");
|
||||
notify_data->crashed ? "crashed" : "stopping");
|
||||
if (ipa->setup_complete)
|
||||
ipa_modem_crashed(ipa);
|
||||
break;
|
||||
|
||||
case MODEM_OFFLINE:
|
||||
case QCOM_SSR_AFTER_SHUTDOWN:
|
||||
dev_info(dev, "received modem offline event\n");
|
||||
break;
|
||||
|
||||
case MODEM_REMOVING:
|
||||
dev_info(dev, "received modem stopping event\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&ipa->pdev->dev, "unrecognized event %u\n", event);
|
||||
dev_err(dev, "received unrecognized event %lu\n", action);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int ipa_modem_init(struct ipa *ipa, bool modem_init)
|
||||
|
|
@ -362,13 +359,30 @@ void ipa_modem_exit(struct ipa *ipa)
|
|||
|
||||
int ipa_modem_config(struct ipa *ipa)
|
||||
{
|
||||
return qcom_register_ipa_notify(ipa->modem_rproc, ipa_modem_notify,
|
||||
ipa);
|
||||
void *notifier;
|
||||
|
||||
ipa->nb.notifier_call = ipa_modem_notify;
|
||||
|
||||
notifier = qcom_register_ssr_notifier("mpss", &ipa->nb);
|
||||
if (IS_ERR(notifier))
|
||||
return PTR_ERR(notifier);
|
||||
|
||||
ipa->notifier = notifier;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipa_modem_deconfig(struct ipa *ipa)
|
||||
{
|
||||
qcom_deregister_ipa_notify(ipa->modem_rproc);
|
||||
struct device *dev = &ipa->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = qcom_unregister_ssr_notifier(ipa->notifier, &ipa->nb);
|
||||
if (ret)
|
||||
dev_err(dev, "error %d unregistering notifier", ret);
|
||||
|
||||
ipa->notifier = NULL;
|
||||
memset(&ipa->nb, 0, sizeof(ipa->nb));
|
||||
}
|
||||
|
||||
int ipa_modem_setup(struct ipa *ipa)
|
||||
|
|
|
|||
|
|
@ -2264,12 +2264,13 @@ static void virtnet_update_settings(struct virtnet_info *vi)
|
|||
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX))
|
||||
return;
|
||||
|
||||
speed = virtio_cread32(vi->vdev, offsetof(struct virtio_net_config,
|
||||
speed));
|
||||
virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed);
|
||||
|
||||
if (ethtool_validate_speed(speed))
|
||||
vi->speed = speed;
|
||||
duplex = virtio_cread8(vi->vdev, offsetof(struct virtio_net_config,
|
||||
duplex));
|
||||
|
||||
virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex);
|
||||
|
||||
if (ethtool_validate_duplex(duplex))
|
||||
vi->duplex = duplex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1037,9 +1037,25 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
|||
dimm_name = "bus";
|
||||
}
|
||||
|
||||
/* Validate command family support against bus declared support */
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
unsigned long *mask;
|
||||
|
||||
if (copy_from_user(&pkg, p, sizeof(pkg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (nvdimm) {
|
||||
if (pkg.nd_family > NVDIMM_FAMILY_MAX)
|
||||
return -EINVAL;
|
||||
mask = &nd_desc->dimm_family_mask;
|
||||
} else {
|
||||
if (pkg.nd_family > NVDIMM_BUS_FAMILY_MAX)
|
||||
return -EINVAL;
|
||||
mask = &nd_desc->bus_family_mask;
|
||||
}
|
||||
|
||||
if (!test_bit(pkg.nd_family, mask))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc ||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#include <linux/libnvdimm.h>
|
||||
#include <linux/badblocks.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
|
@ -389,8 +390,156 @@ static const struct attribute_group nvdimm_bus_attribute_group = {
|
|||
.attrs = nvdimm_bus_attributes,
|
||||
};
|
||||
|
||||
static ssize_t capability_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
enum nvdimm_fwa_capability cap;
|
||||
|
||||
if (!nd_desc->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
cap = nd_desc->fw_ops->capability(nd_desc);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
switch (cap) {
|
||||
case NVDIMM_FWA_CAP_QUIESCE:
|
||||
return sprintf(buf, "quiesce\n");
|
||||
case NVDIMM_FWA_CAP_LIVE:
|
||||
return sprintf(buf, "live\n");
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(capability);
|
||||
|
||||
static ssize_t activate_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
enum nvdimm_fwa_capability cap;
|
||||
enum nvdimm_fwa_state state;
|
||||
|
||||
if (!nd_desc->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
cap = nd_desc->fw_ops->capability(nd_desc);
|
||||
state = nd_desc->fw_ops->activate_state(nd_desc);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
if (cap < NVDIMM_FWA_CAP_QUIESCE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (state) {
|
||||
case NVDIMM_FWA_IDLE:
|
||||
return sprintf(buf, "idle\n");
|
||||
case NVDIMM_FWA_BUSY:
|
||||
return sprintf(buf, "busy\n");
|
||||
case NVDIMM_FWA_ARMED:
|
||||
return sprintf(buf, "armed\n");
|
||||
case NVDIMM_FWA_ARM_OVERFLOW:
|
||||
return sprintf(buf, "overflow\n");
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int exec_firmware_activate(void *data)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc = data;
|
||||
|
||||
return nd_desc->fw_ops->activate(nd_desc);
|
||||
}
|
||||
|
||||
static ssize_t activate_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
enum nvdimm_fwa_state state;
|
||||
bool quiesce;
|
||||
ssize_t rc;
|
||||
|
||||
if (!nd_desc->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (sysfs_streq(buf, "live"))
|
||||
quiesce = false;
|
||||
else if (sysfs_streq(buf, "quiesce"))
|
||||
quiesce = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
state = nd_desc->fw_ops->activate_state(nd_desc);
|
||||
|
||||
switch (state) {
|
||||
case NVDIMM_FWA_BUSY:
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
case NVDIMM_FWA_ARMED:
|
||||
case NVDIMM_FWA_ARM_OVERFLOW:
|
||||
if (quiesce)
|
||||
rc = hibernate_quiet_exec(exec_firmware_activate, nd_desc);
|
||||
else
|
||||
rc = nd_desc->fw_ops->activate(nd_desc);
|
||||
break;
|
||||
case NVDIMM_FWA_IDLE:
|
||||
default:
|
||||
rc = -ENXIO;
|
||||
}
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
if (rc == 0)
|
||||
rc = len;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_ADMIN_RW(activate);
|
||||
|
||||
static umode_t nvdimm_bus_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, typeof(*dev), kobj);
|
||||
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
enum nvdimm_fwa_capability cap;
|
||||
|
||||
/*
|
||||
* Both 'activate' and 'capability' disappear when no ops
|
||||
* detected, or a negative capability is indicated.
|
||||
*/
|
||||
if (!nd_desc->fw_ops)
|
||||
return 0;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
cap = nd_desc->fw_ops->capability(nd_desc);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
if (cap < NVDIMM_FWA_CAP_QUIESCE)
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
static struct attribute *nvdimm_bus_firmware_attributes[] = {
|
||||
&dev_attr_activate.attr,
|
||||
&dev_attr_capability.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group nvdimm_bus_firmware_attribute_group = {
|
||||
.name = "firmware",
|
||||
.attrs = nvdimm_bus_firmware_attributes,
|
||||
.is_visible = nvdimm_bus_firmware_visible,
|
||||
};
|
||||
|
||||
const struct attribute_group *nvdimm_bus_attribute_groups[] = {
|
||||
&nvdimm_bus_attribute_group,
|
||||
&nvdimm_bus_firmware_attribute_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -363,14 +363,14 @@ __weak ssize_t security_show(struct device *dev,
|
|||
{
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
|
||||
if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
|
||||
return sprintf(buf, "overwrite\n");
|
||||
if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
|
||||
return sprintf(buf, "disabled\n");
|
||||
if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
|
||||
return sprintf(buf, "unlocked\n");
|
||||
if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
|
||||
return sprintf(buf, "locked\n");
|
||||
if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
|
||||
return sprintf(buf, "overwrite\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
|
@ -446,9 +446,124 @@ static const struct attribute_group nvdimm_attribute_group = {
|
|||
.is_visible = nvdimm_visible,
|
||||
};
|
||||
|
||||
static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
enum nvdimm_fwa_result result;
|
||||
|
||||
if (!nvdimm->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
result = nvdimm->fw_ops->activate_result(nvdimm);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
switch (result) {
|
||||
case NVDIMM_FWA_RESULT_NONE:
|
||||
return sprintf(buf, "none\n");
|
||||
case NVDIMM_FWA_RESULT_SUCCESS:
|
||||
return sprintf(buf, "success\n");
|
||||
case NVDIMM_FWA_RESULT_FAIL:
|
||||
return sprintf(buf, "fail\n");
|
||||
case NVDIMM_FWA_RESULT_NOTSTAGED:
|
||||
return sprintf(buf, "not_staged\n");
|
||||
case NVDIMM_FWA_RESULT_NEEDRESET:
|
||||
return sprintf(buf, "need_reset\n");
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(result);
|
||||
|
||||
static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
enum nvdimm_fwa_state state;
|
||||
|
||||
if (!nvdimm->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
state = nvdimm->fw_ops->activate_state(nvdimm);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
switch (state) {
|
||||
case NVDIMM_FWA_IDLE:
|
||||
return sprintf(buf, "idle\n");
|
||||
case NVDIMM_FWA_BUSY:
|
||||
return sprintf(buf, "busy\n");
|
||||
case NVDIMM_FWA_ARMED:
|
||||
return sprintf(buf, "armed\n");
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t activate_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
enum nvdimm_fwa_trigger arg;
|
||||
int rc;
|
||||
|
||||
if (!nvdimm->fw_ops)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (sysfs_streq(buf, "arm"))
|
||||
arg = NVDIMM_FWA_ARM;
|
||||
else if (sysfs_streq(buf, "disarm"))
|
||||
arg = NVDIMM_FWA_DISARM;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
rc = nvdimm->fw_ops->arm(nvdimm, arg);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RW(activate);
|
||||
|
||||
static struct attribute *nvdimm_firmware_attributes[] = {
|
||||
&dev_attr_activate.attr,
|
||||
&dev_attr_result.attr,
|
||||
};
|
||||
|
||||
static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, typeof(*dev), kobj);
|
||||
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
||||
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
|
||||
struct nvdimm *nvdimm = to_nvdimm(dev);
|
||||
enum nvdimm_fwa_capability cap;
|
||||
|
||||
if (!nd_desc->fw_ops)
|
||||
return 0;
|
||||
if (!nvdimm->fw_ops)
|
||||
return 0;
|
||||
|
||||
nvdimm_bus_lock(dev);
|
||||
cap = nd_desc->fw_ops->capability(nd_desc);
|
||||
nvdimm_bus_unlock(dev);
|
||||
|
||||
if (cap < NVDIMM_FWA_CAP_QUIESCE)
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group nvdimm_firmware_attribute_group = {
|
||||
.name = "firmware",
|
||||
.attrs = nvdimm_firmware_attributes,
|
||||
.is_visible = nvdimm_firmware_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nvdimm_attribute_groups[] = {
|
||||
&nd_device_attribute_group,
|
||||
&nvdimm_attribute_group,
|
||||
&nvdimm_firmware_attribute_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -467,7 +582,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
|
|||
void *provider_data, const struct attribute_group **groups,
|
||||
unsigned long flags, unsigned long cmd_mask, int num_flush,
|
||||
struct resource *flush_wpq, const char *dimm_id,
|
||||
const struct nvdimm_security_ops *sec_ops)
|
||||
const struct nvdimm_security_ops *sec_ops,
|
||||
const struct nvdimm_fw_ops *fw_ops)
|
||||
{
|
||||
struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
|
||||
struct device *dev;
|
||||
|
|
@ -497,6 +613,7 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
|
|||
dev->devt = MKDEV(nvdimm_major, nvdimm->id);
|
||||
dev->groups = groups;
|
||||
nvdimm->sec.ops = sec_ops;
|
||||
nvdimm->fw_ops = fw_ops;
|
||||
nvdimm->sec.overwrite_tmo = 0;
|
||||
INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query);
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1309,7 +1309,7 @@ static ssize_t resource_show(struct device *dev,
|
|||
return -ENXIO;
|
||||
return sprintf(buf, "%#llx\n", (unsigned long long) res->start);
|
||||
}
|
||||
static DEVICE_ATTR(resource, 0400, resource_show, NULL);
|
||||
static DEVICE_ATTR_ADMIN_RO(resource);
|
||||
|
||||
static const unsigned long blk_lbasize_supported[] = { 512, 520, 528,
|
||||
4096, 4104, 4160, 4224, 0 };
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ struct nvdimm {
|
|||
struct kernfs_node *overwrite_state;
|
||||
} sec;
|
||||
struct delayed_work dwork;
|
||||
const struct nvdimm_fw_ops *fw_ops;
|
||||
};
|
||||
|
||||
static inline unsigned long nvdimm_security_flags(
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static ssize_t resource_show(struct device *dev,
|
|||
|
||||
return rc;
|
||||
}
|
||||
static DEVICE_ATTR(resource, 0400, resource_show, NULL);
|
||||
static DEVICE_ATTR_ADMIN_RO(resource);
|
||||
|
||||
static ssize_t size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
|
|||
|
|
@ -605,7 +605,7 @@ static ssize_t resource_show(struct device *dev,
|
|||
|
||||
return sprintf(buf, "%#llx\n", nd_region->ndr_start);
|
||||
}
|
||||
static DEVICE_ATTR(resource, 0400, resource_show, NULL);
|
||||
static DEVICE_ATTR_ADMIN_RO(resource);
|
||||
|
||||
static ssize_t persistence_domain_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
|
|||
|
|
@ -450,14 +450,19 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
|
|||
else
|
||||
dev_dbg(&nvdimm->dev, "overwrite completed\n");
|
||||
|
||||
if (nvdimm->sec.overwrite_state)
|
||||
sysfs_notify_dirent(nvdimm->sec.overwrite_state);
|
||||
/*
|
||||
* Mark the overwrite work done and update dimm security flags,
|
||||
* then send a sysfs event notification to wake up userspace
|
||||
* poll threads to picked up the changed state.
|
||||
*/
|
||||
nvdimm->sec.overwrite_tmo = 0;
|
||||
clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
|
||||
clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
|
||||
put_device(&nvdimm->dev);
|
||||
nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
||||
nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
|
||||
nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
|
||||
if (nvdimm->sec.overwrite_state)
|
||||
sysfs_notify_dirent(nvdimm->sec.overwrite_state);
|
||||
put_device(&nvdimm->dev);
|
||||
}
|
||||
|
||||
void nvdimm_security_overwrite_query(struct work_struct *work)
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
virtio_cread(vpmem->vdev, struct virtio_pmem_config,
|
||||
virtio_cread_le(vpmem->vdev, struct virtio_pmem_config,
|
||||
start, &vpmem->start);
|
||||
virtio_cread(vpmem->vdev, struct virtio_pmem_config,
|
||||
virtio_cread_le(vpmem->vdev, struct virtio_pmem_config,
|
||||
size, &vpmem->size);
|
||||
|
||||
res.start = vpmem->start;
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ config CROS_EC_TYPEC
|
|||
tristate "ChromeOS EC Type-C Connector Control"
|
||||
depends on MFD_CROS_EC_DEV && TYPEC
|
||||
depends on CROS_USBPD_NOTIFY
|
||||
depends on USB_ROLE_SWITCH
|
||||
default MFD_CROS_EC_DEV
|
||||
help
|
||||
If you say Y here, you get support for accessing Type C connector
|
||||
|
|
|
|||
|
|
@ -242,6 +242,25 @@ static ssize_t cros_ec_pdinfo_read(struct file *file,
|
|||
read_buf, p - read_buf);
|
||||
}
|
||||
|
||||
static bool cros_ec_uptime_is_supported(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct {
|
||||
struct cros_ec_command cmd;
|
||||
struct ec_response_uptime_info resp;
|
||||
} __packed msg = {};
|
||||
int ret;
|
||||
|
||||
msg.cmd.command = EC_CMD_GET_UPTIME_INFO;
|
||||
msg.cmd.insize = sizeof(msg.resp);
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd);
|
||||
if (ret == -EPROTO && msg.cmd.result == EC_RES_INVALID_COMMAND)
|
||||
return false;
|
||||
|
||||
/* Other errors maybe a transient error, do not rule about support. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_uptime_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
|
|
@ -444,8 +463,9 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
|
|||
debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
|
||||
&cros_ec_pdinfo_fops);
|
||||
|
||||
debugfs_create_file("uptime", 0444, debug_info->dir, debug_info,
|
||||
&cros_ec_uptime_fops);
|
||||
if (cros_ec_uptime_is_supported(ec->ec_dev))
|
||||
debugfs_create_file("uptime", 0444, debug_info->dir, debug_info,
|
||||
&cros_ec_uptime_fops);
|
||||
|
||||
debugfs_create_x32("last_resume_result", 0444, debug_info->dir,
|
||||
&ec->ec_dev->last_resume_result);
|
||||
|
|
|
|||
|
|
@ -681,8 +681,10 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
|
|||
|
||||
/* Register croc_ec_dev mfd */
|
||||
rv = cros_ec_dev_init(client_data);
|
||||
if (rv)
|
||||
if (rv) {
|
||||
down_write(&init_lock);
|
||||
goto end_cros_ec_dev_init_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user