diff options
303 files changed, 9096 insertions, 2604 deletions
diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst index 783d30b7bb42..0692c9aec66f 100644 --- a/Documentation/PCI/msi-howto.rst +++ b/Documentation/PCI/msi-howto.rst @@ -103,7 +103,7 @@ min_vecs argument set to this limit, and the PCI core will return -ENOSPC if it can't meet the minimum number of vectors. The flags argument is used to specify which type of interrupt can be used -by the device and the driver (PCI_IRQ_LEGACY, PCI_IRQ_MSI, PCI_IRQ_MSIX). +by the device and the driver (PCI_IRQ_INTX, PCI_IRQ_MSI, PCI_IRQ_MSIX). A convenient short-hand (PCI_IRQ_ALL_TYPES) is also available to ask for any possible kind of interrupt. If the PCI_IRQ_AFFINITY flag is set, pci_alloc_irq_vectors() will spread the interrupts around the available CPUs. diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index cced568d78e9..dd7b1c0c21da 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -335,7 +335,7 @@ causes the PCI support to program CPU vector data into the PCI device capability registers. Many architectures, chip-sets, or BIOSes do NOT support MSI or MSI-X and a call to pci_alloc_irq_vectors with just the PCI_IRQ_MSI and PCI_IRQ_MSIX flags will fail, so try to always -specify PCI_IRQ_LEGACY as well. +specify PCI_IRQ_INTX as well. Drivers that have different interrupt handlers for MSI/MSI-X and legacy INTx should chose the right one based on the msi_enabled diff --git a/Documentation/PCI/pcieaer-howto.rst b/Documentation/PCI/pcieaer-howto.rst index e00d63971695..f013f3b27c82 100644 --- a/Documentation/PCI/pcieaer-howto.rst +++ b/Documentation/PCI/pcieaer-howto.rst @@ -241,7 +241,7 @@ After reboot with new kernel or insert the module, a device file named Then, you need a user space tool named aer-inject, which can be gotten from: - https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/ + https://github.com/intel/aer-inject.git More information about aer-inject can be found in the document in its source code. diff --git a/Documentation/devicetree/bindings/dma/fsl,edma.yaml b/Documentation/devicetree/bindings/dma/fsl,edma.yaml index aa51d278cb67..acfb4b2ee7a9 100644 --- a/Documentation/devicetree/bindings/dma/fsl,edma.yaml +++ b/Documentation/devicetree/bindings/dma/fsl,edma.yaml @@ -21,8 +21,8 @@ properties: - enum: - fsl,vf610-edma - fsl,imx7ulp-edma - - fsl,imx8qm-adma - fsl,imx8qm-edma + - fsl,imx8ulp-edma - fsl,imx93-edma3 - fsl,imx93-edma4 - fsl,imx95-edma5 @@ -43,6 +43,17 @@ properties: maxItems: 64 "#dma-cells": + description: | + Specifies the number of cells needed to encode an DMA channel. + + Encode for cells number 2: + cell 0: index of dma channel mux instance. + cell 1: peripheral dma request id. + + Encode for cells number 3: + cell 0: peripheral dma request id. + cell 1: dma channel priority. + cell 2: bitmask, defined at include/dt-bindings/dma/fsl-edma.h enum: - 2 - 3 @@ -53,11 +64,18 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 33 clock-names: minItems: 1 - maxItems: 2 + maxItems: 33 + + power-domains: + description: + The number of power domains matches the number of channels, arranged + in ascending order according to their associated DMA channels. + minItems: 1 + maxItems: 64 big-endian: description: | @@ -70,7 +88,6 @@ required: - compatible - reg - interrupts - - clocks - dma-channels allOf: @@ -80,7 +97,6 @@ allOf: compatible: contains: enum: - - fsl,imx8qm-adma - fsl,imx8qm-edma - fsl,imx93-edma3 - fsl,imx93-edma4 @@ -108,6 +124,7 @@ allOf: properties: clocks: minItems: 2 + maxItems: 2 clock-names: items: - const: dmamux0 @@ -136,6 +153,7 @@ allOf: properties: clock: minItems: 2 + maxItems: 2 clock-names: items: - const: dma @@ -151,6 +169,58 @@ allOf: dma-channels: const: 32 + - if: + properties: + compatible: + contains: + const: fsl,imx8ulp-edma + then: + properties: + clocks: + minItems: 33 + clock-names: + minItems: 33 + items: + oneOf: + - const: dma + - pattern: "^ch(0[0-9]|[1-2][0-9]|3[01])$" + + interrupt-names: false + interrupts: + minItems: 32 + "#dma-cells": + const: 3 + + - if: + properties: + compatible: + contains: + enum: + - fsl,vf610-edma + - fsl,imx7ulp-edma + - fsl,imx93-edma3 + - fsl,imx93-edma4 + - fsl,imx95-edma5 + - fsl,imx8ulp-edma + - fsl,ls1028a-edma + then: + required: + - clocks + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-adma + - fsl,imx8qm-edma + then: + required: + - power-domains + else: + properties: + power-domains: false + unevaluatedProperties: false examples: @@ -206,44 +276,27 @@ examples: - | #include <dt-bindings/interrupt-controller/arm-gic.h> - #include <dt-bindings/clock/imx93-clock.h> + #include <dt-bindings/firmware/imx/rsrc.h> - dma-controller@44000000 { - compatible = "fsl,imx93-edma3"; - reg = <0x44000000 0x200000>; + dma-controller@5a9f0000 { + compatible = "fsl,imx8qm-edma"; + reg = <0x5a9f0000 0x90000>; #dma-cells = <3>; - dma-channels = <31>; - interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk IMX93_CLK_EDMA1_GATE>; - clock-names = "dma"; + dma-channels = <8>; + interrupts = <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 426 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 427 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 428 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 429 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 430 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 431 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pd IMX_SC_R_DMA_3_CH0>, + <&pd IMX_SC_R_DMA_3_CH1>, + <&pd IMX_SC_R_DMA_3_CH2>, + <&pd IMX_SC_R_DMA_3_CH3>, + <&pd IMX_SC_R_DMA_3_CH4>, + <&pd IMX_SC_R_DMA_3_CH5>, + <&pd IMX_SC_R_DMA_3_CH6>, + <&pd IMX_SC_R_DMA_3_CH7>; }; diff --git a/Documentation/devicetree/bindings/dma/fsl,imx-sdma.yaml b/Documentation/devicetree/bindings/dma/fsl,imx-sdma.yaml index 37135fa024f9..738b25b88b37 100644 --- a/Documentation/devicetree/bindings/dma/fsl,imx-sdma.yaml +++ b/Documentation/devicetree/bindings/dma/fsl,imx-sdma.yaml @@ -94,6 +94,7 @@ properties: - SAI: 24 - Multi SAI: 25 - HDMI Audio: 26 + - I2C: 27 The third cell: transfer priority ID enum: diff --git a/Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt b/Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt deleted file mode 100644 index 1ae4748730a8..000000000000 --- a/Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt +++ /dev/null @@ -1,95 +0,0 @@ -Qualcomm Technologies HIDMA Management interface - -Qualcomm Technologies HIDMA is a high speed DMA device. It only supports -memcpy and memset capabilities. It has been designed for virtualized -environments. - -Each HIDMA HW instance consists of multiple DMA channels. These channels -share the same bandwidth. The bandwidth utilization can be partitioned -among channels based on the priority and weight assignments. - -There are only two priority levels and 15 weigh assignments possible. - -Other parameters here determine how much of the system bus this HIDMA -instance can use like maximum read/write request and number of bytes to -read/write in a single burst. - -Main node required properties: -- compatible: "qcom,hidma-mgmt-1.0"; -- reg: Address range for DMA device -- dma-channels: Number of channels supported by this DMA controller. -- max-write-burst-bytes: Maximum write burst in bytes that HIDMA can - occupy the bus for in a single transaction. A memcpy requested is - fragmented to multiples of this amount. This parameter is used while - writing into destination memory. Setting this value incorrectly can - starve other peripherals in the system. -- max-read-burst-bytes: Maximum read burst in bytes that HIDMA can - occupy the bus for in a single transaction. A memcpy request is - fragmented to multiples of this amount. This parameter is used while - reading the source memory. Setting this value incorrectly can starve - other peripherals in the system. -- max-write-transactions: This value is how many times a write burst is - applied back to back while writing to the destination before yielding - the bus. -- max-read-transactions: This value is how many times a read burst is - applied back to back while reading the source before yielding the bus. -- channel-reset-timeout-cycles: Channel reset timeout in cycles for this SOC. - Once a reset is applied to the HW, HW starts a timer for reset operation - to confirm. If reset is not completed within this time, HW reports reset - failure. - -Sub-nodes: - -HIDMA has one or more DMA channels that are used to move data from one -memory location to another. - -When the OS is not in control of the management interface (i.e. it's a guest), -the channel nodes appear on their own, not under a management node. - -Required properties: -- compatible: must contain "qcom,hidma-1.0" for initial HW or - "qcom,hidma-1.1"/"qcom,hidma-1.2" for MSI capable HW. -- reg: Addresses for the transfer and event channel -- interrupts: Should contain the event interrupt -- desc-count: Number of asynchronous requests this channel can handle -- iommus: required a iommu node - -Optional properties for MSI: -- msi-parent : See the generic MSI binding described in - devicetree/bindings/interrupt-controller/msi.txt for a description of the - msi-parent property. - -Example: - -Hypervisor OS configuration: - - hidma-mgmt@f9984000 = { - compatible = "qcom,hidma-mgmt-1.0"; - reg = <0xf9984000 0x15000>; - dma-channels = <6>; - max-write-burst-bytes = <1024>; - max-read-burst-bytes = <1024>; - max-write-transactions = <31>; - max-read-transactions = <31>; - channel-reset-timeout-cycles = <0x500>; - - hidma_24: dma-controller@5c050000 { - compatible = "qcom,hidma-1.0"; - reg = <0 0x5c050000 0x0 0x1000>, - <0 0x5c0b0000 0x0 0x1000>; - interrupts = <0 389 0>; - desc-count = <10>; - iommus = <&system_mmu>; - }; - }; - -Guest OS configuration: - - hidma_24: dma-controller@5c050000 { - compatible = "qcom,hidma-1.0"; - reg = <0 0x5c050000 0x0 0x1000>, - <0 0x5c0b0000 0x0 0x1000>; - interrupts = <0 389 0>; - desc-count = <10>; - iommus = <&system_mmu>; - }; diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml index 5da8291a7de0..c21a4f073f6c 100644 --- a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml @@ -93,10 +93,10 @@ properties: data-width: $ref: /schemas/types.yaml#/definitions/uint32-array description: Data bus width per each DMA master in bytes. + minItems: 1 + maxItems: 4 items: - maxItems: 4 - items: - enum: [4, 8, 16, 32] + enum: [4, 8, 16, 32] data_width: $ref: /schemas/types.yaml#/definitions/uint32-array @@ -106,28 +106,28 @@ properties: deprecated. It' usage is discouraged in favor of data-width one. Moreover the property incorrectly permits to define data-bus width of 8 and 16 bits, which is impossible in accordance with DW DMAC IP-core data book. + minItems: 1 + maxItems: 4 items: - maxItems: 4 - items: - enum: - - 0 # 8 bits - - 1 # 16 bits - - 2 # 32 bits - - 3 # 64 bits - - 4 # 128 bits - - 5 # 256 bits - default: 0 + enum: + - 0 # 8 bits + - 1 # 16 bits + - 2 # 32 bits + - 3 # 64 bits + - 4 # 128 bits + - 5 # 256 bits + default: 0 multi-block: $ref: /schemas/types.yaml#/definitions/uint32-array description: | LLP-based multi-block transfer supported by hardware per each DMA channel. + minItems: 1 + maxItems: 8 items: - maxItems: 8 - items: - enum: [0, 1] - default: 1 + enum: [0, 1] + default: 1 snps,max-burst-len: $ref: /schemas/types.yaml#/definitions/uint32-array @@ -138,11 +138,11 @@ properties: will be from 1 to max-burst-len words. It's an array property with one cell per channel in the units determined by the value set in the CTLx.SRC_TR_WIDTH/CTLx.DST_TR_WIDTH fields (data width). + minItems: 1 + maxItems: 8 items: - maxItems: 8 - items: - enum: [4, 8, 16, 32, 64, 128, 256] - default: 256 + enum: [4, 8, 16, 32, 64, 128, 256] + default: 256 snps,dma-protection-control: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 363cf8bd150d..525f5f3932f5 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -21,6 +21,7 @@ properties: - snps,axi-dma-1.01a - intel,kmb-axi-dma - starfive,jh7110-axi-dma + - starfive,jh8100-axi-dma reg: minItems: 1 diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml new file mode 100644 index 000000000000..449b55afeb7d --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml @@ -0,0 +1,224 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/arm,mhuv3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM MHUv3 Mailbox Controller + +maintainers: + - Sudeep Holla <[email protected]> + - Cristian Marussi <[email protected]> + +description: | + The Arm Message Handling Unit (MHU) Version 3 is a mailbox controller that + enables unidirectional communications with remote processors through various + possible transport protocols. + The controller can optionally support a varying number of extensions that, in + turn, enable different kinds of transport to be used for communication. + Number, type and characteristics of each supported extension can be discovered + dynamically at runtime. + + Given the unidirectional nature of the controller, an MHUv3 mailbox controller + is composed of a MHU Sender (MHUS) containing a PostBox (PBX) block and a MHU + Receiver (MHUR) containing a MailBox (MBX) block, where + + PBX is used to + - Configure the MHU + - Send Transfers to the Receiver + - Optionally receive acknowledgment of a Transfer from the Receiver + + MBX is used to + - Configure the MHU + - Receive Transfers from the Sender + - Optionally acknowledge Transfers sent by the Sender + + Both PBX and MBX need to be present and defined in the DT description if you + need to establish a bidirectional communication, since you will have to + acquire two distinct unidirectional channels, one for each block. + + As a consequence both blocks needs to be represented separately and specified + as distinct DT nodes in order to properly describe their resources. + + Note that, though, thanks to the runtime discoverability, there is no need to + identify the type of blocks with distinct compatibles. + + Following are the MHUv3 possible extensions. + + - Doorbell Extension (DBE): DBE defines a type of channel called a Doorbell + Channel (DBCH). DBCH enables a single bit Transfer to be sent from the + Sender to Receiver. The Transfer indicates that an event has occurred. + When DBE is implemented, the number of DBCHs that an implementation of the + MHU can support is between 1 and 128, numbered starting from 0 in ascending + order and discoverable at run-time. + Each DBCH contains 32 individual fields, referred to as flags, each of which + can be used independently. It is possible for the Sender to send multiple + Transfers at once using a single DBCH, so long as each Transfer uses + a different flag in the DBCH. + Optionally, data may be transmitted through an out-of-band shared memory + region, wherein the MHU Doorbell is used strictly as an interrupt generation + mechanism, but this is out of the scope of these bindings. + + - FastChannel Extension (FCE): FCE defines a type of channel called a Fast + Channel (FCH). FCH is intended for lower overhead communication between + Sender and Receiver at the expense of determinism. An FCH allows the Sender + to update the channel value at any time, regardless of whether the previous + value has been seen by the Receiver. When the Receiver reads the channel's + content it gets the last value written to the channel. + FCH is considered lossy in nature, and means that the Sender has no way of + knowing if, or when, the Receiver will act on the Transfer. + FCHs are expected to behave as RAM which generates interrupts when writes + occur to the locations within the RAM. + When FCE is implemented, the number of FCHs that an implementation of the + MHU can support is between 1-1024, if the FastChannel word-size is 32-bits, + or between 1-512, when the FastChannel word-size is 64-bits. + FCHs are numbered from 0 in ascending order. + Note that the number of FCHs and the word-size are implementation defined, + not configurable but discoverable at run-time. + Optionally, data may be transmitted through an out-of-band shared memory + region, wherein the MHU FastChannel is used as an interrupt generation + mechanism which carries also a pointer to such out-of-band data, but this + is out of the scope of these bindings. + + - FIFO Extension (FE): FE defines a Channel type called a FIFO Channel (FFCH). + FFCH allows a Sender to send + - Multiple Transfers to the Receiver without having to wait for the + previous Transfer to be acknowledged by the Receiver, as long as the + FIFO has room for the Transfer. + - Transfers which require the Receiver to provide acknowledgment. + - Transfers which have in-band payload. + In all cases, the data is guaranteed to be observed by the Receiver in the + same order which the Sender sent it. + When FE is implemented, the number of FFCHs that an implementation of the + MHU can support is between 1 and 64, numbered starting from 0 in ascending + order. The number of FFCHs, their depth (same for all implemented FFCHs) and + the access-granularity are implementation defined, not configurable but + discoverable at run-time. + Optionally, additional data may be transmitted through an out-of-band shared + memory region, wherein the MHU FIFO is used to transmit, in order, a small + part of the payload (like a header) and a reference to the shared memory + area holding the remaining, bigger, chunk of the payload, but this is out of + the scope of these bindings. + +properties: + compatible: + const: arm,mhuv3 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 74 + + interrupt-names: + description: | + The MHUv3 controller generates a number of events some of which are used + to generate interrupts; as a consequence it can expose a varying number of + optional PBX/MBX interrupts, representing the events generated during the + operation of the various transport protocols associated with different + extensions. All interrupts of the MHU are level-sensitive. + Some of these optional interrupts are defined per-channel, where the + number of channels effectively available is implementation defined and + run-time discoverable. + In the following names are enumerated using patterns, with per-channel + interrupts implicitly capped at the maximum channels allowed by the + specification for each extension type. + For the sake of simplicity maxItems is anyway capped to a most plausible + number, assuming way less channels would be implemented than actually + possible. + + The only mandatory interrupts on the MHU are: + - combined + - mbx-fch-xfer-<N> but only if mbx-fcgrp-xfer-<N> is not implemented. + + minItems: 1 + maxItems: 74 + items: + oneOf: + - const: combined + description: PBX/MBX Combined interrupt + - const: combined-ffch + description: PBX/MBX FIFO Combined interrupt + - pattern: '^ffch-low-tide-[0-9]+$' + description: PBX/MBX FIFO Channel <N> Low Tide interrupt + - pattern: '^ffch-high-tide-[0-9]+$' + description: PBX/MBX FIFO Channel <N> High Tide interrupt + - pattern: '^ffch-flush-[0-9]+$' + description: PBX/MBX FIFO Channel <N> Flush interrupt + - pattern: '^mbx-dbch-xfer-[0-9]+$' + description: MBX Doorbell Channel <N> Transfer interrupt + - pattern: '^mbx-fch-xfer-[0-9]+$' + description: MBX FastChannel <N> Transfer interrupt + - pattern: '^mbx-fchgrp-xfer-[0-9]+$' + description: MBX FastChannel <N> Group Transfer interrupt + - pattern: '^mbx-ffch-xfer-[0-9]+$' + description: MBX FIFO Channel <N> Transfer interrupt + - pattern: '^pbx-dbch-xfer-ack-[0-9]+$' + description: PBX Doorbell Channel <N> Transfer Ack interrupt + - pattern: '^pbx-ffch-xfer-ack-[0-9]+$' + description: PBX FIFO Channel <N> Transfer Ack interrupt + + '#mbox-cells': + description: | + The first argument in the consumers 'mboxes' property represents the + extension type, the second is for the channel number while the third + depends on extension type. + + Extension types constants are defined in <dt-bindings/arm/mhuv3-dt.h>. + + Extension type for DBE is DBE_EXT and the third parameter represents the + doorbell flag number to use. + Extension type for FCE is FCE_EXT, third parameter unused. + Extension type for FE is FE_EXT, third parameter unused. + + mboxes = <&mhu DBE_EXT 0 5>; // DBE, Doorbell Channel Window 0, doorbell 5. + mboxes = <&mhu DBE_EXT 7>; // DBE, Doorbell Channel Window 1, doorbell 7. + mboxes = <&mhu FCE_EXT 0 0>; // FCE, FastChannel Window 0. + mboxes = <&mhu FCE_EXT 3 0>; // FCE, FastChannel Window 3. + mboxes = <&mhu FE_EXT 1 0>; // FE, FIFO Channel Window 1. + mboxes = <&mhu FE_EXT 7 0>; // FE, FIFO Channel Window 7. + const: 3 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - '#mbox-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + mailbox@2aaa0000 { + compatible = "arm,mhuv3"; + #mbox-cells = <3>; + reg = <0 0x2aaa0000 0 0x10000>; + clocks = <&clock 0>; + interrupt-names = "combined", "pbx-dbch-xfer-ack-1", + "ffch-high-tide-0"; + interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>; + }; + + mailbox@2ab00000 { + compatible = "arm,mhuv3"; + #mbox-cells = <3>; + reg = <0 0x2aab0000 0 0x10000>; + clocks = <&clock 0>; + interrupt-names = "combined", "mbx-dbch-xfer-1", "ffch-low-tide-0"; + interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index 79eb523b8436..982c741e6225 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -30,6 +30,7 @@ properties: - const: syscon - items: - enum: + - qcom,msm8974-apcs-kpss-global - qcom,msm8976-apcs-kpss-global - const: qcom,msm8994-apcs-kpss-global - const: syscon diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index 8f004868aad9..05e4e1d51713 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -28,6 +28,7 @@ properties: - qcom,sa8775p-ipcc - qcom,sc7280-ipcc - qcom,sc8280xp-ipcc + - qcom,sdx75-ipcc - qcom,sm6350-ipcc - qcom,sm6375-ipcc - qcom,sm8250-ipcc diff --git a/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml b/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml index a5bd90bc0712..79a21ba0f9fd 100644 --- a/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml @@ -13,7 +13,7 @@ description: Amlogic Meson PCIe host controller is based on the Synopsys DesignWare PCI core. allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/pci/snps,dw-pcie-common.yaml# # We need a select here so we don't match all nodes with 'snps,dw-pcie' diff --git a/Documentation/devicetree/bindings/pci/apple,pcie.yaml b/Documentation/devicetree/bindings/pci/apple,pcie.yaml index 215ff9a9c835..c8775f9cb071 100644 --- a/Documentation/devicetree/bindings/pci/apple,pcie.yaml +++ b/Documentation/devicetree/bindings/pci/apple,pcie.yaml @@ -85,7 +85,7 @@ required: unevaluatedProperties: false allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/interrupt-controller/msi-controller.yaml# - if: properties: diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml index 0e07ab61a48d..5434c144d2ec 100644 --- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml @@ -11,7 +11,7 @@ maintainers: - Scott Branden <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index 22491f7f8852..11f8ea33240c 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -108,7 +108,7 @@ required: - msi-controller allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/interrupt-controller/msi-controller.yaml# - if: properties: diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml index bc3c48f60fff..a8190d9b100f 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml @@ -10,7 +10,6 @@ maintainers: - Tom Joseph <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# - $ref: cdns-pcie-host.yaml# properties: @@ -25,8 +24,6 @@ properties: - const: reg - const: cfg - msi-parent: true - required: - reg - reg-names diff --git a/Documentation/devicetree/bindings/pci/cdns-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cdns-pcie-host.yaml index a6b494401ebb..f4eb82e684bd 100644 --- a/Documentation/devicetree/bindings/pci/cdns-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cdns-pcie-host.yaml @@ -10,7 +10,7 @@ maintainers: - Tom Joseph <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: cdns-pcie.yaml# properties: diff --git a/Documentation/devicetree/bindings/pci/faraday,ftpci100.yaml b/Documentation/devicetree/bindings/pci/faraday,ftpci100.yaml index 92efbf0f1297..378dd1c8e2ee 100644 --- a/Documentation/devicetree/bindings/pci/faraday,ftpci100.yaml +++ b/Documentation/devicetree/bindings/pci/faraday,ftpci100.yaml @@ -51,7 +51,7 @@ description: | <0x6000 0 0 4 &pci_intc 2>; allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie-ep.yaml new file mode 100644 index 000000000000..399efa7364c9 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie-ep.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/fsl,layerscape-pcie-ep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Layerscape PCIe Endpoint(EP) controller + +maintainers: + - Frank Li <[email protected]> + +description: + This PCIe EP controller is based on the Synopsys DesignWare PCIe IP. + + This controller derives its clocks from the Reset Configuration Word (RCW) + which is used to describe the PLL settings at the time of chip-reset. + + Also as per the available Reference Manuals, there is no specific 'version' + register available in the Freescale PCIe controller register set, + which can allow determining the underlying DesignWare PCIe controller version + information. + +properties: + compatible: + enum: + - fsl,ls2088a-pcie-ep + - fsl,ls1088a-pcie-ep + - fsl,ls1046a-pcie-ep + - fsl,ls1028a-pcie-ep + - fsl,lx2160ar2-pcie-ep + + reg: + maxItems: 2 + + reg-names: + items: + - const: regs + - const: addr_space + + fsl,pcie-scfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: A phandle to the SCFG device node. The second entry is the + physical PCIe controller index starting from '0'. This is used to get + SCFG PEXN registers. + + big-endian: + $ref: /schemas/types.yaml#/definitions/flag + description: If the PEX_LUT and PF register block is in big-endian, specify + this property. + + dma-coherent: true + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + +required: + - compatible + - reg + - reg-names + +allOf: + - if: + properties: + compatible: + enum: + - fsl,ls1028a-pcie-ep + - fsl,ls1046a-pcie-ep + - fsl,ls1088a-pcie-ep + then: + properties: + interrupt-names: + items: + - const: pme + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie_ep1: pcie-ep@3400000 { + compatible = "fsl,ls1028a-pcie-ep"; + reg = <0x00 0x03400000 0x0 0x00100000 + 0x80 0x00000000 0x8 0x00000000>; + reg-names = "regs", "addr_space"; + interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */ + interrupt-names = "pme"; + num-ib-windows = <6>; + num-ob-windows = <8>; + status = "disabled"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie.yaml new file mode 100644 index 000000000000..793986c5af7f --- /dev/null +++ b/Documentation/devicetree/bindings/pci/fsl,layerscape-pcie.yaml @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/fsl,layerscape-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Layerscape PCIe Root Complex(RC) controller + +maintainers: + - Frank Li <[email protected]> + +description: + This PCIe RC controller is based on the Synopsys DesignWare PCIe IP + + This controller derives its clocks from the Reset Configuration Word (RCW) + which is used to describe the PLL settings at the time of chip-reset. + + Also as per the available Reference Manuals, there is no specific 'version' + register available in the Freescale PCIe controller register set, + which can allow determining the underlying DesignWare PCIe controller version + information. + +properties: + compatible: + enum: + - fsl,ls1021a-pcie + - fsl,ls2080a-pcie + - fsl,ls2085a-pcie + - fsl,ls2088a-pcie + - fsl,ls1088a-pcie + - fsl,ls1046a-pcie + - fsl,ls1043a-pcie + - fsl,ls1012a-pcie + - fsl,ls1028a-pcie + - fsl,lx2160a-pcie + + reg: + maxItems: 2 + + reg-names: + items: + - const: regs + - const: config + + fsl,pcie-scfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: A phandle to the SCFG device node. The second entry is the + physical PCIe controller index starting from '0'. This is used to get + SCFG PEXN registers. + + big-endian: + $ref: /schemas/types.yaml#/definitions/flag + description: If the PEX_LUT and PF register block is in big-endian, specify + this property. + + dma-coherent: true + + msi-parent: true + + iommu-map: true + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + +required: + - compatible + - reg + - reg-names + - "#address-cells" + - "#size-cells" + - device_type + - bus-range + - ranges + - interrupts + - interrupt-names + - "#interrupt-cells" + - interrupt-map-mask + - interrupt-map + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + + - if: + properties: + compatible: + enum: + - fsl,ls1028a-pcie + - fsl,ls1046a-pcie + - fsl,ls1043a-pcie + - fsl,ls1012a-pcie + then: + properties: + interrupts: + maxItems: 2 + interrupt-names: + items: + - const: pme + - const: aer + + - if: + properties: + compatible: + enum: + - fsl,ls2080a-pcie + - fsl,ls2085a-pcie + - fsl,ls2088a-pcie + then: + properties: + interrupts: + maxItems: 1 + interrupt-names: + items: + - const: intr + + - if: + properties: + compatible: + enum: + - fsl,ls1088a-pcie + then: + properties: + interrupts: + maxItems: 1 + interrupt-names: + items: + - const: aer + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@3400000 { + compatible = "fsl,ls1088a-pcie"; + reg = <0x00 0x03400000 0x0 0x00100000>, /* controller registers */ + <0x20 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>; /* aer interrupt */ + interrupt-names = "aer"; + #address-cells = <3>; + #size-cells = <2>; + dma-coherent; + device_type = "pci"; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x20 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0x82000000 0x0 0x40000000 0x20 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + msi-parent = <&its>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic 0 0 0 109 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic 0 0 0 110 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic 0 0 0 111 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic 0 0 0 112 IRQ_TYPE_LEVEL_HIGH>; + iommu-map = <0 &smmu 0 1>; /* Fixed-up by bootloader */ + }; + }; +... diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.yaml b/Documentation/devicetree/bindings/pci/host-generic-pci.yaml index d25423aa7167..3484e0b4b412 100644 --- a/Documentation/devicetree/bindings/pci/host-generic-pci.yaml +++ b/Documentation/devicetree/bindings/pci/host-generic-pci.yaml @@ -116,7 +116,7 @@ required: - ranges allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml index debfb54a8042..3cae2e0f7f5e 100644 --- a/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml +++ b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml @@ -12,7 +12,7 @@ maintainers: description: PCI host controller found in the Intel IXP4xx SoC series. allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/intel,keembay-pcie.yaml b/Documentation/devicetree/bindings/pci/intel,keembay-pcie.yaml index 505acc4f3efc..1fd557504b10 100644 --- a/Documentation/devicetree/bindings/pci/intel,keembay-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/intel,keembay-pcie.yaml @@ -11,7 +11,7 @@ maintainers: - Srikanth Thokala <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt deleted file mode 100644 index ee8a4791a78b..000000000000 --- a/Documentation/devicetree/bindings/pci/layerscape-pci.txt +++ /dev/null @@ -1,79 +0,0 @@ -Freescale Layerscape PCIe controller - -This PCIe host controller is based on the Synopsys DesignWare PCIe IP -and thus inherits all the common properties defined in snps,dw-pcie.yaml. - -This controller derives its clocks from the Reset Configuration Word (RCW) -which is used to describe the PLL settings at the time of chip-reset. - -Also as per the available Reference Manuals, there is no specific 'version' -register available in the Freescale PCIe controller register set, -which can allow determining the underlying DesignWare PCIe controller version -information. - -Required properties: -- compatible: should contain the platform identifier such as: - RC mode: - "fsl,ls1021a-pcie" - "fsl,ls2080a-pcie", "fsl,ls2085a-pcie" - "fsl,ls2088a-pcie" - "fsl,ls1088a-pcie" - "fsl,ls1046a-pcie" - "fsl,ls1043a-pcie" - "fsl,ls1012a-pcie" - "fsl,ls1028a-pcie" - EP mode: - "fsl,ls1028a-pcie-ep", "fsl,ls-pcie-ep" - "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep" - "fsl,ls1088a-pcie-ep", "fsl,ls-pcie-ep" - "fsl,ls2088a-pcie-ep", "fsl,ls-pcie-ep" - "fsl,lx2160ar2-pcie-ep", "fsl,ls-pcie-ep" -- reg: base addresses and lengths of the PCIe controller register blocks. -- interrupts: A list of interrupt outputs of the controller. Must contain an - entry for each entry in the interrupt-names property. -- interrupt-names: It could include the following entries: - "aer": Used for interrupt line which reports AER events when - non MSI/MSI-X/INTx mode is used - "pme": Used for interrupt line which reports PME events when - non MSI/MSI-X/INTx mode is used - "intr": Used for SoCs(like ls2080a, lx2160a, ls2080a, ls2088a, ls1088a) - which has a single interrupt line for miscellaneous controller - events(could include AER and PME events). -- fsl,pcie-scfg: Must include two entries. - The first entry must be a link to the SCFG device node - The second entry is the physical PCIe controller index starting from '0'. - This is used to get SCFG PEXN registers -- dma-coherent: Indicates that the hardware IP block can ensure the coherency - of the data transferred from/to the IP block. This can avoid the software - cache flush/invalid actions, and improve the performance significantly. - -Optional properties: -- big-endian: If the PEX_LUT and PF register block is in big-endian, specify - this property. - -Example: - - pcie@3400000 { - compatible = "fsl,ls1088a-pcie"; - reg = <0x00 0x03400000 0x0 0x00100000>, /* controller registers */ - <0x20 0x00000000 0x0 0x00002000>; /* configuration space */ - reg-names = "regs", "config"; - interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>; /* aer interrupt */ - interrupt-names = "aer"; - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - dma-coherent; - num-viewport = <256>; - bus-range = <0x0 0xff>; - ranges = <0x81000000 0x0 0x00000000 0x20 0x00010000 0x0 0x00010000 /* downstream I/O */ - 0x82000000 0x0 0x40000000 0x20 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ - msi-parent = <&its>; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0000 0 0 1 &gic 0 0 0 109 IRQ_TYPE_LEVEL_HIGH>, - <0000 0 0 2 &gic 0 0 0 110 IRQ_TYPE_LEVEL_HIGH>, - <0000 0 0 3 &gic 0 0 0 111 IRQ_TYPE_LEVEL_HIGH>, - <0000 0 0 4 &gic 0 0 0 112 IRQ_TYPE_LEVEL_HIGH>; - iommu-map = <0 &smmu 0 1>; /* Fixed-up by bootloader */ - }; diff --git a/Documentation/devicetree/bindings/pci/loongson.yaml b/Documentation/devicetree/bindings/pci/loongson.yaml index a8324a9bd002..1988465e73a1 100644 --- a/Documentation/devicetree/bindings/pci/loongson.yaml +++ b/Documentation/devicetree/bindings/pci/loongson.yaml @@ -13,7 +13,7 @@ description: |+ PCI host controller found on Loongson PCHs and SoCs. allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/mediatek,mt7621-pcie.yaml b/Documentation/devicetree/bindings/pci/mediatek,mt7621-pcie.yaml index e63e6458cea8..6fba42156db6 100644 --- a/Documentation/devicetree/bindings/pci/mediatek,mt7621-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek,mt7621-pcie.yaml @@ -14,7 +14,7 @@ description: |+ with 3 Root Ports. Each Root Port supports a Gen1 1-lane Link allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: @@ -33,9 +33,12 @@ properties: patternProperties: '^pcie@[0-2],0$': type: object - $ref: /schemas/pci/pci-bus.yaml# + $ref: /schemas/pci/pci-pci-bridge.yaml# properties: + reg: + maxItems: 1 + resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml index 7e8c7a2a5f9b..76d742051f73 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml @@ -140,7 +140,7 @@ required: - interrupt-controller allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml index e8212a05b7b1..5d7aec5f54e7 100644 --- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml @@ -10,7 +10,7 @@ maintainers: - Daire McNamara <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/interrupt-controller/msi-controller.yaml# properties: diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml index 0d1b23523f62..0a39bbfcb28b 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml @@ -95,6 +95,6 @@ anyOf: - msi-map allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# additionalProperties: true diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml index cf9a6910b542..f867746b1ae5 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml @@ -130,7 +130,7 @@ anyOf: - msi-map allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml index fe38f62da066..91b81ac75592 100644 --- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml @@ -16,7 +16,9 @@ allOf: properties: compatible: items: - - const: renesas,r8a779f0-pcie-ep # R-Car S4-8 + - enum: + - renesas,r8a779f0-pcie-ep # R-Car S4-8 + - renesas,r8a779g0-pcie-ep # R-Car V4H - const: renesas,rcar-gen4-pcie-ep # R-Car Gen4 reg: diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml index ffb34339b637..955c664f1fbb 100644 --- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml @@ -16,7 +16,9 @@ allOf: properties: compatible: items: - - const: renesas,r8a779f0-pcie # R-Car S4-8 + - enum: + - renesas,r8a779f0-pcie # R-Car S4-8 + - renesas,r8a779g0-pcie # R-Car V4H - const: renesas,rcar-gen4-pcie # R-Car Gen4 reg: diff --git a/Documentation/devicetree/bindings/pci/rcar-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-pci-host.yaml index b6a7cb32f61e..666f013e3af8 100644 --- a/Documentation/devicetree/bindings/pci/rcar-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-pci-host.yaml @@ -12,7 +12,7 @@ maintainers: - Yoshihiro Shimoda <[email protected]> allOf: - - $ref: pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: @@ -77,6 +77,9 @@ properties: vpcie12v-supply: description: The 12v regulator to use for PCIe. + iommu-map: true + iommu-map-mask: true + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml b/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml index 5a0d64d3ae6b..b288cdb1ec70 100644 --- a/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml @@ -110,7 +110,7 @@ required: - "#interrupt-cells" allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - if: properties: diff --git a/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie.yaml b/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie.yaml index 531008f0b6ac..720a5f945a4e 100644 --- a/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie.yaml @@ -10,7 +10,7 @@ maintainers: - Shawn Lin <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: rockchip,rk3399-pcie-common.yaml# properties: @@ -37,6 +37,7 @@ properties: description: This property is needed if using 24MHz OSC for RC's PHY. ep-gpios: + maxItems: 1 description: pre-reset GPIO vpcie12v-supply: diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index 022055edbf9e..548f59d76ef2 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -23,7 +23,7 @@ select: - compatible allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/pci/snps,dw-pcie-common.yaml# - if: not: diff --git a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml index a20dccbafd94..0a9d10532cc8 100644 --- a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml @@ -11,7 +11,7 @@ maintainers: - Kishon Vijay Abraham I <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: @@ -55,6 +55,20 @@ properties: dma-coherent: true + num-viewport: + $ref: /schemas/types.yaml#/definitions/uint32 + + phys: + description: per-lane PHYs + minItems: 1 + maxItems: 2 + + phy-names: + minItems: 1 + maxItems: 2 + items: + pattern: '^pcie-phy[0-1]$' + required: - compatible - reg @@ -74,6 +88,7 @@ then: - dma-coherent - power-domains - msi-map + - num-viewport unevaluatedProperties: false @@ -81,6 +96,7 @@ examples: - | #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/phy/phy.h> #include <dt-bindings/soc/ti,sci_pm_domain.h> pcie0_rc: pcie@5500000 { @@ -98,9 +114,13 @@ examples: ti,syscon-pcie-id = <&scm_conf 0x0210>; ti,syscon-pcie-mode = <&scm_conf 0x4060>; bus-range = <0x0 0xff>; + num-viewport = <16>; max-link-speed = <2>; dma-coherent; interrupts = <GIC_SPI 340 IRQ_TYPE_EDGE_RISING>; msi-map = <0x0 &gic_its 0x0 0x10000>; device_type = "pci"; + num-lanes = <1>; + phys = <&serdes0 PHY_TYPE_PCIE 0>; + phy-names = "pcie-phy0"; }; diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml index b7a534cef24d..15a2658ceeef 100644 --- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml @@ -23,6 +23,10 @@ properties: items: - const: ti,j7200-pcie-host - const: ti,j721e-pcie-host + - description: PCIe controller in J722S + items: + - const: ti,j722s-pcie-host + - const: ti,j721e-pcie-host reg: maxItems: 4 @@ -68,6 +72,7 @@ properties: - 0xb00d - 0xb00f - 0xb010 + - 0xb012 - 0xb013 msi-map: true diff --git a/Documentation/devicetree/bindings/pci/versatile.yaml b/Documentation/devicetree/bindings/pci/versatile.yaml index 09748ef6b94f..294c7cd84b37 100644 --- a/Documentation/devicetree/bindings/pci/versatile.yaml +++ b/Documentation/devicetree/bindings/pci/versatile.yaml @@ -13,7 +13,7 @@ description: |+ PCI host controller found on the ARM Versatile PB board's FPGA. allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml index c41344f8a242..4770ce02fcc3 100644 --- a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml +++ b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml @@ -10,7 +10,7 @@ maintainers: - Bharat Kumar Gogada <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/xlnx,axi-pcie-host.yaml b/Documentation/devicetree/bindings/pci/xlnx,axi-pcie-host.yaml index 69b7decabd45..fb87b960a250 100644 --- a/Documentation/devicetree/bindings/pci/xlnx,axi-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/xlnx,axi-pcie-host.yaml @@ -10,7 +10,7 @@ maintainers: - Thippeswamy Havalige <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml index cbe832c23dae..9cad860c51a3 100644 --- a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml @@ -10,7 +10,7 @@ maintainers: - Thippeswamy Havalige <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# - $ref: /schemas/interrupt-controller/msi-controller.yaml# properties: diff --git a/Documentation/devicetree/bindings/pci/xlnx,xdma-host.yaml b/Documentation/devicetree/bindings/pci/xlnx,xdma-host.yaml index 0aa00b8e49b3..2f59b3a73dd2 100644 --- a/Documentation/devicetree/bindings/pci/xlnx,xdma-host.yaml +++ b/Documentation/devicetree/bindings/pci/xlnx,xdma-host.yaml @@ -10,7 +10,7 @@ maintainers: - Thippeswamy Havalige <[email protected]> allOf: - - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/pci-host-bridge.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml new file mode 100644 index 000000000000..c43e86a8c2e0 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/fsl,imx8mp-hdmi-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8MP HDMI PHY + +maintainers: + - Lucas Stach <[email protected]> + +properties: + compatible: + enum: + - fsl,imx8mp-hdmi-phy + + reg: + maxItems: 1 + + "#clock-cells": + const: 0 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: apb + - const: ref + + "#phy-cells": + const: 0 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#clock-cells" + - clocks + - clock-names + - "#phy-cells" + - power-domains + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8mp-clock.h> + #include <dt-bindings/power/imx8mp-power.h> + + phy@32fdff00 { + compatible = "fsl,imx8mp-hdmi-phy"; + reg = <0x32fdff00 0x100>; + clocks = <&clk IMX8MP_CLK_HDMI_APB>, + <&clk IMX8MP_CLK_HDMI_24M>; + clock-names = "apb", "ref"; + power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX_PHY>; + #clock-cells = <0>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt7988-xfi-tphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt7988-xfi-tphy.yaml new file mode 100644 index 000000000000..cfb3ca97f87c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/mediatek,mt7988-xfi-tphy.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/mediatek,mt7988-xfi-tphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT7988 XFI T-PHY + +maintainers: + - Daniel Golle <[email protected]> + +description: + The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes + used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in + MediaTek's 10G-capabale MT7988 SoC. + In MediaTek's SDK sources, this unit is referred to as "pextp". + +properties: + compatible: + const: mediatek,mt7988-xfi-tphy + + reg: + maxItems: 1 + + clocks: + items: + - description: XFI PHY clock + - description: XFI register clock + + clock-names: + items: + - const: xfipll + - const: topxtal + + resets: + items: + - description: Reset controller corresponding to the phy instance. + + mediatek,usxgmii-performance-errata: + $ref: /schemas/types.yaml#/definitions/flag + description: + One instance of the T-PHY on MT7988 suffers from a performance + problem in 10GBase-R mode which needs a work-around in the driver. + This flag enables a work-around ajusting an analog phy setting and + is required for XFI Port0 of the MT7988 SoC to be in compliance with + the SFP specification. + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/mediatek,mt7988-clk.h> + soc { + #address-cells = <2>; + #size-cells = <2>; + + phy@11f20000 { + compatible = "mediatek,mt7988-xfi-tphy"; + reg = <0 0x11f20000 0 0x10000>; + clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>, + <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>; + clock-names = "xfipll", "topxtal"; + resets = <&watchdog 14>; + mediatek,usxgmii-performance-errata; + #phy-cells = <0>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml new file mode 100644 index 000000000000..1f1f8863b80d --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/phy-rockchip-usbdp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip USBDP Combo PHY with Samsung IP block + +maintainers: + - Frank Wang <[email protected]> + - Zhang Yubing <[email protected]> + +properties: + compatible: + enum: + - rockchip,rk3588-usbdp-phy + + reg: + maxItems: 1 + + "#phy-cells": + description: | + Cell allows setting the type of the PHY. Possible values are: + - PHY_TYPE_USB3 + - PHY_TYPE_DP + const: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: refclk + - const: immortal + - const: pclk + - const: utmi + + resets: + maxItems: 5 + + reset-names: + items: + - const: init + - const: cmn + - const: lane + - const: pcs_apb + - const: pma_apb + + rockchip,dp-lane-mux: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 4 + items: + maximum: 3 + description: + An array of physical Type-C lanes indexes. Position of an entry + determines the DisplayPort (DP) lane index, while the value of an entry + indicates physical Type-C lane. The supported DP lanes number are 2 or 4. + e.g. for 2 lanes DP lanes map, we could have "rockchip,dp-lane-mux = <2, + 3>;", assuming DP lane0 on Type-C phy lane2, DP lane1 on Type-C phy + lane3. For 4 lanes DP lanes map, we could have "rockchip,dp-lane-mux = + <0, 1, 2, 3>;", assuming DP lane0 on Type-C phy lane0, DP lane1 on Type-C + phy lane1, DP lane2 on Type-C phy lane2, DP lane3 on Type-C phy lane3. If + DP lanes are mapped by DisplayPort Alt mode, this property is not needed. + + rockchip,u2phy-grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the syscon managing the 'usb2 phy general register files'. + + rockchip,usb-grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the syscon managing the 'usb general register files'. + + rockchip,usbdpphy-grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the syscon managing the 'usbdp phy general register files'. + + rockchip,vo-grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the syscon managing the 'video output general register files'. + When select the DP lane mapping will request its phandle. + + sbu1-dc-gpios: + description: + GPIO connected to the SBU1 line of the USB-C connector via a big resistor + (~100K) to apply a DC offset for signalling the connector orientation. + maxItems: 1 + + sbu2-dc-gpios: + description: + GPIO connected to the SBU2 line of the USB-C connector via a big resistor + (~100K) to apply a DC offset for signalling the connector orientation. + maxItems: 1 + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + mode-switch: + description: Flag the port as possible handler of altmode switching + type: boolean + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the PHY to a TypeC controller for the purpose of + handling orientation switching. + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/rockchip,rk3588-cru.h> + #include <dt-bindings/reset/rockchip,rk3588-cru.h> + + usbdp_phy0: phy@fed80000 { + compatible = "rockchip,rk3588-usbdp-phy"; + reg = <0xfed80000 0x10000>; + #phy-cells = <1>; + clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>, + <&cru CLK_USBDP_PHY0_IMMORTAL>, + <&cru PCLK_USBDPPHY0>, + <&u2phy0>; + clock-names = "refclk", "immortal", "pclk", "utmi"; + resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>, + <&cru SRST_USBDP_COMBO_PHY0_CMN>, + <&cru SRST_USBDP_COMBO_PHY0_LANE>, + <&cru SRST_USBDP_COMBO_PHY0_PCS>, + <&cru SRST_P_USBDPPHY0>; + reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb"; + rockchip,u2phy-grf = <&usb2phy0_grf>; + rockchip,usb-grf = <&usb_grf>; + rockchip,usbdpphy-grf = <&usbdpphy0_grf>; + rockchip,vo-grf = <&vo0_grf>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml index 6566353f1a02..4e15d90d08b0 100644 --- a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml @@ -21,6 +21,7 @@ properties: - qcom,sc8180x-edp-phy - qcom,sc8280xp-dp-phy - qcom,sc8280xp-edp-phy + - qcom,x1e80100-dp-phy reg: items: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index ba966a78a128..16634f73bdcf 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -88,11 +88,11 @@ properties: - description: offset of PCIe 4-lane configuration register - description: offset of configuration bit for this PHY - "#clock-cells": - const: 0 + "#clock-cells": true clock-output-names: - maxItems: 1 + minItems: 1 + maxItems: 2 "#phy-cells": const: 0 @@ -198,7 +198,6 @@ allOf: enum: - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen4x2-pcie-phy - - qcom,x1e80100-qmp-gen3x2-pcie-phy - qcom,x1e80100-qmp-gen4x2-pcie-phy then: properties: @@ -213,6 +212,27 @@ allOf: reset-names: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - qcom,sm8450-qmp-gen4x2-pcie-phy + - qcom,sm8550-qmp-gen4x2-pcie-phy + - qcom,sm8650-qmp-gen4x2-pcie-phy + then: + properties: + clock-output-names: + minItems: 2 + "#clock-cells": + const: 1 + else: + properties: + clock-output-names: + maxItems: 1 + "#clock-cells": + const: 0 + examples: - | #include <dt-bindings/clock/qcom,gcc-sc8280xp.h> diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index 91a6cc38ff7f..f9cfbd0b2de6 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -32,6 +32,7 @@ properties: - qcom,sm8250-qmp-ufs-phy - qcom,sm8350-qmp-ufs-phy - qcom,sm8450-qmp-ufs-phy + - qcom,sm8475-qmp-ufs-phy - qcom,sm8550-qmp-ufs-phy - qcom,sm8650-qmp-ufs-phy @@ -71,7 +72,6 @@ required: - reg - clocks - clock-names - - power-domains - resets - reset-names - vdda-phy-supply @@ -86,6 +86,7 @@ allOf: enum: - qcom,msm8998-qmp-ufs-phy - qcom,sa8775p-qmp-ufs-phy + - qcom,sc7180-qmp-ufs-phy - qcom,sc7280-qmp-ufs-phy - qcom,sc8180x-qmp-ufs-phy - qcom,sc8280xp-qmp-ufs-phy @@ -98,6 +99,7 @@ allOf: - qcom,sm8250-qmp-ufs-phy - qcom,sm8350-qmp-ufs-phy - qcom,sm8450-qmp-ufs-phy + - qcom,sm8475-qmp-ufs-phy - qcom,sm8550-qmp-ufs-phy - qcom,sm8650-qmp-ufs-phy then: @@ -127,6 +129,21 @@ allOf: - const: ref - const: qref + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8996-qmp-ufs-phy + - qcom,msm8998-qmp-ufs-phy + then: + properties: + power-domains: + false + else: + required: + - power-domains + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml index 1e2d4ddc5391..325585bc881b 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml @@ -20,6 +20,7 @@ properties: - qcom,ipq8074-qmp-usb3-phy - qcom,ipq9574-qmp-usb3-phy - qcom,msm8996-qmp-usb3-phy + - com,qdu1000-qmp-usb3-uni-phy - qcom,sa8775p-qmp-usb3-uni-phy - qcom,sc8280xp-qmp-usb3-uni-phy - qcom,sdm845-qmp-usb3-uni-phy @@ -109,6 +110,7 @@ allOf: compatible: contains: enum: + - qcom,qdu1000-qmp-usb3-uni-phy - qcom,sa8775p-qmp-usb3-uni-phy - qcom,sc8280xp-qmp-usb3-uni-phy - qcom,sm8150-qmp-usb3-uni-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml index 24c733c10e0e..90d79491e281 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml @@ -20,7 +20,9 @@ properties: - enum: - qcom,pm7550ba-eusb2-repeater - const: qcom,pm8550b-eusb2-repeater - - const: qcom,pm8550b-eusb2-repeater + - enum: + - qcom,pm8550b-eusb2-repeater + - qcom,smb2360-eusb2-repeater reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml index 0f200e3f97a9..519c2b403f66 100644 --- a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml @@ -15,9 +15,6 @@ description: | properties: compatible: oneOf: - - enum: - - qcom,sc8180x-usb-hs-phy - - qcom,usb-snps-femto-v2-phy - items: - enum: - qcom,sa8775p-usb-hs-phy @@ -25,7 +22,9 @@ properties: - const: qcom,usb-snps-hs-5nm-phy - items: - enum: + - qcom,qdu1000-usb-hs-phy - qcom,sc7280-usb-hs-phy + - qcom,sc8180x-usb-hs-phy - qcom,sdx55-usb-hs-phy - qcom,sdx65-usb-hs-phy - qcom,sm6375-usb-hs-phy diff --git a/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml index c4fbffcde6e4..ba67dca5a446 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml @@ -54,6 +54,16 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: phandle to the syscon managing the pipe "general register files" + rockchip,rx-common-refclk-mode: + description: which lanes (by position) should be configured to run in + RX common reference clock mode. 0 means disabled, 1 means enabled. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + items: + minimum: 0 + maximum: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml index 782f975b43ae..f402e31bf58d 100644 --- a/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml @@ -15,6 +15,7 @@ properties: compatible: enum: + - google,gs101-ufs-phy - samsung,exynos7-ufs-phy - samsung,exynosautov9-ufs-phy - tesla,fsd-ufs-phy diff --git a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml index 507f98f73d23..c5dc3c2820d7 100644 --- a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml +++ b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml @@ -19,6 +19,7 @@ properties: - mediatek,mt8183-scp - mediatek,mt8186-scp - mediatek,mt8188-scp + - mediatek,mt8188-scp-dual - mediatek,mt8192-scp - mediatek,mt8195-scp - mediatek,mt8195-scp-dual @@ -194,6 +195,7 @@ allOf: properties: compatible: enum: + - mediatek,mt8188-scp-dual - mediatek,mt8195-scp-dual then: properties: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml index 971734085d51..4d2055f283ac 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml @@ -231,7 +231,6 @@ allOf: - const: snoc_axi - const: mnoc_axi - const: qdss - glink-edge: false required: - pll-supply - smd-edge diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml index 06f5f93f62a9..bca59394aef4 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml @@ -81,7 +81,11 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle-array description: Phandle reference to a syscon representing TCSR followed by the - three offsets within syscon for q6, modem and nc halt registers. + offset within syscon for q6 halt register. + items: + - items: + - description: phandle to TCSR syscon region + - description: offset to the Q6 halt register qcom,smem-states: $ref: /schemas/types.yaml#/definitions/phandle-array diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml index 9381c7022ff4..f4118b2da5f6 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml @@ -89,7 +89,11 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle-array description: Phandle reference to a syscon representing TCSR followed by the - three offsets within syscon for q6, modem and nc halt registers. + offset within syscon for q6 halt register. + items: + - items: + - description: phandle to TCSR syscon region + - description: offset to the Q6 halt register qcom,qmp: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml index 20df83a96ef3..a3c74871457f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml @@ -81,7 +81,11 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle-array description: Phandle reference to a syscon representing TCSR followed by the - three offsets within syscon for q6, modem and nc halt registers. + offset within syscon for q6 halt register. + items: + - items: + - description: phandle to TCSR syscon region + - description: offset to the Q6 halt register qcom,smem-states: $ref: /schemas/types.yaml#/definitions/phandle-array diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml index 02c85b420c1a..63500b1a0f6f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml @@ -61,6 +61,7 @@ properties: description: Three entries specifying the outgoing ipc bit used for signaling the remote processor. + deprecated: true qcom,smd-edge: $ref: /schemas/types.yaml#/definitions/uint32 @@ -111,7 +112,7 @@ examples: smd-edge { interrupts = <GIC_SPI 156 IRQ_TYPE_EDGE_RISING>; - qcom,ipc = <&apcs 8 8>; + mboxes = <&apcs 8>; qcom,smd-edge = <1>; }; }; diff --git a/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml b/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml index 78aac69f1060..6f13da11f593 100644 --- a/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml +++ b/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml @@ -18,11 +18,26 @@ description: | properties: compatible: - const: xlnx,zynqmp-r5fss + enum: + - xlnx,zynqmp-r5fss + - xlnx,versal-r5fss + - xlnx,versal-net-r52fss + + "#address-cells": + const: 2 + + "#size-cells": + const: 2 + + ranges: + description: | + Standard ranges definition providing address translations for + local R5F TCM address spaces to bus addresses. xlnx,cluster-mode: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2] + default: 1 description: | The RPU MPCore can operate in split mode (Dual-processor performance), Safety lock-step mode(Both RPU cores execute the same code in lock-step, @@ -36,8 +51,16 @@ properties: 1: lockstep mode (default) 2: single cpu mode + xlnx,tcm-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Configure RPU TCM + 0: split mode + 1: lockstep mode + patternProperties: - "^r5f-[a-f0-9]+$": + "^r(.*)@[0-9a-f]+$": type: object description: | The RPU is located in the Low Power Domain of the Processor Subsystem. @@ -52,10 +75,22 @@ patternProperties: properties: compatible: - const: xlnx,zynqmp-r5f + enum: + - xlnx,zynqmp-r5f + - xlnx,versal-r5f + - xlnx,versal-net-r52f + + reg: + minItems: 1 + maxItems: 4 + + reg-names: + minItems: 1 + maxItems: 4 power-domains: - maxItems: 1 + minItems: 2 + maxItems: 5 mboxes: minItems: 1 @@ -101,35 +136,235 @@ patternProperties: required: - compatible + - reg + - reg-names - power-domains - unevaluatedProperties: false - required: - compatible + - "#address-cells" + - "#size-cells" + - ranges + +allOf: + - if: + properties: + compatible: + contains: + enum: + - xlnx,versal-net-r52fss + then: + properties: + xlnx,tcm-mode: false + + patternProperties: + "^r52f@[0-9a-f]+$": + type: object + + properties: + reg: + minItems: 1 + items: + - description: ATCM internal memory + - description: BTCM internal memory + - description: CTCM internal memory + + reg-names: + minItems: 1 + items: + - const: atcm0 + - const: btcm0 + - const: ctcm0 + + power-domains: + minItems: 2 + items: + - description: RPU core power domain + - description: ATCM power domain + - description: BTCM power domain + - description: CTCM power domain + + - if: + properties: + compatible: + contains: + enum: + - xlnx,zynqmp-r5fss + - xlnx,versal-r5fss + then: + if: + properties: + xlnx,cluster-mode: + enum: [1, 2] + then: + properties: + xlnx,tcm-mode: + enum: [1] + + patternProperties: + "^r5f@[0-9a-f]+$": + type: object + + properties: + reg: + minItems: 1 + items: + - description: ATCM internal memory + - description: BTCM internal memory + - description: extra ATCM memory in lockstep mode + - description: extra BTCM memory in lockstep mode + + reg-names: + minItems: 1 + items: + - const: atcm0 + - const: btcm0 + - const: atcm1 + - const: btcm1 + + power-domains: + minItems: 2 + items: + - description: RPU core power domain + - description: ATCM power domain + - description: BTCM power domain + - description: second ATCM power domain + - description: second BTCM power domain + + required: + - xlnx,tcm-mode + + else: + properties: + xlnx,tcm-mode: + enum: [0] + + patternProperties: + "^r5f@[0-9a-f]+$": + type: object + + properties: + reg: + minItems: 1 + items: + - description: ATCM internal memory + - description: BTCM internal memory + + reg-names: + minItems: 1 + items: + - const: atcm0 + - const: btcm0 + + power-domains: + minItems: 2 + items: + - description: RPU core power domain + - description: ATCM power domain + - description: BTCM power domain + + required: + - xlnx,tcm-mode additionalProperties: false examples: - | - remoteproc { - compatible = "xlnx,zynqmp-r5fss"; - xlnx,cluster-mode = <1>; - - r5f-0 { - compatible = "xlnx,zynqmp-r5f"; - power-domains = <&zynqmp_firmware 0x7>; - memory-region = <&rproc_0_fw_image>, <&rpu0vdev0buffer>, <&rpu0vdev0vring0>, <&rpu0vdev0vring1>; - mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>; - mbox-names = "tx", "rx"; + #include <dt-bindings/power/xlnx-zynqmp-power.h> + + // Split mode configuration + soc { + #address-cells = <2>; + #size-cells = <2>; + + remoteproc@ffe00000 { + compatible = "xlnx,zynqmp-r5fss"; + xlnx,cluster-mode = <0>; + xlnx,tcm-mode = <0>; + + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0xffe00000 0x0 0x10000>, + <0x0 0x20000 0x0 0xffe20000 0x0 0x10000>, + <0x1 0x0 0x0 0xffe90000 0x0 0x10000>, + <0x1 0x20000 0x0 0xffeb0000 0x0 0x10000>; + + r5f@0 { + compatible = "xlnx,zynqmp-r5f"; + reg = <0x0 0x0 0x0 0x10000>, <0x0 0x20000 0x0 0x10000>; + reg-names = "atcm0", "btcm0"; + power-domains = <&zynqmp_firmware PD_RPU_0>, + <&zynqmp_firmware PD_R5_0_ATCM>, + <&zynqmp_firmware PD_R5_0_BTCM>; + memory-region = <&rproc_0_fw_image>, <&rpu0vdev0buffer>, + <&rpu0vdev0vring0>, <&rpu0vdev0vring1>; + mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>; + mbox-names = "tx", "rx"; + }; + + r5f@1 { + compatible = "xlnx,zynqmp-r5f"; + reg = <0x1 0x0 0x0 0x10000>, <0x1 0x20000 0x0 0x10000>; + reg-names = "atcm0", "btcm0"; + power-domains = <&zynqmp_firmware PD_RPU_1>, + <&zynqmp_firmware PD_R5_1_ATCM>, + <&zynqmp_firmware PD_R5_1_BTCM>; + memory-region = <&rproc_1_fw_image>, <&rpu1vdev0buffer>, + <&rpu1vdev0vring0>, <&rpu1vdev0vring1>; + mboxes = <&ipi_mailbox_rpu1 0>, <&ipi_mailbox_rpu1 1>; + mbox-names = "tx", "rx"; + }; }; + }; + + - | + //Lockstep configuration + soc { + #address-cells = <2>; + #size-cells = <2>; + + remoteproc@ffe00000 { + compatible = "xlnx,zynqmp-r5fss"; + xlnx,cluster-mode = <1>; + xlnx,tcm-mode = <1>; + + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0xffe00000 0x0 0x10000>, + <0x0 0x20000 0x0 0xffe20000 0x0 0x10000>, + <0x0 0x10000 0x0 0xffe10000 0x0 0x10000>, + <0x0 0x30000 0x0 0xffe30000 0x0 0x10000>; + + r5f@0 { + compatible = "xlnx,zynqmp-r5f"; + reg = <0x0 0x0 0x0 0x10000>, + <0x0 0x20000 0x0 0x10000>, + <0x0 0x10000 0x0 0x10000>, + <0x0 0x30000 0x0 0x10000>; + reg-names = "atcm0", "btcm0", "atcm1", "btcm1"; + power-domains = <&zynqmp_firmware PD_RPU_0>, + <&zynqmp_firmware PD_R5_0_ATCM>, + <&zynqmp_firmware PD_R5_0_BTCM>, + <&zynqmp_firmware PD_R5_1_ATCM>, + <&zynqmp_firmware PD_R5_1_BTCM>; + memory-region = <&rproc_0_fw_image>, <&rpu0vdev0buffer>, + <&rpu0vdev0vring0>, <&rpu0vdev0vring1>; + mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>; + mbox-names = "tx", "rx"; + }; - r5f-1 { - compatible = "xlnx,zynqmp-r5f"; - power-domains = <&zynqmp_firmware 0x8>; - memory-region = <&rproc_1_fw_image>, <&rpu1vdev0buffer>, <&rpu1vdev0vring0>, <&rpu1vdev0vring1>; - mboxes = <&ipi_mailbox_rpu1 0>, <&ipi_mailbox_rpu1 1>; - mbox-names = "tx", "rx"; + r5f@1 { + compatible = "xlnx,zynqmp-r5f"; + reg = <0x1 0x0 0x0 0x10000>, <0x1 0x20000 0x0 0x10000>; + reg-names = "atcm0", "btcm0"; + power-domains = <&zynqmp_firmware PD_RPU_1>, + <&zynqmp_firmware PD_R5_1_ATCM>, + <&zynqmp_firmware PD_R5_1_BTCM>; + memory-region = <&rproc_1_fw_image>, <&rpu1vdev0buffer>, + <&rpu1vdev0vring0>, <&rpu1vdev0vring1>; + mboxes = <&ipi_mailbox_rpu1 0>, <&ipi_mailbox_rpu1 1>; + mbox-names = "tx", "rx"; + }; }; }; ... diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index f2b44c2400c6..92bffcc6747a 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1134,3 +1134,10 @@ superblock of the main block device, i.e., the one stored in sb->s_bdev. Block device freezing now works for any block device owned by a given superblock, not just the main block device. The get_active_super() helper and bd_fsfreeze_sb pointer are gone. + +--- + +**mandatory** + +set_blocksize() takes opened struct file instead of struct block_device now +and it *must* be opened exclusive. diff --git a/Documentation/translations/zh_CN/PCI/msi-howto.rst b/Documentation/translations/zh_CN/PCI/msi-howto.rst index 1b9b5ea790d8..95baadf767e4 100644 --- a/Documentation/translations/zh_CN/PCI/msi-howto.rst +++ b/Documentation/translations/zh_CN/PCI/msi-howto.rst @@ -88,7 +88,7 @@ MSI功能。 如果设备对最小数量的向量有要求,驱动程序可以传递一个min_vecs参数,设置为这个限制, 如果PCI核不能满足最小数量的向量,将返回-ENOSPC。 -flags参数用来指定设备和驱动程序可以使用哪种类型的中断(PCI_IRQ_LEGACY, PCI_IRQ_MSI, +flags参数用来指定设备和驱动程序可以使用哪种类型的中断(PCI_IRQ_INTX, PCI_IRQ_MSI, PCI_IRQ_MSIX)。一个方便的短语(PCI_IRQ_ALL_TYPES)也可以用来要求任何可能的中断类型。 如果PCI_IRQ_AFFINITY标志被设置,pci_alloc_irq_vectors()将把中断分散到可用的CPU上。 diff --git a/Documentation/translations/zh_CN/PCI/pci.rst b/Documentation/translations/zh_CN/PCI/pci.rst index 83c2a41d38d3..347f5c3f5ce9 100644 --- a/Documentation/translations/zh_CN/PCI/pci.rst +++ b/Documentation/translations/zh_CN/PCI/pci.rst @@ -304,7 +304,7 @@ MSI-X可以分配几个单独的向量。 的PCI_IRQ_MSI和/或PCI_IRQ_MSIX标志来启用MSI功能。这将导致PCI支持将CPU向量数 据编程到PCI设备功能寄存器中。许多架构、芯片组或BIOS不支持MSI或MSI-X,调用 ``pci_alloc_irq_vectors`` 时只使用PCI_IRQ_MSI和PCI_IRQ_MSIX标志会失败, -所以尽量也要指定 ``PCI_IRQ_LEGACY`` 。 +所以尽量也要指定 ``PCI_IRQ_INTX`` 。 对MSI/MSI-X和传统INTx有不同中断处理程序的驱动程序应该在调用 ``pci_alloc_irq_vectors`` 后根据 ``pci_dev``结构体中的 ``msi_enabled`` diff --git a/MAINTAINERS b/MAINTAINERS index 82f9fb4c0493..94872dce51ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6181,7 +6181,6 @@ F: drivers/mtd/nand/raw/denali* DESIGNWARE EDMA CORE IP DRIVER M: Manivannan Sadhasivam <[email protected]> -R: Gustavo Pimentel <[email protected]> R: Serge Semin <[email protected]> S: Maintained @@ -9313,6 +9312,7 @@ S: Maintained F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml F: arch/arm64/boot/dts/exynos/google/ F: drivers/clk/samsung/clk-gs101.c +F: drivers/phy/samsung/phy-gs101-ufs.c F: include/dt-bindings/clock/google,gs101.h K: [gG]oogle.?[tT]ensor @@ -11090,7 +11090,7 @@ F: drivers/idle/intel_idle.c INTEL IDXD DRIVER M: Fenghua Yu <[email protected]> -M: Dave Jiang <[email protected]> +R: Dave Jiang <[email protected]> S: Supported F: drivers/dma/idxd/* @@ -13195,6 +13195,15 @@ F: Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml F: drivers/mailbox/arm_mhuv2.c F: include/linux/mailbox/arm_mhuv2_message.h +MAILBOX ARM MHUv3 +M: Sudeep Holla <[email protected]> +M: Cristian Marussi <[email protected]> +L: [email protected] (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml +F: drivers/mailbox/arm_mhuv3.c + MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 M: Alejandro Colomar <[email protected]> @@ -13979,6 +13988,7 @@ L: [email protected] S: Maintained F: drivers/net/phy/mediatek-ge-soc.c F: drivers/net/phy/mediatek-ge.c +F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c MEDIATEK I2C CONTROLLER DRIVER M: Qii Wang <[email protected]> diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index d9651f15ae4f..340769242dea 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -184,7 +184,6 @@ static int x86_msi_prepare(struct irq_domain *domain, struct device *dev, alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSI; return 0; case DOMAIN_BUS_PCI_DEVICE_MSIX: - case DOMAIN_BUS_PCI_DEVICE_IMS: alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX; return 0; default: @@ -229,10 +228,6 @@ static bool x86_init_dev_msi_info(struct device *dev, struct irq_domain *domain, case DOMAIN_BUS_PCI_DEVICE_MSI: case DOMAIN_BUS_PCI_DEVICE_MSIX: break; - case DOMAIN_BUS_PCI_DEVICE_IMS: - if (!(pops->supported_flags & MSI_FLAG_PCI_IMS)) - return false; - break; default: WARN_ON_ONCE(1); return false; diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 0cc9520666ef..39255f0eb14d 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -518,7 +518,34 @@ static bool __ref pci_mmcfg_reserved(struct device *dev, { struct resource *conflict; - if (!early && !acpi_disabled) { + if (early) { + + /* + * Don't try to do this check unless configuration type 1 + * is available. How about type 2? + */ + + /* + * 946f2ee5c731 ("Check that MCFG points to an e820 + * reserved area") added this E820 check in 2006 to work + * around BIOS defects. + * + * Per PCI Firmware r3.3, sec 4.1.2, ECAM space must be + * reserved by a PNP0C02 resource, but it need not be + * mentioned in E820. Before the ACPI interpreter is + * available, we can't check for PNP0C02 resources, so + * there's no reliable way to verify the region in this + * early check. Keep it only for the old machines that + * motivated 946f2ee5c731. + */ + if (dmi_get_bios_year() < 2016 && raw_pci_ops) + return is_mmconf_reserved(e820__mapped_all, cfg, dev, + "E820 entry"); + + return true; + } + + if (!acpi_disabled) { if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, "ACPI motherboard resource")) return true; @@ -551,16 +578,7 @@ static bool __ref pci_mmcfg_reserved(struct device *dev, * For MCFG information constructed from hotpluggable host bridge's * _CBA method, just assume it's reserved. */ - if (pci_mmcfg_running_state) - return true; - - /* Don't try to do this check unless configuration - type 1 is available. how about type 2 ?*/ - if (raw_pci_ops) - return is_mmconf_reserved(e820__mapped_all, cfg, dev, - "E820 entry"); - - return false; + return pci_mmcfg_running_state; } static void __init pci_mmcfg_reject_broken(int early) diff --git a/arch/x86/pci/olpc.c b/arch/x86/pci/olpc.c index f3aab76e357a..4b18c6404363 100644 --- a/arch/x86/pci/olpc.c +++ b/arch/x86/pci/olpc.c @@ -154,9 +154,6 @@ static const uint32_t ehci_hdr[] = { /* dev f function 4 - devfn = 7d */ 0x0, 0x40, 0x0, 0x40a, /* CapPtr INT-D, IRQA */ 0xc8020001, 0x0, 0x0, 0x0, /* Capabilities - 40 is R/O, 44 is mask 8103 (power control) */ -#if 0 - 0x1, 0x40080000, 0x0, 0x0, /* EECP - see EHCI spec section 2.1.7 */ -#endif 0x01000001, 0x0, 0x0, 0x0, /* EECP - see EHCI spec section 2.1.7 */ 0x2020, 0x0, 0x0, 0x0, /* (EHCI page 8) 60 SBRN (R/O), 61 FLADJ (R/W), PORTWAKECAP */ diff --git a/block/bdev.c b/block/bdev.c index 2af3dca56f3d..5a03c93ba222 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -43,6 +43,11 @@ static inline struct bdev_inode *BDEV_I(struct inode *inode) return container_of(inode, struct bdev_inode, vfs_inode); } +static inline struct inode *BD_INODE(struct block_device *bdev) +{ + return &container_of(bdev, struct bdev_inode, bdev)->vfs_inode; +} + struct block_device *I_BDEV(struct inode *inode) { return &BDEV_I(inode)->bdev; @@ -57,7 +62,7 @@ EXPORT_SYMBOL(file_bdev); static void bdev_write_inode(struct block_device *bdev) { - struct inode *inode = bdev->bd_inode; + struct inode *inode = BD_INODE(bdev); int ret; spin_lock(&inode->i_lock); @@ -76,7 +81,7 @@ static void bdev_write_inode(struct block_device *bdev) /* Kill _all_ buffers and pagecache , dirty or not.. */ static void kill_bdev(struct block_device *bdev) { - struct address_space *mapping = bdev->bd_inode->i_mapping; + struct address_space *mapping = bdev->bd_mapping; if (mapping_empty(mapping)) return; @@ -88,7 +93,7 @@ static void kill_bdev(struct block_device *bdev) /* Invalidate clean unused buffers and pagecache. */ void invalidate_bdev(struct block_device *bdev) { - struct address_space *mapping = bdev->bd_inode->i_mapping; + struct address_space *mapping = bdev->bd_mapping; if (mapping->nrpages) { invalidate_bh_lrus(); @@ -116,7 +121,7 @@ int truncate_bdev_range(struct block_device *bdev, blk_mode_t mode, goto invalidate; } - truncate_inode_pages_range(bdev->bd_inode->i_mapping, lstart, lend); + truncate_inode_pages_range(bdev->bd_mapping, lstart, lend); if (!(mode & BLK_OPEN_EXCL)) bd_abort_claiming(bdev, truncate_bdev_range); return 0; @@ -126,7 +131,7 @@ invalidate: * Someone else has handle exclusively open. Try invalidating instead. * The 'end' argument is inclusive so the rounding is safe. */ - return invalidate_inode_pages2_range(bdev->bd_inode->i_mapping, + return invalidate_inode_pages2_range(bdev->bd_mapping, lstart >> PAGE_SHIFT, lend >> PAGE_SHIFT); } @@ -134,18 +139,21 @@ invalidate: static void set_init_blocksize(struct block_device *bdev) { unsigned int bsize = bdev_logical_block_size(bdev); - loff_t size = i_size_read(bdev->bd_inode); + loff_t size = i_size_read(BD_INODE(bdev)); while (bsize < PAGE_SIZE) { if (size & bsize) break; bsize <<= 1; } - bdev->bd_inode->i_blkbits = blksize_bits(bsize); + BD_INODE(bdev)->i_blkbits = blksize_bits(bsize); } -int set_blocksize(struct block_device *bdev, int size) +int set_blocksize(struct file *file, int size) { + struct inode *inode = file->f_mapping->host; + struct block_device *bdev = I_BDEV(inode); + /* Size must be a power of two, and between 512 and PAGE_SIZE */ if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size)) return -EINVAL; @@ -154,10 +162,13 @@ int set_blocksize(struct block_device *bdev, int size) if (size < bdev_logical_block_size(bdev)) return -EINVAL; + if (!file->private_data) + return -EINVAL; + /* Don't change the size if it is same as current */ - if (bdev->bd_inode->i_blkbits != blksize_bits(size)) { + if (inode->i_blkbits != blksize_bits(size)) { sync_blockdev(bdev); - bdev->bd_inode->i_blkbits = blksize_bits(size); + inode->i_blkbits = blksize_bits(size); kill_bdev(bdev); } return 0; @@ -167,7 +178,7 @@ EXPORT_SYMBOL(set_blocksize); int sb_set_blocksize(struct super_block *sb, int size) { - if (set_blocksize(sb->s_bdev, size)) + if (set_blocksize(sb->s_bdev_file, size)) return 0; /* If we get here, we know size is power of two * and it's value is between 512 and PAGE_SIZE */ @@ -192,7 +203,7 @@ int sync_blockdev_nowait(struct block_device *bdev) { if (!bdev) return 0; - return filemap_flush(bdev->bd_inode->i_mapping); + return filemap_flush(bdev->bd_mapping); } EXPORT_SYMBOL_GPL(sync_blockdev_nowait); @@ -204,13 +215,13 @@ int sync_blockdev(struct block_device *bdev) { if (!bdev) return 0; - return filemap_write_and_wait(bdev->bd_inode->i_mapping); + return filemap_write_and_wait(bdev->bd_mapping); } EXPORT_SYMBOL(sync_blockdev); int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend) { - return filemap_write_and_wait_range(bdev->bd_inode->i_mapping, + return filemap_write_and_wait_range(bdev->bd_mapping, lstart, lend); } EXPORT_SYMBOL(sync_blockdev_range); @@ -412,7 +423,7 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno) spin_lock_init(&bdev->bd_size_lock); mutex_init(&bdev->bd_holder_lock); bdev->bd_partno = partno; - bdev->bd_inode = inode; + bdev->bd_mapping = &inode->i_data; bdev->bd_queue = disk->queue; if (partno) bdev->bd_has_submit_bio = disk->part0->bd_has_submit_bio; @@ -430,19 +441,30 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno) void bdev_set_nr_sectors(struct block_device *bdev, sector_t sectors) { spin_lock(&bdev->bd_size_lock); - i_size_write(bdev->bd_inode, (loff_t)sectors << SECTOR_SHIFT); + i_size_write(BD_INODE(bdev), (loff_t)sectors << SECTOR_SHIFT); bdev->bd_nr_sectors = sectors; spin_unlock(&bdev->bd_size_lock); } void bdev_add(struct block_device *bdev, dev_t dev) { + struct inode *inode = BD_INODE(bdev); if (bdev_stable_writes(bdev)) - mapping_set_stable_writes(bdev->bd_inode->i_mapping); + mapping_set_stable_writes(bdev->bd_mapping); bdev->bd_dev = dev; - bdev->bd_inode->i_rdev = dev; - bdev->bd_inode->i_ino = dev; - insert_inode_hash(bdev->bd_inode); + inode->i_rdev = dev; + inode->i_ino = dev; + insert_inode_hash(inode); +} + +void bdev_unhash(struct block_device *bdev) +{ + remove_inode_hash(BD_INODE(bdev)); +} + +void bdev_drop(struct block_device *bdev) +{ + iput(BD_INODE(bdev)); } long nr_blockdev_pages(void) @@ -917,7 +939,7 @@ int bdev_open(struct block_device *bdev, blk_mode_t mode, void *holder, bdev_file->f_mode |= FMODE_NOWAIT; if (mode & BLK_OPEN_RESTRICT_WRITES) bdev_file->f_mode |= FMODE_WRITE_RESTRICTED; - bdev_file->f_mapping = bdev->bd_inode->i_mapping; + bdev_file->f_mapping = bdev->bd_mapping; bdev_file->f_wb_err = filemap_sample_wb_err(bdev_file->f_mapping); bdev_file->private_data = holder; @@ -979,13 +1001,13 @@ struct file *bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, return ERR_PTR(-ENXIO); flags = blk_to_file_flags(mode); - bdev_file = alloc_file_pseudo_noaccount(bdev->bd_inode, + bdev_file = alloc_file_pseudo_noaccount(BD_INODE(bdev), blockdev_mnt, "", flags | O_LARGEFILE, &def_blk_fops); if (IS_ERR(bdev_file)) { blkdev_put_no_open(bdev); return bdev_file; } - ihold(bdev->bd_inode); + ihold(BD_INODE(bdev)); ret = bdev_open(bdev, mode, holder, hops, bdev_file); if (ret) { @@ -1260,6 +1282,18 @@ void bdev_statx_dioalign(struct inode *inode, struct kstat *stat) blkdev_put_no_open(bdev); } +bool disk_live(struct gendisk *disk) +{ + return !inode_unhashed(BD_INODE(disk->part0)); +} +EXPORT_SYMBOL_GPL(disk_live); + +unsigned int block_size(struct block_device *bdev) +{ + return 1 << BD_INODE(bdev)->i_blkbits; +} +EXPORT_SYMBOL_GPL(block_size); + static int __init setup_bdev_allow_write_mounted(char *str) { if (kstrtobool(str, &bdev_allow_write_mounted)) diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 48e5e3bbb89c..57d367ada1f2 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -416,7 +416,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode, op = REQ_OP_ZONE_RESET; /* Invalidate the page cache, including dirty pages. */ - filemap_invalidate_lock(bdev->bd_inode->i_mapping); + filemap_invalidate_lock(bdev->bd_mapping); ret = blkdev_truncate_zone_range(bdev, mode, &zrange); if (ret) goto fail; @@ -438,7 +438,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode, fail: if (cmd == BLKRESETZONE) - filemap_invalidate_unlock(bdev->bd_inode->i_mapping); + filemap_invalidate_unlock(bdev->bd_mapping); return ret; } diff --git a/block/blk.h b/block/blk.h index 6e94c10af798..189bc25beb50 100644 --- a/block/blk.h +++ b/block/blk.h @@ -499,6 +499,8 @@ static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev, struct block_device *bdev_alloc(struct gendisk *disk, u8 partno); void bdev_add(struct block_device *bdev, dev_t dev); +void bdev_unhash(struct block_device *bdev); +void bdev_drop(struct block_device *bdev); int blk_alloc_ext_minor(void); void blk_free_ext_minor(unsigned int minor); diff --git a/block/fops.c b/block/fops.c index 7a163f7fe2d8..376265935714 100644 --- a/block/fops.c +++ b/block/fops.c @@ -663,8 +663,8 @@ static ssize_t blkdev_buffered_write(struct kiocb *iocb, struct iov_iter *from) static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; - struct block_device *bdev = I_BDEV(file->f_mapping->host); - struct inode *bd_inode = bdev->bd_inode; + struct inode *bd_inode = bdev_file_inode(file); + struct block_device *bdev = I_BDEV(bd_inode); loff_t size = bdev_nr_bytes(bdev); size_t shorted = 0; ssize_t ret; diff --git a/block/genhd.c b/block/genhd.c index 7f39fbe60753..0519097afa35 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -653,7 +653,7 @@ void del_gendisk(struct gendisk *disk) */ mutex_lock(&disk->open_mutex); xa_for_each(&disk->part_tbl, idx, part) - remove_inode_hash(part->bd_inode); + bdev_unhash(part); mutex_unlock(&disk->open_mutex); /* @@ -742,7 +742,7 @@ void invalidate_disk(struct gendisk *disk) struct block_device *bdev = disk->part0; invalidate_bdev(bdev); - bdev->bd_inode->i_mapping->wb_err = 0; + bdev->bd_mapping->wb_err = 0; set_capacity(disk, 0); } EXPORT_SYMBOL(invalidate_disk); @@ -1191,7 +1191,7 @@ static void disk_release(struct device *dev) if (test_bit(GD_ADDED, &disk->state) && disk->fops->free_disk) disk->fops->free_disk(disk); - iput(disk->part0->bd_inode); /* frees the disk */ + bdev_drop(disk->part0); /* frees the disk */ } static int block_uevent(const struct device *dev, struct kobj_uevent_env *env) @@ -1379,7 +1379,7 @@ out_erase_part0: out_destroy_part_tbl: xa_destroy(&disk->part_tbl); disk->part0->bd_disk = NULL; - iput(disk->part0->bd_inode); + bdev_drop(disk->part0); out_free_bdi: bdi_put(disk->bdi); out_free_bioset: diff --git a/block/ioctl.c b/block/ioctl.c index c7db3bd2d653..515f75fd0d83 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -96,7 +96,6 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, unsigned long arg) { unsigned int bs_mask = bdev_logical_block_size(bdev) - 1; - struct inode *inode = bdev->bd_inode; uint64_t range[2], start, len, end; struct bio *prev = NULL, *bio; sector_t sector, nr_sects; @@ -126,7 +125,7 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, end > bdev_nr_bytes(bdev)) return -EINVAL; - filemap_invalidate_lock(inode->i_mapping); + filemap_invalidate_lock(bdev->bd_mapping); err = truncate_bdev_range(bdev, mode, start, start + len - 1); if (err) goto fail; @@ -157,7 +156,7 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, out_unplug: blk_finish_plug(&plug); fail: - filemap_invalidate_unlock(inode->i_mapping); + filemap_invalidate_unlock(bdev->bd_mapping); return err; } @@ -182,12 +181,12 @@ static int blk_ioctl_secure_erase(struct block_device *bdev, blk_mode_t mode, if (start + len > bdev_nr_bytes(bdev)) return -EINVAL; - filemap_invalidate_lock(bdev->bd_inode->i_mapping); + filemap_invalidate_lock(bdev->bd_mapping); err = truncate_bdev_range(bdev, mode, start, start + len - 1); if (!err) err = blkdev_issue_secure_erase(bdev, start >> 9, len >> 9, GFP_KERNEL); - filemap_invalidate_unlock(bdev->bd_inode->i_mapping); + filemap_invalidate_unlock(bdev->bd_mapping); return err; } @@ -197,7 +196,6 @@ static int blk_ioctl_zeroout(struct block_device *bdev, blk_mode_t mode, { uint64_t range[2]; uint64_t start, end, len; - struct inode *inode = bdev->bd_inode; int err; if (!(mode & BLK_OPEN_WRITE)) @@ -220,7 +218,7 @@ static int blk_ioctl_zeroout(struct block_device *bdev, blk_mode_t mode, return -EINVAL; /* Invalidate the page cache, including dirty pages */ - filemap_invalidate_lock(inode->i_mapping); + filemap_invalidate_lock(bdev->bd_mapping); err = truncate_bdev_range(bdev, mode, start, end); if (err) goto fail; @@ -229,7 +227,7 @@ static int blk_ioctl_zeroout(struct block_device *bdev, blk_mode_t mode, BLKDEV_ZERO_NOUNMAP); fail: - filemap_invalidate_unlock(inode->i_mapping); + filemap_invalidate_unlock(bdev->bd_mapping); return err; } @@ -503,11 +501,14 @@ static int compat_hdio_getgeo(struct block_device *bdev, #endif /* set the logical block size */ -static int blkdev_bszset(struct block_device *bdev, blk_mode_t mode, +static int blkdev_bszset(struct file *file, blk_mode_t mode, int __user *argp) { + // this one might be file_inode(file)->i_rdev - a rare valid + // use of file_inode() for those. + dev_t dev = I_BDEV(file->f_mapping->host)->bd_dev; + struct file *excl_file; int ret, n; - struct file *file; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -517,13 +518,13 @@ static int blkdev_bszset(struct block_device *bdev, blk_mode_t mode, return -EFAULT; if (mode & BLK_OPEN_EXCL) - return set_blocksize(bdev, n); + return set_blocksize(file, n); - file = bdev_file_open_by_dev(bdev->bd_dev, mode, &bdev, NULL); - if (IS_ERR(file)) + excl_file = bdev_file_open_by_dev(dev, mode, &dev, NULL); + if (IS_ERR(excl_file)) return -EBUSY; - ret = set_blocksize(bdev, n); - fput(file); + ret = set_blocksize(excl_file, n); + fput(excl_file); return ret; } @@ -652,7 +653,7 @@ long blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ return put_int(argp, block_size(bdev)); case BLKBSZSET: - return blkdev_bszset(bdev, mode, argp); + return blkdev_bszset(file, mode, argp); case BLKGETSIZE64: return put_u64(argp, bdev_nr_bytes(bdev)); @@ -712,7 +713,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */ return put_int(argp, bdev_logical_block_size(bdev)); case BLKBSZSET_32: - return blkdev_bszset(bdev, mode, argp); + return blkdev_bszset(file, mode, argp); case BLKGETSIZE64_32: return put_u64(argp, bdev_nr_bytes(bdev)); diff --git a/block/partitions/core.c b/block/partitions/core.c index 37b5f92d07fe..84023b82e5a1 100644 --- a/block/partitions/core.c +++ b/block/partitions/core.c @@ -243,7 +243,7 @@ static const struct attribute_group *part_attr_groups[] = { static void part_release(struct device *dev) { put_disk(dev_to_bdev(dev)->bd_disk); - iput(dev_to_bdev(dev)->bd_inode); + bdev_drop(dev_to_bdev(dev)); } static int part_uevent(const struct device *dev, struct kobj_uevent_env *env) @@ -469,7 +469,7 @@ int bdev_del_partition(struct gendisk *disk, int partno) * Just delete the partition and invalidate it. */ - remove_inode_hash(part->bd_inode); + bdev_unhash(part); invalidate_bdev(part); drop_partition(part); ret = 0; @@ -652,7 +652,7 @@ rescan: * it cannot be looked up any more even when openers * still hold references. */ - remove_inode_hash(part->bd_inode); + bdev_unhash(part); /* * If @disk->open_partitions isn't elevated but there's @@ -701,7 +701,7 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed); void *read_part_sector(struct parsed_partitions *state, sector_t n, Sector *p) { - struct address_space *mapping = state->disk->part0->bd_inode->i_mapping; + struct address_space *mapping = state->disk->part0->bd_mapping; struct folio *folio; if (n >= get_capacity(state->disk)) { diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 38795508c2e9..027cf67101ef 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -151,12 +151,6 @@ static int cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!host) return -ENOMEM; - /* Perform set up for DMA */ - if (pci_enable_device_io(pdev)) { - dev_err(&pdev->dev, "unable to configure BAR2.\n"); - return -ENODEV; - } - if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) { dev_err(&pdev->dev, "unable to configure DMA mask.\n"); return -ENODEV; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 21728e9ea5c3..8a2ce8070010 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2215,6 +2215,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, bool write) } dev_info(ddev, "%lukB available on disc\n", lba << 1); } + set_blocksize(bdev_file, CD_FRAMESIZE); return 0; @@ -2278,11 +2279,6 @@ static int pkt_open(struct gendisk *disk, blk_mode_t mode) ret = pkt_open_dev(pd, mode & BLK_OPEN_WRITE); if (ret) goto out_dec; - /* - * needed here as well, since ext2 (among others) may change - * the blocksize at mount time - */ - set_blocksize(disk->part0, CD_FRAMESIZE); } mutex_unlock(&ctl_mutex); mutex_unlock(&pktcdvd_mutex); @@ -2526,7 +2522,6 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) __module_get(THIS_MODULE); pd->bdev_file = bdev_file; - set_blocksize(file_bdev(bdev_file), CD_FRAMESIZE); atomic_set(&pd->cdrw.pending_bios, 0); pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->disk->disk_name); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 4cf38f7d3e0a..3acd7006ad2c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -426,11 +426,10 @@ static void reset_bdev(struct zram *zram) if (!zram->backing_dev) return; - fput(zram->bdev_file); /* hope filp_close flush all of IO */ filp_close(zram->backing_dev, NULL); zram->backing_dev = NULL; - zram->bdev_file = NULL; + zram->bdev = NULL; zram->disk->fops = &zram_devops; kvfree(zram->bitmap); zram->bitmap = NULL; @@ -473,10 +472,8 @@ static ssize_t backing_dev_store(struct device *dev, size_t sz; struct file *backing_dev = NULL; struct inode *inode; - struct address_space *mapping; unsigned int bitmap_sz; unsigned long nr_pages, *bitmap = NULL; - struct file *bdev_file = NULL; int err; struct zram *zram = dev_to_zram(dev); @@ -497,15 +494,14 @@ static ssize_t backing_dev_store(struct device *dev, if (sz > 0 && file_name[sz - 1] == '\n') file_name[sz - 1] = 0x00; - backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0); + backing_dev = filp_open(file_name, O_RDWR | O_LARGEFILE | O_EXCL, 0); if (IS_ERR(backing_dev)) { err = PTR_ERR(backing_dev); backing_dev = NULL; goto out; } - mapping = backing_dev->f_mapping; - inode = mapping->host; + inode = backing_dev->f_mapping->host; /* Support only block device in this moment */ if (!S_ISBLK(inode->i_mode)) { @@ -513,14 +509,6 @@ static ssize_t backing_dev_store(struct device *dev, goto out; } - bdev_file = bdev_file_open_by_dev(inode->i_rdev, - BLK_OPEN_READ | BLK_OPEN_WRITE, zram, NULL); - if (IS_ERR(bdev_file)) { - err = PTR_ERR(bdev_file); - bdev_file = NULL; - goto out; - } - nr_pages = i_size_read(inode) >> PAGE_SHIFT; bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long); bitmap = kvzalloc(bitmap_sz, GFP_KERNEL); @@ -531,7 +519,7 @@ static ssize_t backing_dev_store(struct device *dev, reset_bdev(zram); - zram->bdev_file = bdev_file; + zram->bdev = I_BDEV(inode); zram->backing_dev = backing_dev; zram->bitmap = bitmap; zram->nr_pages = nr_pages; @@ -544,9 +532,6 @@ static ssize_t backing_dev_store(struct device *dev, out: kvfree(bitmap); - if (bdev_file) - fput(bdev_file); - if (backing_dev) filp_close(backing_dev, NULL); @@ -587,7 +572,7 @@ static void read_from_bdev_async(struct zram *zram, struct page *page, { struct bio *bio; - bio = bio_alloc(file_bdev(zram->bdev_file), 1, parent->bi_opf, GFP_NOIO); + bio = bio_alloc(zram->bdev, 1, parent->bi_opf, GFP_NOIO); bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9); __bio_add_page(bio, page, PAGE_SIZE, 0); bio_chain(bio, parent); @@ -703,7 +688,7 @@ static ssize_t writeback_store(struct device *dev, continue; } - bio_init(&bio, file_bdev(zram->bdev_file), &bio_vec, 1, + bio_init(&bio, zram->bdev, &bio_vec, 1, REQ_OP_WRITE | REQ_SYNC); bio.bi_iter.bi_sector = blk_idx * (PAGE_SIZE >> 9); __bio_add_page(&bio, page, PAGE_SIZE, 0); @@ -785,7 +770,7 @@ static void zram_sync_read(struct work_struct *work) struct bio_vec bv; struct bio bio; - bio_init(&bio, file_bdev(zw->zram->bdev_file), &bv, 1, REQ_OP_READ); + bio_init(&bio, zw->zram->bdev, &bv, 1, REQ_OP_READ); bio.bi_iter.bi_sector = zw->entry * (PAGE_SIZE >> 9); __bio_add_page(&bio, zw->page, PAGE_SIZE, 0); zw->error = submit_bio_wait(&bio); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 37bf29f34d26..35e322144629 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -132,7 +132,7 @@ struct zram { spinlock_t wb_limit_lock; bool wb_limit_enable; u64 bd_wb_limit; - struct file *bdev_file; + struct block_device *bdev; unsigned long *bitmap; unsigned long nr_pages; #endif diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 0df09bd79408..8567dd11eaac 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -525,7 +525,7 @@ static int cxl_cdat_get_length(struct device *dev, __le32 response[2]; int rc; - rc = pci_doe(doe_mb, PCI_DVSEC_VENDOR_ID_CXL, + rc = pci_doe(doe_mb, PCI_VENDOR_ID_CXL, CXL_DOE_PROTOCOL_TABLE_ACCESS, &request, sizeof(request), &response, sizeof(response)); @@ -555,7 +555,7 @@ static int cxl_cdat_read_table(struct device *dev, __le32 request = CDAT_DOE_REQ(entry_handle); int rc; - rc = pci_doe(doe_mb, PCI_DVSEC_VENDOR_ID_CXL, + rc = pci_doe(doe_mb, PCI_VENDOR_ID_CXL, CXL_DOE_PROTOCOL_TABLE_ACCESS, &request, sizeof(request), rsp, sizeof(*rsp) + remaining); @@ -640,7 +640,7 @@ void read_cdat_data(struct cxl_port *port) if (!pdev) return; - doe_mb = pci_find_doe_mailbox(pdev, PCI_DVSEC_VENDOR_ID_CXL, + doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_CXL, CXL_DOE_PROTOCOL_TABLE_ACCESS); if (!doe_mb) { dev_dbg(dev, "No CDAT mailbox\n"); @@ -1045,3 +1045,32 @@ long cxl_pci_get_latency(struct pci_dev *pdev) return cxl_flit_size(pdev) * MEGA / bw; } + +static int __cxl_endpoint_decoder_reset_detected(struct device *dev, void *data) +{ + struct cxl_port *port = data; + struct cxl_decoder *cxld; + struct cxl_hdm *cxlhdm; + void __iomem *hdm; + u32 ctrl; + + if (!is_endpoint_decoder(dev)) + return 0; + + cxld = to_cxl_decoder(dev); + if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0) + return 0; + + cxlhdm = dev_get_drvdata(&port->dev); + hdm = cxlhdm->regs.hdm_decoder; + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id)); + + return !FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl); +} + +bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port) +{ + return device_for_each_child(&port->dev, port, + __cxl_endpoint_decoder_reset_detected); +} +EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_reset_detected, CXL); diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c index 3c42f984eeaf..e1082e749c69 100644 --- a/drivers/cxl/core/regs.c +++ b/drivers/cxl/core/regs.c @@ -314,7 +314,7 @@ int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type, .resource = CXL_RESOURCE_NONE, }; - regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL, + regloc = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL, CXL_DVSEC_REG_LOCATOR); if (!regloc) return -ENXIO; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 80f58b96dc1c..603c0120cff8 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -898,6 +898,8 @@ void cxl_coordinates_combine(struct access_coordinate *out, struct access_coordinate *c1, struct access_coordinate *c2); +bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port); + /* * Unit test builds overrides this to __weak, find the 'strong' version * of these symbols in tools/testing/cxl/. diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 93992a1c8eec..4da07727ab9c 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -13,7 +13,6 @@ * "DVSEC" redundancies removed. When obvious, abbreviations may be used. */ #define PCI_DVSEC_HEADER1_LENGTH_MASK GENMASK(31, 20) -#define PCI_DVSEC_VENDOR_ID_CXL 0x1E98 /* CXL 2.0 8.1.3: PCIe DVSEC for CXL Device */ #define CXL_DVSEC_PCIE_DEVICE 0 diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 74876c9835e8..e53646e9f2fb 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -817,7 +817,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) cxlds->rcd = is_cxl_restricted(pdev); cxlds->serial = pci_get_dsn(pdev); cxlds->cxl_dvsec = pci_find_dvsec_capability( - pdev, PCI_DVSEC_VENDOR_ID_CXL, CXL_DVSEC_PCIE_DEVICE); + pdev, PCI_VENDOR_ID_CXL, CXL_DVSEC_PCIE_DEVICE); if (!cxlds->cxl_dvsec) dev_warn(&pdev->dev, "Device DVSEC not present, skip CXL.mem init\n"); @@ -957,11 +957,33 @@ static void cxl_error_resume(struct pci_dev *pdev) dev->driver ? "successful" : "failed"); } +static void cxl_reset_done(struct pci_dev *pdev) +{ + struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); + struct cxl_memdev *cxlmd = cxlds->cxlmd; + struct device *dev = &pdev->dev; + + /* + * FLR does not expect to touch the HDM decoders and related + * registers. SBR, however, will wipe all device configurations. + * Issue a warning if there was an active decoder before the reset + * that no longer exists. + */ + guard(device)(&cxlmd->dev); + if (cxlmd->endpoint && + cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) { + dev_crit(dev, "SBR happened without memory regions removal.\n"); + dev_crit(dev, "System may be unstable if regions hosted system memory.\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } +} + static const struct pci_error_handlers cxl_error_handlers = { .error_detected = cxl_error_detected, .slot_reset = cxl_slot_reset, .resume = cxl_error_resume, .cor_error_detected = cxl_cor_error_detected, + .reset_done = cxl_reset_done, }; static struct pci_driver cxl_pci_driver = { diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index dfd40d14e408..802ca916f05f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -31,10 +31,12 @@ obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_DW_EDMA) += dw-edma/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o +fsl-edma-trace-$(CONFIG_TRACING) := fsl-edma-trace.o +CFLAGS_fsl-edma-trace.o := -I$(src) obj-$(CONFIG_FSL_DMA) += fsldma.o -fsl-edma-objs := fsl-edma-main.o fsl-edma-common.o +fsl-edma-objs := fsl-edma-main.o fsl-edma-common.o ${fsl-edma-trace-y} obj-$(CONFIG_FSL_EDMA) += fsl-edma.o -mcf-edma-objs := mcf-edma-main.o fsl-edma-common.o +mcf-edma-objs := mcf-edma-main.o fsl-edma-common.o ${fsl-edma-trace-y} obj-$(CONFIG_MCF_EDMA) += mcf-edma.o obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index fbf048f432bf..73a5cfb4da8a 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -2855,8 +2855,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) } /* Initialize physical channels */ - pl08x->phy_chans = kzalloc((vd->channels * sizeof(*pl08x->phy_chans)), - GFP_KERNEL); + pl08x->phy_chans = kcalloc(vd->channels, sizeof(*pl08x->phy_chans), + GFP_KERNEL); if (!pl08x->phy_chans) { ret = -ENOMEM; goto out_no_phychans; diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 4e339c04fc1e..bdb752f11869 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -1002,6 +1002,16 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version) return 0; } +static void axi_dmac_tasklet_kill(void *task) +{ + tasklet_kill(task); +} + +static void axi_dmac_free_dma_controller(void *of_node) +{ + of_dma_controller_free(of_node); +} + static int axi_dmac_probe(struct platform_device *pdev) { struct dma_device *dma_dev; @@ -1025,14 +1035,10 @@ static int axi_dmac_probe(struct platform_device *pdev) if (IS_ERR(dmac->base)) return PTR_ERR(dmac->base); - dmac->clk = devm_clk_get(&pdev->dev, NULL); + dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(dmac->clk)) return PTR_ERR(dmac->clk); - ret = clk_prepare_enable(dmac->clk); - if (ret < 0) - return ret; - version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION); if (version >= ADI_AXI_PCORE_VER(4, 3, 'a')) @@ -1041,7 +1047,7 @@ static int axi_dmac_probe(struct platform_device *pdev) ret = axi_dmac_parse_dt(&pdev->dev, dmac); if (ret < 0) - goto err_clk_disable; + return ret; INIT_LIST_HEAD(&dmac->chan.active_descs); @@ -1072,7 +1078,7 @@ static int axi_dmac_probe(struct platform_device *pdev) ret = axi_dmac_detect_caps(dmac, version); if (ret) - goto err_clk_disable; + return ret; dma_dev->copy_align = (dmac->chan.address_align_mask + 1); @@ -1088,57 +1094,42 @@ static int axi_dmac_probe(struct platform_device *pdev) !AXI_DMAC_DST_COHERENT_GET(ret)) { dev_err(dmac->dma_dev.dev, "Coherent DMA not supported in hardware"); - ret = -EINVAL; - goto err_clk_disable; + return -EINVAL; } } - ret = dma_async_device_register(dma_dev); + ret = dmaenginem_async_device_register(dma_dev); + if (ret) + return ret; + + /* + * Put the action in here so it get's done before unregistering the DMA + * device. + */ + ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill, + &dmac->chan.vchan.task); if (ret) - goto err_clk_disable; + return ret; ret = of_dma_controller_register(pdev->dev.of_node, of_dma_xlate_by_chan_id, dma_dev); if (ret) - goto err_unregister_device; + return ret; - ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, IRQF_SHARED, - dev_name(&pdev->dev), dmac); + ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_free_dma_controller, + pdev->dev.of_node); if (ret) - goto err_unregister_of; + return ret; - platform_set_drvdata(pdev, dmac); + ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler, + IRQF_SHARED, dev_name(&pdev->dev), dmac); + if (ret) + return ret; regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base, &axi_dmac_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_irq; - } - - return 0; - -err_free_irq: - free_irq(dmac->irq, dmac); -err_unregister_of: - of_dma_controller_free(pdev->dev.of_node); -err_unregister_device: - dma_async_device_unregister(&dmac->dma_dev); -err_clk_disable: - clk_disable_unprepare(dmac->clk); - - return ret; -} - -static void axi_dmac_remove(struct platform_device *pdev) -{ - struct axi_dmac *dmac = platform_get_drvdata(pdev); - of_dma_controller_free(pdev->dev.of_node); - free_irq(dmac->irq, dmac); - tasklet_kill(&dmac->chan.vchan.task); - dma_async_device_unregister(&dmac->dma_dev); - clk_disable_unprepare(dmac->clk); + return PTR_ERR_OR_ZERO(regmap); } static const struct of_device_id axi_dmac_of_match_table[] = { @@ -1153,7 +1144,6 @@ static struct platform_driver axi_dmac_driver = { .of_match_table = axi_dmac_of_match_table, }, .probe = axi_dmac_probe, - .remove_new = axi_dmac_remove, }; module_platform_driver(axi_dmac_driver); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index a86a81ff0caa..fffafa86d964 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -302,6 +302,7 @@ static struct axi_dma_desc *axi_desc_alloc(u32 num) kfree(desc); return NULL; } + desc->nr_hw_descs = num; return desc; } @@ -328,7 +329,7 @@ static struct axi_dma_lli *axi_desc_get(struct axi_dma_chan *chan, static void axi_desc_put(struct axi_dma_desc *desc) { struct axi_dma_chan *chan = desc->chan; - int count = atomic_read(&chan->descs_allocated); + int count = desc->nr_hw_descs; struct axi_dma_hw_desc *hw_desc; int descs_put; @@ -1139,9 +1140,6 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) /* Remove the completed descriptor from issued list before completing */ list_del(&vd->node); vchan_cookie_complete(vd); - - /* Submit queued descriptors after processing the completed ones */ - axi_chan_start_first_queued(chan); } out: @@ -1445,6 +1443,24 @@ static int parse_device_properties(struct axi_dma_chip *chip) return 0; } +static int axi_req_irqs(struct platform_device *pdev, struct axi_dma_chip *chip) +{ + int irq_count = platform_irq_count(pdev); + int ret; + + for (int i = 0; i < irq_count; i++) { + chip->irq[i] = platform_get_irq(pdev, i); + if (chip->irq[i] < 0) + return chip->irq[i]; + ret = devm_request_irq(chip->dev, chip->irq[i], dw_axi_dma_interrupt, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (ret < 0) + return ret; + } + + return 0; +} + static int dw_probe(struct platform_device *pdev) { struct axi_dma_chip *chip; @@ -1471,10 +1487,6 @@ static int dw_probe(struct platform_device *pdev) chip->dev = &pdev->dev; chip->dw->hdata = hdata; - chip->irq = platform_get_irq(pdev, 0); - if (chip->irq < 0) - return chip->irq; - chip->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); @@ -1515,8 +1527,7 @@ static int dw_probe(struct platform_device *pdev) if (!dw->chan) return -ENOMEM; - ret = devm_request_irq(chip->dev, chip->irq, dw_axi_dma_interrupt, - IRQF_SHARED, KBUILD_MODNAME, chip); + ret = axi_req_irqs(pdev, chip); if (ret) return ret; @@ -1629,7 +1640,9 @@ static void dw_remove(struct platform_device *pdev) pm_runtime_disable(chip->dev); axi_dma_suspend(chip); - devm_free_irq(chip->dev, chip->irq, chip); + for (i = 0; i < DMAC_MAX_CHANNELS; i++) + if (chip->irq[i] > 0) + devm_free_irq(chip->dev, chip->irq[i], chip); of_dma_controller_free(chip->dev->of_node); @@ -1653,6 +1666,9 @@ static const struct of_device_id dw_dma_of_id_table[] = { }, { .compatible = "starfive,jh7110-axi-dma", .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2), + }, { + .compatible = "starfive,jh8100-axi-dma", + .data = (void *)AXI_DMA_FLAG_HAS_RESETS, }, {} }; diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index 454904d99654..b842e6a8d90d 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -65,7 +65,7 @@ struct dw_axi_dma { struct axi_dma_chip { struct device *dev; - int irq; + int irq[DMAC_MAX_CHANNELS]; void __iomem *regs; void __iomem *apb_regs; struct clk *core_clk; @@ -104,6 +104,7 @@ struct axi_dma_desc { u32 completed_blocks; u32 length; u32 period_len; + u32 nr_hw_descs; }; struct axi_dma_chan_config { diff --git a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c index 5a8061a307cd..36384d019263 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c +++ b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c @@ -362,7 +362,7 @@ static int __cold dpaa2_qdma_setup(struct fsl_mc_device *ls_dev) for (i = 0; i < priv->num_pairs; i++) { err = dpdmai_get_rx_queue(priv->mc_io, 0, ls_dev->mc_handle, - i, &priv->rx_queue_attr[i]); + i, 0, &priv->rx_queue_attr[i]); if (err) { dev_err(dev, "dpdmai_get_rx_queue() failed\n"); goto exit; @@ -370,13 +370,13 @@ static int __cold dpaa2_qdma_setup(struct fsl_mc_device *ls_dev) ppriv->rsp_fqid = priv->rx_queue_attr[i].fqid; err = dpdmai_get_tx_queue(priv->mc_io, 0, ls_dev->mc_handle, - i, &priv->tx_fqid[i]); + i, 0, &priv->tx_queue_attr[i]); if (err) { dev_err(dev, "dpdmai_get_tx_queue() failed\n"); goto exit; } - ppriv->req_fqid = priv->tx_fqid[i]; - ppriv->prio = i; + ppriv->req_fqid = priv->tx_queue_attr[i].fqid; + ppriv->prio = DPAA2_QDMA_DEFAULT_PRIORITY; ppriv->priv = priv; ppriv++; } @@ -542,7 +542,7 @@ static int __cold dpaa2_dpdmai_bind(struct dpaa2_qdma_priv *priv) rx_queue_cfg.dest_cfg.dest_id = ppriv->nctx.dpio_id; rx_queue_cfg.dest_cfg.priority = ppriv->prio; err = dpdmai_set_rx_queue(priv->mc_io, 0, ls_dev->mc_handle, - rx_queue_cfg.dest_cfg.priority, + rx_queue_cfg.dest_cfg.priority, 0, &rx_queue_cfg); if (err) { dev_err(dev, "dpdmai_set_rx_queue() failed\n"); @@ -642,7 +642,7 @@ static int dpaa2_dpdmai_init_channels(struct dpaa2_qdma_engine *dpaa2_qdma) for (i = 0; i < dpaa2_qdma->n_chans; i++) { dpaa2_chan = &dpaa2_qdma->chans[i]; dpaa2_chan->qdma = dpaa2_qdma; - dpaa2_chan->fqid = priv->tx_fqid[i % num]; + dpaa2_chan->fqid = priv->tx_queue_attr[i % num].fqid; dpaa2_chan->vchan.desc_free = dpaa2_qdma_free_desc; vchan_init(&dpaa2_chan->vchan, &dpaa2_qdma->dma_dev); spin_lock_init(&dpaa2_chan->queue_lock); @@ -802,7 +802,7 @@ static void dpaa2_qdma_shutdown(struct fsl_mc_device *ls_dev) dpdmai_disable(priv->mc_io, 0, ls_dev->mc_handle); dpaa2_dpdmai_dpio_unbind(priv); dpdmai_close(priv->mc_io, 0, ls_dev->mc_handle); - dpdmai_destroy(priv->mc_io, 0, ls_dev->mc_handle); + dpdmai_destroy(priv->mc_io, 0, priv->dpqdma_id, ls_dev->mc_handle); } static const struct fsl_mc_device_id dpaa2_qdma_id_table[] = { diff --git a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h index 03e2f4e0baca..2c80077cb7c0 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h +++ b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h @@ -6,6 +6,7 @@ #define DPAA2_QDMA_STORE_SIZE 16 #define NUM_CH 8 +#define DPAA2_QDMA_DEFAULT_PRIORITY 0 struct dpaa2_qdma_sd_d { u32 rsv:32; @@ -122,8 +123,8 @@ struct dpaa2_qdma_priv { struct dpaa2_qdma_engine *dpaa2_qdma; struct dpaa2_qdma_priv_per_prio *ppriv; - struct dpdmai_rx_queue_attr rx_queue_attr[DPDMAI_PRIO_NUM]; - u32 tx_fqid[DPDMAI_PRIO_NUM]; + struct dpdmai_rx_queue_attr rx_queue_attr[DPDMAI_MAX_QUEUE_NUM]; + struct dpdmai_tx_queue_attr tx_queue_attr[DPDMAI_MAX_QUEUE_NUM]; }; struct dpaa2_qdma_priv_per_prio { diff --git a/drivers/dma/fsl-dpaa2-qdma/dpdmai.c b/drivers/dma/fsl-dpaa2-qdma/dpdmai.c index 878662aaa1c2..36897b41ee7e 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpdmai.c +++ b/drivers/dma/fsl-dpaa2-qdma/dpdmai.c @@ -1,47 +1,52 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright 2019 NXP +#include <linux/bitfield.h> #include <linux/module.h> #include <linux/types.h> #include <linux/io.h> #include <linux/fsl/mc.h> #include "dpdmai.h" +#define DEST_TYPE_MASK 0xF + struct dpdmai_rsp_get_attributes { __le32 id; u8 num_of_priorities; - u8 pad0[3]; + u8 num_of_queues; + u8 pad0[2]; __le16 major; __le16 minor; }; struct dpdmai_cmd_queue { __le32 dest_id; - u8 priority; - u8 queue; + u8 dest_priority; + union { + u8 queue; + u8 pri; + }; u8 dest_type; - u8 pad; + u8 queue_idx; __le64 user_ctx; union { __le32 options; __le32 fqid; }; -}; +} __packed; struct dpdmai_rsp_get_tx_queue { __le64 pad; __le32 fqid; }; -#define MC_CMD_OP(_cmd, _param, _offset, _width, _type, _arg) \ - ((_cmd).params[_param] |= mc_enc((_offset), (_width), _arg)) +struct dpdmai_cmd_open { + __le32 dpdmai_id; +} __packed; -/* cmd, param, offset, width, type, arg_name */ -#define DPDMAI_CMD_CREATE(cmd, cfg) \ -do { \ - MC_CMD_OP(cmd, 0, 8, 8, u8, (cfg)->priorities[0]);\ - MC_CMD_OP(cmd, 0, 16, 8, u8, (cfg)->priorities[1]);\ -} while (0) +struct dpdmai_cmd_destroy { + __le32 dpdmai_id; +} __packed; static inline u64 mc_enc(int lsoffset, int width, u64 val) { @@ -68,16 +73,16 @@ static inline u64 mc_enc(int lsoffset, int width, u64 val) int dpdmai_open(struct fsl_mc_io *mc_io, u32 cmd_flags, int dpdmai_id, u16 *token) { + struct dpdmai_cmd_open *cmd_params; struct fsl_mc_command cmd = { 0 }; - __le64 *cmd_dpdmai_id; int err; /* prepare command */ cmd.header = mc_encode_cmd_header(DPDMAI_CMDID_OPEN, cmd_flags, 0); - cmd_dpdmai_id = cmd.params; - *cmd_dpdmai_id = cpu_to_le32(dpdmai_id); + cmd_params = (struct dpdmai_cmd_open *)&cmd.params; + cmd_params->dpdmai_id = cpu_to_le32(dpdmai_id); /* send command to mc*/ err = mc_send_command(mc_io, &cmd); @@ -116,65 +121,26 @@ int dpdmai_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token) EXPORT_SYMBOL_GPL(dpdmai_close); /** - * dpdmai_create() - Create the DPDMAI object - * @mc_io: Pointer to MC portal's I/O object - * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' - * @cfg: Configuration structure - * @token: Returned token; use in subsequent API calls - * - * Create the DPDMAI object, allocate required resources and - * perform required initialization. - * - * The object can be created either by declaring it in the - * DPL file, or by calling this function. - * - * This function returns a unique authentication token, - * associated with the specific object ID and the specific MC - * portal; this token must be used in all subsequent calls to - * this specific object. For objects that are created using the - * DPL file, call dpdmai_open() function to get an authentication - * token first. - * - * Return: '0' on Success; Error code otherwise. - */ -int dpdmai_create(struct fsl_mc_io *mc_io, u32 cmd_flags, - const struct dpdmai_cfg *cfg, u16 *token) -{ - struct fsl_mc_command cmd = { 0 }; - int err; - - /* prepare command */ - cmd.header = mc_encode_cmd_header(DPDMAI_CMDID_CREATE, - cmd_flags, 0); - DPDMAI_CMD_CREATE(cmd, cfg); - - /* send command to mc*/ - err = mc_send_command(mc_io, &cmd); - if (err) - return err; - - /* retrieve response parameters */ - *token = mc_cmd_hdr_read_token(&cmd); - - return 0; -} - -/** * dpdmai_destroy() - Destroy the DPDMAI object and release all its resources. * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @dpdmai_id: The object id; it must be a valid id within the container that created this object; * @token: Token of DPDMAI object * * Return: '0' on Success; error code otherwise. */ -int dpdmai_destroy(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token) +int dpdmai_destroy(struct fsl_mc_io *mc_io, u32 cmd_flags, u32 dpdmai_id, u16 token) { + struct dpdmai_cmd_destroy *cmd_params; struct fsl_mc_command cmd = { 0 }; /* prepare command */ cmd.header = mc_encode_cmd_header(DPDMAI_CMDID_DESTROY, cmd_flags, token); + cmd_params = (struct dpdmai_cmd_destroy *)&cmd.params; + cmd_params->dpdmai_id = cpu_to_le32(dpdmai_id); + /* send command to mc*/ return mc_send_command(mc_io, &cmd); } @@ -274,6 +240,7 @@ int dpdmai_get_attributes(struct fsl_mc_io *mc_io, u32 cmd_flags, attr->version.major = le16_to_cpu(rsp_params->major); attr->version.minor = le16_to_cpu(rsp_params->minor); attr->num_of_priorities = rsp_params->num_of_priorities; + attr->num_of_queues = rsp_params->num_of_queues; return 0; } @@ -284,13 +251,14 @@ EXPORT_SYMBOL_GPL(dpdmai_get_attributes); * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPDMAI object + * @queue_idx: DMA queue index * @priority: Select the queue relative to number of * priorities configured at DPDMAI creation * @cfg: Rx queue configuration * * Return: '0' on Success; Error code otherwise. */ -int dpdmai_set_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, +int dpdmai_set_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 queue_idx, u8 priority, const struct dpdmai_rx_queue_cfg *cfg) { struct dpdmai_cmd_queue *cmd_params; @@ -302,11 +270,12 @@ int dpdmai_set_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, cmd_params = (struct dpdmai_cmd_queue *)cmd.params; cmd_params->dest_id = cpu_to_le32(cfg->dest_cfg.dest_id); - cmd_params->priority = cfg->dest_cfg.priority; - cmd_params->queue = priority; + cmd_params->dest_priority = cfg->dest_cfg.priority; + cmd_params->pri = priority; cmd_params->dest_type = cfg->dest_cfg.dest_type; cmd_params->user_ctx = cpu_to_le64(cfg->user_ctx); cmd_params->options = cpu_to_le32(cfg->options); + cmd_params->queue_idx = queue_idx; /* send command to mc*/ return mc_send_command(mc_io, &cmd); @@ -318,13 +287,14 @@ EXPORT_SYMBOL_GPL(dpdmai_set_rx_queue); * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPDMAI object + * @queue_idx: DMA Queue index * @priority: Select the queue relative to number of * priorities configured at DPDMAI creation * @attr: Returned Rx queue attributes * * Return: '0' on Success; Error code otherwise. */ -int dpdmai_get_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, +int dpdmai_get_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 queue_idx, u8 priority, struct dpdmai_rx_queue_attr *attr) { struct dpdmai_cmd_queue *cmd_params; @@ -337,6 +307,7 @@ int dpdmai_get_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, cmd_params = (struct dpdmai_cmd_queue *)cmd.params; cmd_params->queue = priority; + cmd_params->queue_idx = queue_idx; /* send command to mc*/ err = mc_send_command(mc_io, &cmd); @@ -345,8 +316,8 @@ int dpdmai_get_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, /* retrieve response parameters */ attr->dest_cfg.dest_id = le32_to_cpu(cmd_params->dest_id); - attr->dest_cfg.priority = cmd_params->priority; - attr->dest_cfg.dest_type = cmd_params->dest_type; + attr->dest_cfg.priority = cmd_params->dest_priority; + attr->dest_cfg.dest_type = FIELD_GET(DEST_TYPE_MASK, cmd_params->dest_type); attr->user_ctx = le64_to_cpu(cmd_params->user_ctx); attr->fqid = le32_to_cpu(cmd_params->fqid); @@ -359,14 +330,15 @@ EXPORT_SYMBOL_GPL(dpdmai_get_rx_queue); * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPDMAI object + * @queue_idx: DMA queue index * @priority: Select the queue relative to number of * priorities configured at DPDMAI creation - * @fqid: Returned Tx queue + * @attr: Returned DMA Tx queue attributes * * Return: '0' on Success; Error code otherwise. */ int dpdmai_get_tx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, - u16 token, u8 priority, u32 *fqid) + u16 token, u8 queue_idx, u8 priority, struct dpdmai_tx_queue_attr *attr) { struct dpdmai_rsp_get_tx_queue *rsp_params; struct dpdmai_cmd_queue *cmd_params; @@ -379,6 +351,7 @@ int dpdmai_get_tx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, cmd_params = (struct dpdmai_cmd_queue *)cmd.params; cmd_params->queue = priority; + cmd_params->queue_idx = queue_idx; /* send command to mc*/ err = mc_send_command(mc_io, &cmd); @@ -388,7 +361,7 @@ int dpdmai_get_tx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, /* retrieve response parameters */ rsp_params = (struct dpdmai_rsp_get_tx_queue *)cmd.params; - *fqid = le32_to_cpu(rsp_params->fqid); + attr->fqid = le32_to_cpu(rsp_params->fqid); return 0; } diff --git a/drivers/dma/fsl-dpaa2-qdma/dpdmai.h b/drivers/dma/fsl-dpaa2-qdma/dpdmai.h index b13b9bf0c003..3fe7d8327366 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpdmai.h +++ b/drivers/dma/fsl-dpaa2-qdma/dpdmai.h @@ -5,14 +5,19 @@ #define __FSL_DPDMAI_H /* DPDMAI Version */ -#define DPDMAI_VER_MAJOR 2 -#define DPDMAI_VER_MINOR 2 +#define DPDMAI_VER_MAJOR 3 +#define DPDMAI_VER_MINOR 3 -#define DPDMAI_CMD_BASE_VERSION 0 +#define DPDMAI_CMD_BASE_VERSION 1 #define DPDMAI_CMD_ID_OFFSET 4 -#define DPDMAI_CMDID_FORMAT(x) (((x) << DPDMAI_CMD_ID_OFFSET) | \ - DPDMAI_CMD_BASE_VERSION) +/* + * Maximum number of Tx/Rx queues per DPDMAI object + */ +#define DPDMAI_MAX_QUEUE_NUM 8 + +#define DPDMAI_CMDID_FORMAT_V(x, v) (((x) << DPDMAI_CMD_ID_OFFSET) | (v)) +#define DPDMAI_CMDID_FORMAT(x) DPDMAI_CMDID_FORMAT_V(x, DPDMAI_CMD_BASE_VERSION) /* Command IDs */ #define DPDMAI_CMDID_CLOSE DPDMAI_CMDID_FORMAT(0x800) @@ -26,18 +31,9 @@ #define DPDMAI_CMDID_RESET DPDMAI_CMDID_FORMAT(0x005) #define DPDMAI_CMDID_IS_ENABLED DPDMAI_CMDID_FORMAT(0x006) -#define DPDMAI_CMDID_SET_IRQ DPDMAI_CMDID_FORMAT(0x010) -#define DPDMAI_CMDID_GET_IRQ DPDMAI_CMDID_FORMAT(0x011) -#define DPDMAI_CMDID_SET_IRQ_ENABLE DPDMAI_CMDID_FORMAT(0x012) -#define DPDMAI_CMDID_GET_IRQ_ENABLE DPDMAI_CMDID_FORMAT(0x013) -#define DPDMAI_CMDID_SET_IRQ_MASK DPDMAI_CMDID_FORMAT(0x014) -#define DPDMAI_CMDID_GET_IRQ_MASK DPDMAI_CMDID_FORMAT(0x015) -#define DPDMAI_CMDID_GET_IRQ_STATUS DPDMAI_CMDID_FORMAT(0x016) -#define DPDMAI_CMDID_CLEAR_IRQ_STATUS DPDMAI_CMDID_FORMAT(0x017) - -#define DPDMAI_CMDID_SET_RX_QUEUE DPDMAI_CMDID_FORMAT(0x1A0) -#define DPDMAI_CMDID_GET_RX_QUEUE DPDMAI_CMDID_FORMAT(0x1A1) -#define DPDMAI_CMDID_GET_TX_QUEUE DPDMAI_CMDID_FORMAT(0x1A2) +#define DPDMAI_CMDID_SET_RX_QUEUE DPDMAI_CMDID_FORMAT_V(0x1A0, 2) +#define DPDMAI_CMDID_GET_RX_QUEUE DPDMAI_CMDID_FORMAT_V(0x1A1, 2) +#define DPDMAI_CMDID_GET_TX_QUEUE DPDMAI_CMDID_FORMAT_V(0x1A2, 2) #define MC_CMD_HDR_TOKEN_O 32 /* Token field offset */ #define MC_CMD_HDR_TOKEN_S 16 /* Token field size */ @@ -49,30 +45,32 @@ * Contains initialization APIs and runtime control APIs for DPDMAI */ -/** +/* * Maximum number of Tx/Rx priorities per DPDMAI object */ #define DPDMAI_PRIO_NUM 2 /* DPDMAI queue modification options */ -/** +/* * Select to modify the user's context associated with the queue */ #define DPDMAI_QUEUE_OPT_USER_CTX 0x1 -/** +/* * Select to modify the queue's destination */ #define DPDMAI_QUEUE_OPT_DEST 0x2 /** * struct dpdmai_cfg - Structure representing DPDMAI configuration + * @num_queues: Number of the DMA queues * @priorities: Priorities for the DMA hardware processing; valid priorities are * configured with values 1-8; the entry following last valid entry * should be configured with 0 */ struct dpdmai_cfg { + u8 num_queues; u8 priorities[DPDMAI_PRIO_NUM]; }; @@ -80,20 +78,19 @@ struct dpdmai_cfg { * struct dpdmai_attr - Structure representing DPDMAI attributes * @id: DPDMAI object ID * @version: DPDMAI version + * @version.major: DPDMAI major version + * @version.minor: DPDMAI minor version * @num_of_priorities: number of priorities + * @num_of_queues: number of the DMA queues */ struct dpdmai_attr { int id; - /** - * struct version - DPDMAI version - * @major: DPDMAI major version - * @minor: DPDMAI minor version - */ struct { u16 major; u16 minor; } version; u8 num_of_priorities; + u8 num_of_queues; }; /** @@ -158,22 +155,24 @@ struct dpdmai_rx_queue_attr { u32 fqid; }; +struct dpdmai_tx_queue_attr { + u32 fqid; +}; + int dpdmai_open(struct fsl_mc_io *mc_io, u32 cmd_flags, int dpdmai_id, u16 *token); int dpdmai_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); -int dpdmai_destroy(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); -int dpdmai_create(struct fsl_mc_io *mc_io, u32 cmd_flags, - const struct dpdmai_cfg *cfg, u16 *token); +int dpdmai_destroy(struct fsl_mc_io *mc_io, u32 cmd_flags, u32 dpdmai_id, u16 token); int dpdmai_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); int dpdmai_disable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); int dpdmai_reset(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); int dpdmai_get_attributes(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, struct dpdmai_attr *attr); int dpdmai_set_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, - u8 priority, const struct dpdmai_rx_queue_cfg *cfg); + u8 queue_idx, u8 priority, const struct dpdmai_rx_queue_cfg *cfg); int dpdmai_get_rx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, - u8 priority, struct dpdmai_rx_queue_attr *attr); + u8 queue_idx, u8 priority, struct dpdmai_rx_queue_attr *attr); int dpdmai_get_tx_queue(struct fsl_mc_io *mc_io, u32 cmd_flags, - u16 token, u8 priority, u32 *fqid); + u16 token, u8 queue_idx, u8 priority, struct dpdmai_tx_queue_attr *attr); #endif /* __FSL_DPDMAI_H */ diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index b18faa7cfedb..3af430787315 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -3,6 +3,8 @@ // Copyright (c) 2013-2014 Freescale Semiconductor, Inc // Copyright (c) 2017 Sysam, Angelo Dureghello <[email protected]> +#include <linux/cleanup.h> +#include <linux/clk.h> #include <linux/dmapool.h> #include <linux/module.h> #include <linux/slab.h> @@ -74,18 +76,10 @@ static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan) flags = fsl_edma_drvflags(fsl_chan); val = edma_readl_chreg(fsl_chan, ch_sbr); - /* Remote/local swapped wrongly on iMX8 QM Audio edma */ - if (flags & FSL_EDMA_DRV_QUIRK_SWAPPED) { - if (!fsl_chan->is_rxchan) - val |= EDMA_V3_CH_SBR_RD; - else - val |= EDMA_V3_CH_SBR_WR; - } else { - if (fsl_chan->is_rxchan) - val |= EDMA_V3_CH_SBR_RD; - else - val |= EDMA_V3_CH_SBR_WR; - } + if (fsl_chan->is_rxchan) + val |= EDMA_V3_CH_SBR_RD; + else + val |= EDMA_V3_CH_SBR_WR; if (fsl_chan->is_remote) val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR); @@ -546,6 +540,8 @@ void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan, csr |= EDMA_TCD_CSR_START; fsl_edma_set_tcd_to_le(fsl_chan, tcd, csr, csr); + + trace_edma_fill_tcd(fsl_chan, tcd); } static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, @@ -810,6 +806,9 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK) + clk_prepare_enable(fsl_chan->clk); + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_TCD64 ? sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd), @@ -838,6 +837,8 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_chan->tcd_pool = NULL; fsl_chan->is_sw = false; fsl_chan->srcid = 0; + if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK) + clk_disable_unprepare(fsl_chan->clk); } void fsl_edma_cleanup_vchan(struct dma_device *dmadev) diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index 7bf0aba471a8..ac66222c1604 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -151,7 +151,6 @@ struct fsl_edma_chan { enum dma_status status; enum fsl_edma_pm_state pm_state; bool idle; - u32 slave_id; struct fsl_edma_engine *edma; struct fsl_edma_desc *edesc; struct dma_slave_config cfg; @@ -195,8 +194,6 @@ struct fsl_edma_desc { #define FSL_EDMA_DRV_HAS_PD BIT(5) #define FSL_EDMA_DRV_HAS_CHCLK BIT(6) #define FSL_EDMA_DRV_HAS_CHMUX BIT(7) -/* imx8 QM audio edma remote local swapped */ -#define FSL_EDMA_DRV_QUIRK_SWAPPED BIT(8) /* control and status register is in tcd address space, edma3 reg layout */ #define FSL_EDMA_DRV_SPLIT_REG BIT(9) #define FSL_EDMA_DRV_BUS_8BYTE BIT(10) @@ -238,7 +235,6 @@ struct fsl_edma_engine { void __iomem *muxbase[DMAMUX_NR]; struct clk *muxclk[DMAMUX_NR]; struct clk *dmaclk; - struct clk *chclk; struct mutex fsl_edma_mutex; const struct fsl_edma_drvdata *drvdata; u32 n_chans; @@ -250,13 +246,17 @@ struct fsl_edma_engine { struct fsl_edma_chan chans[] __counted_by(n_chans); }; +static inline u32 fsl_edma_drvflags(struct fsl_edma_chan *fsl_chan) +{ + return fsl_chan->edma->drvdata->flags; +} + #define edma_read_tcdreg_c(chan, _tcd, __name) \ -(sizeof((_tcd)->__name) == sizeof(u64) ? \ - edma_readq(chan->edma, &(_tcd)->__name) : \ - ((sizeof((_tcd)->__name) == sizeof(u32)) ? \ - edma_readl(chan->edma, &(_tcd)->__name) : \ - edma_readw(chan->edma, &(_tcd)->__name) \ - )) +_Generic(((_tcd)->__name), \ + __iomem __le64 : edma_readq(chan->edma, &(_tcd)->__name), \ + __iomem __le32 : edma_readl(chan->edma, &(_tcd)->__name), \ + __iomem __le16 : edma_readw(chan->edma, &(_tcd)->__name) \ + ) #define edma_read_tcdreg(chan, __name) \ ((fsl_edma_drvflags(chan) & FSL_EDMA_DRV_TCD64) ? \ @@ -264,23 +264,13 @@ struct fsl_edma_engine { edma_read_tcdreg_c(chan, ((struct fsl_edma_hw_tcd __iomem *)chan->tcd), __name) \ ) -#define edma_write_tcdreg_c(chan, _tcd, _val, __name) \ -do { \ - switch (sizeof(_tcd->__name)) { \ - case sizeof(u64): \ - edma_writeq(chan->edma, (u64 __force)_val, &_tcd->__name); \ - break; \ - case sizeof(u32): \ - edma_writel(chan->edma, (u32 __force)_val, &_tcd->__name); \ - break; \ - case sizeof(u16): \ - edma_writew(chan->edma, (u16 __force)_val, &_tcd->__name); \ - break; \ - case sizeof(u8): \ - edma_writeb(chan->edma, (u8 __force)_val, &_tcd->__name); \ - break; \ - } \ -} while (0) +#define edma_write_tcdreg_c(chan, _tcd, _val, __name) \ +_Generic((_tcd->__name), \ + __iomem __le64 : edma_writeq(chan->edma, (u64 __force)(_val), &_tcd->__name), \ + __iomem __le32 : edma_writel(chan->edma, (u32 __force)(_val), &_tcd->__name), \ + __iomem __le16 : edma_writew(chan->edma, (u16 __force)(_val), &_tcd->__name), \ + __iomem u8 : edma_writeb(chan->edma, _val, &_tcd->__name) \ + ) #define edma_write_tcdreg(chan, val, __name) \ do { \ @@ -321,9 +311,11 @@ do { \ (((struct fsl_edma_hw_tcd *)_tcd)->_field)) #define fsl_edma_le_to_cpu(x) \ -(sizeof(x) == sizeof(u64) ? le64_to_cpu((__force __le64)(x)) : \ - (sizeof(x) == sizeof(u32) ? le32_to_cpu((__force __le32)(x)) : \ - le16_to_cpu((__force __le16)(x)))) +_Generic((x), \ + __le64 : le64_to_cpu((x)), \ + __le32 : le32_to_cpu((x)), \ + __le16 : le16_to_cpu((x)) \ +) #define fsl_edma_get_tcd_to_cpu(_chan, _tcd, _field) \ (fsl_edma_drvflags(_chan) & FSL_EDMA_DRV_TCD64 ? \ @@ -331,19 +323,11 @@ do { \ fsl_edma_le_to_cpu(((struct fsl_edma_hw_tcd *)_tcd)->_field)) #define fsl_edma_set_tcd_to_le_c(_tcd, _val, _field) \ -do { \ - switch (sizeof((_tcd)->_field)) { \ - case sizeof(u64): \ - *(__force __le64 *)(&((_tcd)->_field)) = cpu_to_le64(_val); \ - break; \ - case sizeof(u32): \ - *(__force __le32 *)(&((_tcd)->_field)) = cpu_to_le32(_val); \ - break; \ - case sizeof(u16): \ - *(__force __le16 *)(&((_tcd)->_field)) = cpu_to_le16(_val); \ - break; \ - } \ -} while (0) +_Generic(((_tcd)->_field), \ + __le64 : (_tcd)->_field = cpu_to_le64(_val), \ + __le32 : (_tcd)->_field = cpu_to_le32(_val), \ + __le16 : (_tcd)->_field = cpu_to_le16(_val) \ +) #define fsl_edma_set_tcd_to_le(_chan, _tcd, _val, _field) \ do { \ @@ -353,6 +337,9 @@ do { \ fsl_edma_set_tcd_to_le_c((struct fsl_edma_hw_tcd *)_tcd, _val, _field); \ } while (0) +/* Need after struct defination */ +#include "fsl-edma-trace.h" + /* * R/W functions for big- or little-endian registers: * The eDMA controller's endian is independent of the CPU core's endian. @@ -371,23 +358,38 @@ static inline u64 edma_readq(struct fsl_edma_engine *edma, void __iomem *addr) h = ioread32(addr + 4); } + trace_edma_readl(edma, addr, l); + trace_edma_readl(edma, addr + 4, h); + return (h << 32) | l; } static inline u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) { + u32 val; + if (edma->big_endian) - return ioread32be(addr); + val = ioread32be(addr); else - return ioread32(addr); + val = ioread32(addr); + + trace_edma_readl(edma, addr, val); + + return val; } static inline u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) { + u16 val; + if (edma->big_endian) - return ioread16be(addr); + val = ioread16be(addr); else - return ioread16(addr); + val = ioread16(addr); + + trace_edma_readw(edma, addr, val); + + return val; } static inline void edma_writeb(struct fsl_edma_engine *edma, @@ -398,6 +400,8 @@ static inline void edma_writeb(struct fsl_edma_engine *edma, iowrite8(val, (void __iomem *)((unsigned long)addr ^ 0x3)); else iowrite8(val, addr); + + trace_edma_writeb(edma, addr, val); } static inline void edma_writew(struct fsl_edma_engine *edma, @@ -408,6 +412,8 @@ static inline void edma_writew(struct fsl_edma_engine *edma, iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2)); else iowrite16(val, addr); + + trace_edma_writew(edma, addr, val); } static inline void edma_writel(struct fsl_edma_engine *edma, @@ -417,6 +423,8 @@ static inline void edma_writel(struct fsl_edma_engine *edma, iowrite32be(val, addr); else iowrite32(val, addr); + + trace_edma_writel(edma, addr, val); } static inline void edma_writeq(struct fsl_edma_engine *edma, @@ -429,6 +437,9 @@ static inline void edma_writeq(struct fsl_edma_engine *edma, iowrite32(val & 0xFFFFFFFF, addr); iowrite32(val >> 32, addr + 4); } + + trace_edma_writel(edma, addr, val & 0xFFFFFFFF); + trace_edma_writel(edma, addr + 4, val >> 32); } static inline struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) @@ -436,11 +447,6 @@ static inline struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) return container_of(chan, struct fsl_edma_chan, vchan.chan); } -static inline u32 fsl_edma_drvflags(struct fsl_edma_chan *fsl_chan) -{ - return fsl_chan->edma->drvdata->flags; -} - static inline struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) { return container_of(vd, struct fsl_edma_desc, vdesc); diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index 402f0058a180..391e4f13dfeb 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -105,7 +105,8 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, if (dma_spec->args_count != 2) return NULL; - mutex_lock(&fsl_edma->fsl_edma_mutex); + guard(mutex)(&fsl_edma->fsl_edma_mutex); + list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { if (chan->client_count) continue; @@ -114,15 +115,20 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, if (chan) { chan->device->privatecnt++; fsl_chan = to_fsl_edma_chan(chan); - fsl_chan->slave_id = dma_spec->args[1]; - fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, + fsl_chan->srcid = dma_spec->args[1]; + + if (!fsl_chan->srcid) { + dev_err(&fsl_chan->pdev->dev, "Invalidate srcid %d\n", + fsl_chan->srcid); + return NULL; + } + + fsl_edma_chan_mux(fsl_chan, fsl_chan->srcid, true); - mutex_unlock(&fsl_edma->fsl_edma_mutex); return chan; } } } - mutex_unlock(&fsl_edma->fsl_edma_mutex); return NULL; } @@ -342,10 +348,13 @@ static struct fsl_edma_drvdata imx8qm_data = { .setup_irq = fsl_edma3_irq_init, }; -static struct fsl_edma_drvdata imx8qm_audio_data = { - .flags = FSL_EDMA_DRV_QUIRK_SWAPPED | FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3, +static struct fsl_edma_drvdata imx8ulp_data = { + .flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_CHCLK | FSL_EDMA_DRV_HAS_DMACLK | + FSL_EDMA_DRV_EDMA3, .chreg_space_sz = 0x10000, .chreg_off = 0x10000, + .mux_off = 0x10000 + offsetof(struct fsl_edma3_ch_reg, ch_mux), + .mux_skip = 0x10000, .setup_irq = fsl_edma3_irq_init, }; @@ -380,7 +389,7 @@ static const struct of_device_id fsl_edma_dt_ids[] = { { .compatible = "fsl,ls1028a-edma", .data = &ls1028a_data}, { .compatible = "fsl,imx7ulp-edma", .data = &imx7ulp_data}, { .compatible = "fsl,imx8qm-edma", .data = &imx8qm_data}, - { .compatible = "fsl,imx8qm-adma", .data = &imx8qm_audio_data}, + { .compatible = "fsl,imx8ulp-edma", .data = &imx8ulp_data}, { .compatible = "fsl,imx93-edma3", .data = &imx93_data3}, { .compatible = "fsl,imx93-edma4", .data = &imx93_data4}, { .compatible = "fsl,imx95-edma5", .data = &imx95_data5}, @@ -434,6 +443,7 @@ static int fsl_edma_probe(struct platform_device *pdev) struct fsl_edma_engine *fsl_edma; const struct fsl_edma_drvdata *drvdata = NULL; u32 chan_mask[2] = {0, 0}; + char clk_name[36]; struct edma_regs *regs; int chans; int ret, i; @@ -476,14 +486,6 @@ static int fsl_edma_probe(struct platform_device *pdev) } } - if (drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) { - fsl_edma->chclk = devm_clk_get_enabled(&pdev->dev, "mp"); - if (IS_ERR(fsl_edma->chclk)) { - dev_err(&pdev->dev, "Missing MP block clock.\n"); - return PTR_ERR(fsl_edma->chclk); - } - } - ret = of_property_read_variable_u32_array(np, "dma-channel-mask", chan_mask, 1, 2); if (ret > 0) { @@ -540,7 +542,7 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->edma = fsl_edma; fsl_chan->pm_state = RUNNING; - fsl_chan->slave_id = 0; + fsl_chan->srcid = 0; fsl_chan->idle = true; fsl_chan->dma_dir = DMA_NONE; fsl_chan->vchan.desc_free = fsl_edma_free_desc; @@ -551,11 +553,21 @@ static int fsl_edma_probe(struct platform_device *pdev) + i * drvdata->chreg_space_sz + drvdata->chreg_off + len; fsl_chan->mux_addr = fsl_edma->membase + drvdata->mux_off + i * drvdata->mux_skip; + if (drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) { + snprintf(clk_name, sizeof(clk_name), "ch%02d", i); + fsl_chan->clk = devm_clk_get_enabled(&pdev->dev, + (const char *)clk_name); + + if (IS_ERR(fsl_chan->clk)) + return PTR_ERR(fsl_chan->clk); + } fsl_chan->pdev = pdev; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); edma_write_tcdreg(fsl_chan, cpu_to_le32(0), csr); fsl_edma_chan_mux(fsl_chan, 0, false); + if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) + clk_disable_unprepare(fsl_chan->clk); } ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma); @@ -682,8 +694,8 @@ static int fsl_edma_resume_early(struct device *dev) continue; fsl_chan->pm_state = RUNNING; edma_write_tcdreg(fsl_chan, 0, csr); - if (fsl_chan->slave_id != 0) - fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); + if (fsl_chan->srcid != 0) + fsl_edma_chan_mux(fsl_chan, fsl_chan->srcid, true); } if (!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)) diff --git a/drivers/dma/fsl-edma-trace.c b/drivers/dma/fsl-edma-trace.c new file mode 100644 index 000000000000..28300ad80bb7 --- /dev/null +++ b/drivers/dma/fsl-edma-trace.c @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define CREATE_TRACE_POINTS +#include "fsl-edma-common.h" diff --git a/drivers/dma/fsl-edma-trace.h b/drivers/dma/fsl-edma-trace.h new file mode 100644 index 000000000000..d3541301a247 --- /dev/null +++ b/drivers/dma/fsl-edma-trace.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 NXP. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fsl_edma + +#if !defined(__LINUX_FSL_EDMA_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __LINUX_FSL_EDMA_TRACE + +#include <linux/types.h> +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(edma_log_io, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value), + TP_STRUCT__entry( + __field(struct fsl_edma_engine *, edma) + __field(void __iomem *, addr) + __field(u32, value) + ), + TP_fast_assign( + __entry->edma = edma; + __entry->addr = addr; + __entry->value = value; + ), + TP_printk("offset %08x: value %08x", + (u32)(__entry->addr - __entry->edma->membase), __entry->value) +); + +DEFINE_EVENT(edma_log_io, edma_readl, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DEFINE_EVENT(edma_log_io, edma_writel, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DEFINE_EVENT(edma_log_io, edma_readw, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DEFINE_EVENT(edma_log_io, edma_writew, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DEFINE_EVENT(edma_log_io, edma_readb, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DEFINE_EVENT(edma_log_io, edma_writeb, + TP_PROTO(struct fsl_edma_engine *edma, void __iomem *addr, u32 value), + TP_ARGS(edma, addr, value) +); + +DECLARE_EVENT_CLASS(edma_log_tcd, + TP_PROTO(struct fsl_edma_chan *chan, void *tcd), + TP_ARGS(chan, tcd), + TP_STRUCT__entry( + __field(u64, saddr) + __field(u16, soff) + __field(u16, attr) + __field(u32, nbytes) + __field(u64, slast) + __field(u64, daddr) + __field(u16, doff) + __field(u16, citer) + __field(u64, dlast_sga) + __field(u16, csr) + __field(u16, biter) + + ), + TP_fast_assign( + __entry->saddr = fsl_edma_get_tcd_to_cpu(chan, tcd, saddr), + __entry->soff = fsl_edma_get_tcd_to_cpu(chan, tcd, soff), + __entry->attr = fsl_edma_get_tcd_to_cpu(chan, tcd, attr), + __entry->nbytes = fsl_edma_get_tcd_to_cpu(chan, tcd, nbytes), + __entry->slast = fsl_edma_get_tcd_to_cpu(chan, tcd, slast), + __entry->daddr = fsl_edma_get_tcd_to_cpu(chan, tcd, daddr), + __entry->doff = fsl_edma_get_tcd_to_cpu(chan, tcd, doff), + __entry->citer = fsl_edma_get_tcd_to_cpu(chan, tcd, citer), + __entry->dlast_sga = fsl_edma_get_tcd_to_cpu(chan, tcd, dlast_sga), + __entry->csr = fsl_edma_get_tcd_to_cpu(chan, tcd, csr), + __entry->biter = fsl_edma_get_tcd_to_cpu(chan, tcd, biter); + ), + TP_printk("\n==== TCD =====\n" + " saddr: 0x%016llx\n" + " soff: 0x%04x\n" + " attr: 0x%04x\n" + " nbytes: 0x%08x\n" + " slast: 0x%016llx\n" + " daddr: 0x%016llx\n" + " doff: 0x%04x\n" + " citer: 0x%04x\n" + " dlast: 0x%016llx\n" + " csr: 0x%04x\n" + " biter: 0x%04x\n", + __entry->saddr, + __entry->soff, + __entry->attr, + __entry->nbytes, + __entry->slast, + __entry->daddr, + __entry->doff, + __entry->citer, + __entry->dlast_sga, + __entry->csr, + __entry->biter) +); + +DEFINE_EVENT(edma_log_tcd, edma_fill_tcd, + TP_PROTO(struct fsl_edma_chan *chan, void *tcd), + TP_ARGS(chan, tcd) +); + +#endif + +/* this part must be outside header guard */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE fsl-edma-trace + +#include <trace/define_trace.h> diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c index 1398814d8fbb..e3505e56784b 100644 --- a/drivers/dma/idma64.c +++ b/drivers/dma/idma64.c @@ -598,7 +598,9 @@ static int idma64_probe(struct idma64_chip *chip) idma64->dma.dev = chip->sysdev; - dma_set_max_seg_size(idma64->dma.dev, IDMA64C_CTLH_BLOCK_TS_MASK); + ret = dma_set_max_seg_size(idma64->dma.dev, IDMA64C_CTLH_BLOCK_TS_MASK); + if (ret) + return ret; ret = dma_async_device_register(&idma64->dma); if (ret) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index 39935071174a..57f1bf2ab20b 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -577,7 +577,6 @@ void idxd_wq_del_cdev(struct idxd_wq *wq) struct idxd_cdev *idxd_cdev; idxd_cdev = wq->idxd_cdev; - ida_destroy(&file_ida); wq->idxd_cdev = NULL; cdev_device_del(&idxd_cdev->cdev, cdev_dev(idxd_cdev)); put_device(cdev_dev(idxd_cdev)); @@ -593,6 +592,14 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev) if (idxd->state != IDXD_DEV_ENABLED) return -ENXIO; + mutex_lock(&wq->wq_lock); + + if (!idxd_wq_driver_name_match(wq, dev)) { + idxd->cmd_status = IDXD_SCMD_WQ_NO_DRV_NAME; + rc = -ENODEV; + goto wq_err; + } + /* * User type WQ is enabled only when SVA is enabled for two reasons: * - If no IOMMU or IOMMU Passthrough without SVA, userspace @@ -608,14 +615,7 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev) dev_dbg(&idxd->pdev->dev, "User type WQ cannot be enabled without SVA.\n"); - return -EOPNOTSUPP; - } - - mutex_lock(&wq->wq_lock); - - if (!idxd_wq_driver_name_match(wq, dev)) { - idxd->cmd_status = IDXD_SCMD_WQ_NO_DRV_NAME; - rc = -ENODEV; + rc = -EOPNOTSUPP; goto wq_err; } diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 9b42f5e96b1e..003e1580b902 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -24,6 +24,7 @@ #include <linux/semaphore.h> #include <linux/spinlock.h> #include <linux/device.h> +#include <linux/genalloc.h> #include <linux/dma-mapping.h> #include <linux/firmware.h> #include <linux/slab.h> @@ -137,7 +138,11 @@ * 0: Source on AIPS * 12 Destination Bit(DP) 1: Destination on SPBA * 0: Destination on AIPS - * 13-15 --------- MUST BE 0 + * 13 Source FIFO 1: Source is dual FIFO + * 0: Source is single FIFO + * 14 Destination FIFO 1: Destination is dual FIFO + * 0: Destination is single FIFO + * 15 --------- MUST BE 0 * 16-23 Higher WML HWML * 24-27 N Total number of samples after * which Pad adding/Swallowing @@ -168,6 +173,8 @@ #define SDMA_WATERMARK_LEVEL_SPDIF BIT(10) #define SDMA_WATERMARK_LEVEL_SP BIT(11) #define SDMA_WATERMARK_LEVEL_DP BIT(12) +#define SDMA_WATERMARK_LEVEL_SD BIT(13) +#define SDMA_WATERMARK_LEVEL_DD BIT(14) #define SDMA_WATERMARK_LEVEL_HWML (0xFF << 16) #define SDMA_WATERMARK_LEVEL_LWE BIT(28) #define SDMA_WATERMARK_LEVEL_HWE BIT(29) @@ -175,6 +182,7 @@ #define SDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) #define SDMA_DMA_DIRECTIONS (BIT(DMA_DEV_TO_MEM) | \ @@ -232,20 +240,23 @@ struct sdma_script_start_addrs { s32 utra_addr; s32 ram_code_start_addr; /* End of v1 array */ - s32 mcu_2_ssish_addr; + union { s32 v1_end; s32 mcu_2_ssish_addr; }; s32 ssish_2_mcu_addr; s32 hdmi_dma_addr; /* End of v2 array */ - s32 zcanfd_2_mcu_addr; + union { s32 v2_end; s32 zcanfd_2_mcu_addr; }; s32 zqspi_2_mcu_addr; s32 mcu_2_ecspi_addr; s32 mcu_2_sai_addr; s32 sai_2_mcu_addr; s32 uart_2_mcu_rom_addr; s32 uartsh_2_mcu_rom_addr; + s32 i2c_2_mcu_addr; + s32 mcu_2_i2c_addr; /* End of v3 array */ - s32 mcu_2_zqspi_addr; + union { s32 v3_end; s32 mcu_2_zqspi_addr; }; /* End of v4 array */ + s32 v4_end[0]; }; /* @@ -531,6 +542,7 @@ struct sdma_engine { /* clock ratio for AHB:SDMA core. 1:1 is 1, 2:1 is 0*/ bool clk_ratio; bool fw_loaded; + struct gen_pool *iram_pool; }; static int sdma_config_write(struct dma_chan *chan, @@ -1072,6 +1084,11 @@ static int sdma_get_pc(struct sdma_channel *sdmac, per_2_emi = sdma->script_addrs->sai_2_mcu_addr; emi_2_per = sdma->script_addrs->mcu_2_sai_addr; break; + case IMX_DMATYPE_I2C: + per_2_emi = sdma->script_addrs->i2c_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_i2c_addr; + sdmac->is_ram_script = true; + break; case IMX_DMATYPE_HDMI: emi_2_per = sdma->script_addrs->hdmi_dma_addr; sdmac->is_ram_script = true; @@ -1255,6 +1272,16 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac) sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_DP; sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT; + + /* + * Limitation: The p2p script support dual fifos in maximum, + * So when fifo number is larger than 1, force enable dual + * fifos. + */ + if (sdmac->n_fifos_src > 1) + sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SD; + if (sdmac->n_fifos_dst > 1) + sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_DD; } static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac) @@ -1358,8 +1385,14 @@ static int sdma_request_channel0(struct sdma_engine *sdma) { int ret = -EBUSY; - sdma->bd0 = dma_alloc_coherent(sdma->dev, PAGE_SIZE, &sdma->bd0_phys, - GFP_NOWAIT); + if (sdma->iram_pool) + sdma->bd0 = gen_pool_dma_alloc(sdma->iram_pool, + sizeof(struct sdma_buffer_descriptor), + &sdma->bd0_phys); + else + sdma->bd0 = dma_alloc_coherent(sdma->dev, + sizeof(struct sdma_buffer_descriptor), + &sdma->bd0_phys, GFP_NOWAIT); if (!sdma->bd0) { ret = -ENOMEM; goto out; @@ -1379,10 +1412,14 @@ out: static int sdma_alloc_bd(struct sdma_desc *desc) { u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); + struct sdma_engine *sdma = desc->sdmac->sdma; int ret = 0; - desc->bd = dma_alloc_coherent(desc->sdmac->sdma->dev, bd_size, - &desc->bd_phys, GFP_NOWAIT); + if (sdma->iram_pool) + desc->bd = gen_pool_dma_alloc(sdma->iram_pool, bd_size, &desc->bd_phys); + else + desc->bd = dma_alloc_coherent(sdma->dev, bd_size, &desc->bd_phys, GFP_NOWAIT); + if (!desc->bd) { ret = -ENOMEM; goto out; @@ -1394,9 +1431,12 @@ out: static void sdma_free_bd(struct sdma_desc *desc) { u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); + struct sdma_engine *sdma = desc->sdmac->sdma; - dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd, - desc->bd_phys); + if (sdma->iram_pool) + gen_pool_free(sdma->iram_pool, (unsigned long)desc->bd, bd_size); + else + dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd, desc->bd_phys); } static void sdma_desc_free(struct virt_dma_desc *vd) @@ -1643,6 +1683,9 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( if (count & 3 || sg->dma_address & 3) goto err_bd_out; break; + case DMA_SLAVE_BUSWIDTH_3_BYTES: + bd->mode.command = 3; + break; case DMA_SLAVE_BUSWIDTH_2_BYTES: bd->mode.command = 2; if (count & 1 || sg->dma_address & 1) @@ -1880,10 +1923,17 @@ static void sdma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&sdmac->vc.lock, flags); } -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38 -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 45 -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4 46 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 \ +(offsetof(struct sdma_script_start_addrs, v1_end) / sizeof(s32)) + +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 \ +(offsetof(struct sdma_script_start_addrs, v2_end) / sizeof(s32)) + +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 \ +(offsetof(struct sdma_script_start_addrs, v3_end) / sizeof(s32)) + +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4 \ +(offsetof(struct sdma_script_start_addrs, v4_end) / sizeof(s32)) static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) @@ -2068,6 +2118,7 @@ static int sdma_init(struct sdma_engine *sdma) { int i, ret; dma_addr_t ccb_phys; + int ccbsize; ret = clk_enable(sdma->clk_ipg); if (ret) @@ -2083,10 +2134,14 @@ static int sdma_init(struct sdma_engine *sdma) /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); - sdma->channel_control = dma_alloc_coherent(sdma->dev, - MAX_DMA_CHANNELS * sizeof(struct sdma_channel_control) + - sizeof(struct sdma_context_data), - &ccb_phys, GFP_KERNEL); + ccbsize = MAX_DMA_CHANNELS * (sizeof(struct sdma_channel_control) + + sizeof(struct sdma_context_data)); + + if (sdma->iram_pool) + sdma->channel_control = gen_pool_dma_alloc(sdma->iram_pool, ccbsize, &ccb_phys); + else + sdma->channel_control = dma_alloc_coherent(sdma->dev, ccbsize, &ccb_phys, + GFP_KERNEL); if (!sdma->channel_control) { ret = -ENOMEM; @@ -2272,6 +2327,12 @@ static int sdma_probe(struct platform_device *pdev) vchan_init(&sdmac->vc, &sdma->dma_device); } + if (np) { + sdma->iram_pool = of_gen_pool_get(np, "iram", 0); + if (sdma->iram_pool) + dev_info(&pdev->dev, "alloc bd from iram.\n"); + } + ret = sdma_init(sdma); if (ret) goto err_init; diff --git a/drivers/dma/mcf-edma-main.c b/drivers/dma/mcf-edma-main.c index dba631783876..78c606f6d002 100644 --- a/drivers/dma/mcf-edma-main.c +++ b/drivers/dma/mcf-edma-main.c @@ -195,7 +195,7 @@ static int mcf_edma_probe(struct platform_device *pdev) struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i]; mcf_chan->edma = mcf_edma; - mcf_chan->slave_id = i; + mcf_chan->srcid = i; mcf_chan->idle = true; mcf_chan->dma_dir = DMA_NONE; mcf_chan->vchan.desc_free = fsl_edma_free_desc; @@ -277,7 +277,7 @@ bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) if (chan->device->dev->driver == &mcf_edma_driver.driver) { struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan); - return (mcf_chan->slave_id == (uintptr_t)param); + return (mcf_chan->srcid == (uintptr_t)param); } return false; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index c359decc07a3..6b2793b07694 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -155,11 +155,6 @@ static inline struct device *chan2dev(struct dma_chan *chan) return &chan->dev->device; } -static inline struct device *chan2parent(struct dma_chan *chan) -{ - return chan->dev->device.parent; -} - static inline struct pch_dma_desc *pdc_first_active(struct pch_dma_chan *pd_chan) { diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 202ac95227cb..721b4ac0857a 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -50,7 +50,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/of_dma.h> #include <linux/property.h> #include <linux/delay.h> #include <linux/acpi.h> @@ -947,22 +946,12 @@ static const struct acpi_device_id hidma_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, hidma_acpi_ids); #endif -static const struct of_device_id hidma_match[] = { - {.compatible = "qcom,hidma-1.0",}, - {.compatible = "qcom,hidma-1.1", .data = (void *)(HIDMA_MSI_CAP),}, - {.compatible = "qcom,hidma-1.2", - .data = (void *)(HIDMA_MSI_CAP | HIDMA_IDENTITY_CAP),}, - {}, -}; -MODULE_DEVICE_TABLE(of, hidma_match); - static struct platform_driver hidma_driver = { .probe = hidma_probe, .remove_new = hidma_remove, .shutdown = hidma_shutdown, .driver = { .name = "hidma", - .of_match_table = hidma_match, .acpi_match_table = ACPI_PTR(hidma_acpi_ids), }, }; diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c index 1d675f31252b..bb883e138ebf 100644 --- a/drivers/dma/qcom/hidma_mgmt.c +++ b/drivers/dma/qcom/hidma_mgmt.c @@ -7,12 +7,7 @@ #include <linux/dmaengine.h> #include <linux/acpi.h> -#include <linux/of.h> #include <linux/property.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/uaccess.h> @@ -327,115 +322,13 @@ static const struct acpi_device_id hidma_mgmt_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, hidma_mgmt_acpi_ids); #endif -static const struct of_device_id hidma_mgmt_match[] = { - {.compatible = "qcom,hidma-mgmt-1.0",}, - {}, -}; -MODULE_DEVICE_TABLE(of, hidma_mgmt_match); - static struct platform_driver hidma_mgmt_driver = { .probe = hidma_mgmt_probe, .driver = { .name = "hidma-mgmt", - .of_match_table = hidma_mgmt_match, .acpi_match_table = ACPI_PTR(hidma_mgmt_acpi_ids), }, }; -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) -static int object_counter; - -static int __init hidma_mgmt_of_populate_channels(struct device_node *np) -{ - struct platform_device *pdev_parent = of_find_device_by_node(np); - struct platform_device_info pdevinfo; - struct device_node *child; - struct resource *res; - int ret = 0; - - /* allocate a resource array */ - res = kcalloc(3, sizeof(*res), GFP_KERNEL); - if (!res) - return -ENOMEM; - - for_each_available_child_of_node(np, child) { - struct platform_device *new_pdev; - - ret = of_address_to_resource(child, 0, &res[0]); - if (!ret) - goto out; - - ret = of_address_to_resource(child, 1, &res[1]); - if (!ret) - goto out; - - ret = of_irq_to_resource(child, 0, &res[2]); - if (ret <= 0) - goto out; - - memset(&pdevinfo, 0, sizeof(pdevinfo)); - pdevinfo.fwnode = &child->fwnode; - pdevinfo.parent = pdev_parent ? &pdev_parent->dev : NULL; - pdevinfo.name = child->name; - pdevinfo.id = object_counter++; - pdevinfo.res = res; - pdevinfo.num_res = 3; - pdevinfo.data = NULL; - pdevinfo.size_data = 0; - pdevinfo.dma_mask = DMA_BIT_MASK(64); - new_pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(new_pdev)) { - ret = PTR_ERR(new_pdev); - goto out; - } - new_pdev->dev.of_node = child; - of_dma_configure(&new_pdev->dev, child, true); - /* - * It is assumed that calling of_msi_configure is safe on - * platforms with or without MSI support. - */ - of_msi_configure(&new_pdev->dev, child); - } - - kfree(res); - - return ret; - -out: - of_node_put(child); - kfree(res); - - return ret; -} -#endif - -static int __init hidma_mgmt_init(void) -{ -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) - struct device_node *child; - - for_each_matching_node(child, hidma_mgmt_match) { - /* device tree based firmware here */ - hidma_mgmt_of_populate_channels(child); - } -#endif - /* - * We do not check for return value here, as it is assumed that - * platform_driver_register must not fail. The reason for this is that - * the (potential) hidma_mgmt_of_populate_channels calls above are not - * cleaned up if it does fail, and to do this work is quite - * complicated. In particular, various calls of of_address_to_resource, - * of_irq_to_resource, platform_device_register_full, of_dma_configure, - * and of_msi_configure which then call other functions and so on, must - * be cleaned up - this is not a trivial exercise. - * - * Currently, this module is not intended to be unloaded, and there is - * no module_exit function defined which does the needed cleanup. For - * this reason, we have to assume success here. - */ - platform_driver_register(&hidma_mgmt_driver); - - return 0; -} -module_init(hidma_mgmt_init); +module_platform_driver(hidma_mgmt_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h index e9f5250fbe4d..59d9eabc8b67 100644 --- a/drivers/dma/virt-dma.h +++ b/drivers/dma/virt-dma.h @@ -81,6 +81,8 @@ static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan */ static inline bool vchan_issue_pending(struct virt_dma_chan *vc) { + lockdep_assert_held(&vc->lock); + list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued); return !list_empty(&vc->desc_issued); } @@ -96,6 +98,8 @@ static inline void vchan_cookie_complete(struct virt_dma_desc *vd) struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); dma_cookie_t cookie; + lockdep_assert_held(&vc->lock); + cookie = vd->tx.cookie; dma_cookie_complete(&vd->tx); dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n", @@ -146,6 +150,8 @@ static inline void vchan_terminate_vdesc(struct virt_dma_desc *vd) { struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); + lockdep_assert_held(&vc->lock); + list_add_tail(&vd->node, &vc->desc_terminated); if (vc->cyclic == vd) @@ -160,6 +166,8 @@ static inline void vchan_terminate_vdesc(struct virt_dma_desc *vd) */ static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc) { + lockdep_assert_held(&vc->lock); + return list_first_entry_or_null(&vc->desc_issued, struct virt_dma_desc, node); } @@ -177,6 +185,8 @@ static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc) static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc, struct list_head *head) { + lockdep_assert_held(&vc->lock); + list_splice_tail_init(&vc->desc_allocated, head); list_splice_tail_init(&vc->desc_submitted, head); list_splice_tail_init(&vc->desc_issued, head); diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c index 313b217388fe..e143a7330816 100644 --- a/drivers/dma/xilinx/xdma.c +++ b/drivers/dma/xilinx/xdma.c @@ -1307,6 +1307,7 @@ static const struct platform_device_id xdma_id_table[] = { { "xdma", 0}, { }, }; +MODULE_DEVICE_TABLE(platform, xdma_id_table); static struct platform_driver xdma_driver = { .driver = { diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index eb0637d90342..36bd4825d389 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -1043,9 +1043,8 @@ static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan) static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan) { struct xilinx_dpdma_tx_desc *active; - unsigned long flags; - spin_lock_irqsave(&chan->lock, flags); + spin_lock(&chan->lock); xilinx_dpdma_debugfs_desc_done_irq(chan); @@ -1057,7 +1056,7 @@ static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan) "chan%u: DONE IRQ with no active descriptor!\n", chan->id); - spin_unlock_irqrestore(&chan->lock, flags); + spin_unlock(&chan->lock); } /** @@ -1072,10 +1071,9 @@ static void xilinx_dpdma_chan_vsync_irq(struct xilinx_dpdma_chan *chan) { struct xilinx_dpdma_tx_desc *pending; struct xilinx_dpdma_sw_desc *sw_desc; - unsigned long flags; u32 desc_id; - spin_lock_irqsave(&chan->lock, flags); + spin_lock(&chan->lock); pending = chan->desc.pending; if (!chan->running || !pending) @@ -1108,7 +1106,7 @@ static void xilinx_dpdma_chan_vsync_irq(struct xilinx_dpdma_chan *chan) spin_unlock(&chan->vchan.lock); out: - spin_unlock_irqrestore(&chan->lock, flags); + spin_unlock(&chan->lock); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 665c63f55278..013ff373e067 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -279,7 +279,7 @@ int amdgpu_irq_init(struct amdgpu_device *adev) adev->irq.msi_enabled = false; if (!amdgpu_msi_ok(adev)) - flags = PCI_IRQ_LEGACY; + flags = PCI_IRQ_INTX; else flags = PCI_IRQ_ALL_TYPES; diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c index 6af57067c32e..78dfe98ebcf7 100644 --- a/drivers/infiniband/hw/qib/qib_iba7220.c +++ b/drivers/infiniband/hw/qib/qib_iba7220.c @@ -3281,7 +3281,7 @@ static int qib_7220_intr_fallback(struct qib_devdata *dd) qib_free_irq(dd); dd->msi_lo = 0; - if (pci_alloc_irq_vectors(dd->pcidev, 1, 1, PCI_IRQ_LEGACY) < 0) + if (pci_alloc_irq_vectors(dd->pcidev, 1, 1, PCI_IRQ_INTX) < 0) qib_dev_err(dd, "Failed to enable INTx\n"); qib_setup_7220_interrupt(dd); return 1; diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index f93906d8fc09..9db29916e35a 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -3471,8 +3471,7 @@ try_intx: pci_irq_vector(dd->pcidev, msixnum), ret); qib_7322_free_irq(dd); - pci_alloc_irq_vectors(dd->pcidev, 1, 1, - PCI_IRQ_LEGACY); + pci_alloc_irq_vectors(dd->pcidev, 1, 1, PCI_IRQ_INTX); goto try_intx; } dd->cspec->msix_entries[msixnum].arg = arg; @@ -5143,7 +5142,7 @@ static int qib_7322_intr_fallback(struct qib_devdata *dd) qib_devinfo(dd->pcidev, "MSIx interrupt not detected, trying INTx interrupts\n"); qib_7322_free_irq(dd); - if (pci_alloc_irq_vectors(dd->pcidev, 1, 1, PCI_IRQ_LEGACY) < 0) + if (pci_alloc_irq_vectors(dd->pcidev, 1, 1, PCI_IRQ_INTX) < 0) qib_dev_err(dd, "Failed to enable INTx\n"); qib_setup_7322_interrupt(dd, 0); return 1; diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 47bf64ace05c..58c1d62d341b 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -210,7 +210,7 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent) } if (dd->flags & QIB_HAS_INTX) - flags |= PCI_IRQ_LEGACY; + flags |= PCI_IRQ_INTX; maxvec = (nent && *nent) ? *nent : 1; nvec = pci_alloc_irq_vectors(dd->pcidev, 1, maxvec, flags); if (nvec < 0) diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index a5e88185171f..768aad364c89 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -531,7 +531,7 @@ static int pvrdma_alloc_intrs(struct pvrdma_dev *dev) PCI_IRQ_MSIX); if (ret < 0) { ret = pci_alloc_irq_vectors(pdev, 1, 1, - PCI_IRQ_MSI | PCI_IRQ_LEGACY); + PCI_IRQ_MSI | PCI_IRQ_INTX); if (ret < 0) return ret; } diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 1fdf37e04215..52d83730a22a 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -3784,20 +3784,11 @@ static struct irq_chip amd_ir_chip = { }; static const struct msi_parent_ops amdvi_msi_parent_ops = { - .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | - MSI_FLAG_MULTI_PCI_MSI | - MSI_FLAG_PCI_IMS, + .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | MSI_FLAG_MULTI_PCI_MSI, .prefix = "IR-", .init_dev_msi_info = msi_parent_init_dev_msi_info, }; -static const struct msi_parent_ops virt_amdvi_msi_parent_ops = { - .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | - MSI_FLAG_MULTI_PCI_MSI, - .prefix = "vIR-", - .init_dev_msi_info = msi_parent_init_dev_msi_info, -}; - int amd_iommu_create_irq_domain(struct amd_iommu *iommu) { struct fwnode_handle *fn; @@ -3815,11 +3806,7 @@ int amd_iommu_create_irq_domain(struct amd_iommu *iommu) irq_domain_update_bus_token(iommu->ir_domain, DOMAIN_BUS_AMDVI); iommu->ir_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT | IRQ_DOMAIN_FLAG_ISOLATED_MSI; - - if (amd_iommu_np_cache) - iommu->ir_domain->msi_parent_ops = &virt_amdvi_msi_parent_ops; - else - iommu->ir_domain->msi_parent_ops = &amdvi_msi_parent_ops; + iommu->ir_domain->msi_parent_ops = &amdvi_msi_parent_ops; return 0; } diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c index d9bcbb1aba89..e4a70886678c 100644 --- a/drivers/iommu/intel/irq_remapping.c +++ b/drivers/iommu/intel/irq_remapping.c @@ -85,7 +85,7 @@ static const struct irq_domain_ops intel_ir_domain_ops; static void iommu_disable_irq_remapping(struct intel_iommu *iommu); static int __init parse_ioapics_under_ir(void); -static const struct msi_parent_ops dmar_msi_parent_ops, virt_dmar_msi_parent_ops; +static const struct msi_parent_ops dmar_msi_parent_ops; static bool ir_pre_enabled(struct intel_iommu *iommu) { @@ -570,11 +570,7 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu) irq_domain_update_bus_token(iommu->ir_domain, DOMAIN_BUS_DMAR); iommu->ir_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT | IRQ_DOMAIN_FLAG_ISOLATED_MSI; - - if (cap_caching_mode(iommu->cap)) - iommu->ir_domain->msi_parent_ops = &virt_dmar_msi_parent_ops; - else - iommu->ir_domain->msi_parent_ops = &dmar_msi_parent_ops; + iommu->ir_domain->msi_parent_ops = &dmar_msi_parent_ops; ir_table->base = ir_table_base; ir_table->bitmap = bitmap; @@ -1526,20 +1522,11 @@ static const struct irq_domain_ops intel_ir_domain_ops = { }; static const struct msi_parent_ops dmar_msi_parent_ops = { - .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | - MSI_FLAG_MULTI_PCI_MSI | - MSI_FLAG_PCI_IMS, + .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | MSI_FLAG_MULTI_PCI_MSI, .prefix = "IR-", .init_dev_msi_info = msi_parent_init_dev_msi_info, }; -static const struct msi_parent_ops virt_dmar_msi_parent_ops = { - .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED | - MSI_FLAG_MULTI_PCI_MSI, - .prefix = "vIR-", - .init_dev_msi_info = msi_parent_init_dev_msi_info, -}; - /* * Support of Interrupt Remapping Unit Hotplug */ diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 42940108a187..3b8842c4a340 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -23,6 +23,18 @@ config ARM_MHU_V2 Say Y here if you want to build the ARM MHUv2 controller driver, which provides unidirectional mailboxes between processing elements. +config ARM_MHU_V3 + tristate "ARM MHUv3 Mailbox" + depends on HAS_IOMEM || COMPILE_TEST + depends on OF + help + Say Y here if you want to build the ARM MHUv3 controller driver, + which provides unidirectional mailboxes between processing elements. + + ARM MHUv3 controllers can implement a varying number of extensions + that provides different means of transports: supported extensions + will be discovered and possibly managed at probe-time. + config IMX_MBOX tristate "i.MX Mailbox" depends on ARCH_MXC || COMPILE_TEST @@ -68,15 +80,6 @@ config OMAP2PLUS_MBOX OMAP2/3; or IPU, IVA HD and DSP in OMAP4/5. Say Y here if you want to use OMAP2+ Mailbox framework support. -config OMAP_MBOX_KFIFO_SIZE - int "Mailbox kfifo default buffer size (bytes)" - depends on OMAP2PLUS_MBOX - default 256 - help - Specify the default size of mailbox's kfifo buffers (bytes). - This can also be changed at runtime (via the mbox_kfifo_size - module parameter). - config ROCKCHIP_MBOX bool "Rockchip Soc Integrated Mailbox Support" depends on ARCH_ROCKCHIP || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 18793e6caa2f..5cf2f54debaf 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_ARM_MHU) += arm_mhu.o arm_mhu_db.o obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o +obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o + obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX) += armada-37xx-rwtm-mailbox.o diff --git a/drivers/mailbox/arm_mhuv3.c b/drivers/mailbox/arm_mhuv3.c new file mode 100644 index 000000000000..b97e79a5870f --- /dev/null +++ b/drivers/mailbox/arm_mhuv3.c @@ -0,0 +1,1103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Message Handling Unit Version 3 (MHUv3) driver. + * + * Copyright (C) 2024 ARM Ltd. + * + * Based on ARM MHUv2 driver. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/types.h> + +/* ====== MHUv3 Registers ====== */ + +/* Maximum number of Doorbell channel windows */ +#define MHUV3_DBCW_MAX 128 +/* Number of DBCH combined interrupt status registers */ +#define MHUV3_DBCH_CMB_INT_ST_REG_CNT 4 + +/* Number of FFCH combined interrupt status registers */ +#define MHUV3_FFCH_CMB_INT_ST_REG_CNT 2 + +#define MHUV3_FLAG_BITS 32 + +/* Not a typo ... */ +#define MHUV3_MAJOR_VERSION 2 + +enum { + MHUV3_MBOX_CELL_TYPE, + MHUV3_MBOX_CELL_CHWN, + MHUV3_MBOX_CELL_PARAM, + MHUV3_MBOX_CELLS +}; + +/* Padding bitfields/fields represents hole in the regs MMIO */ + +/* CTRL_Page */ +struct blk_id { +#define id GENMASK(3, 0) + u32 val; +} __packed; + +struct feat_spt0 { +#define dbe_spt GENMASK(3, 0) +#define fe_spt GENMASK(7, 4) +#define fce_spt GENMASK(11, 8) + u32 val; +} __packed; + +struct feat_spt1 { +#define auto_op_spt GENMASK(3, 0) + u32 val; +} __packed; + +struct dbch_cfg0 { +#define num_dbch GENMASK(7, 0) + u32 val; +} __packed; + +struct ffch_cfg0 { +#define num_ffch GENMASK(7, 0) +#define x8ba_spt BIT(8) +#define x16ba_spt BIT(9) +#define x32ba_spt BIT(10) +#define x64ba_spt BIT(11) +#define ffch_depth GENMASK(25, 16) + u32 val; +} __packed; + +struct fch_cfg0 { +#define num_fch GENMASK(9, 0) +#define fcgi_spt BIT(10) // MBX-only +#define num_fcg GENMASK(15, 11) +#define num_fch_per_grp GENMASK(20, 16) +#define fch_ws GENMASK(28, 21) + u32 val; +} __packed; + +struct ctrl { +#define op_req BIT(0) +#define ch_op_mask BIT(1) + u32 val; +} __packed; + +struct fch_ctrl { +#define _int_en BIT(2) + u32 val; +} __packed; + +struct iidr { +#define implementer GENMASK(11, 0) +#define revision GENMASK(15, 12) +#define variant GENMASK(19, 16) +#define product_id GENMASK(31, 20) + u32 val; +} __packed; + +struct aidr { +#define arch_minor_rev GENMASK(3, 0) +#define arch_major_rev GENMASK(7, 4) + u32 val; +} __packed; + +struct ctrl_page { + struct blk_id blk_id; + u8 pad[12]; + struct feat_spt0 feat_spt0; + struct feat_spt1 feat_spt1; + u8 pad1[8]; + struct dbch_cfg0 dbch_cfg0; + u8 pad2[12]; + struct ffch_cfg0 ffch_cfg0; + u8 pad3[12]; + struct fch_cfg0 fch_cfg0; + u8 pad4[188]; + struct ctrl x_ctrl; + /*-- MBX-only registers --*/ + u8 pad5[60]; + struct fch_ctrl fch_ctrl; + u32 fcg_int_en; + u8 pad6[696]; + /*-- End of MBX-only ---- */ + u32 dbch_int_st[MHUV3_DBCH_CMB_INT_ST_REG_CNT]; + u32 ffch_int_st[MHUV3_FFCH_CMB_INT_ST_REG_CNT]; + /*-- MBX-only registers --*/ + u8 pad7[88]; + u32 fcg_int_st; + u8 pad8[12]; + u32 fcg_grp_int_st[32]; + u8 pad9[2760]; + /*-- End of MBX-only ---- */ + struct iidr iidr; + struct aidr aidr; + u32 imp_def_id[12]; +} __packed; + +/* DBCW_Page */ + +struct xbcw_ctrl { +#define comb_en BIT(0) + u32 val; +} __packed; + +struct pdbcw_int { +#define tfr_ack BIT(0) + u32 val; +} __packed; + +struct pdbcw_page { + u32 st; + u8 pad[8]; + u32 set; + struct pdbcw_int int_st; + struct pdbcw_int int_clr; + struct pdbcw_int int_en; + struct xbcw_ctrl ctrl; +} __packed; + +struct mdbcw_page { + u32 st; + u32 st_msk; + u32 clr; + u8 pad[4]; + u32 msk_st; + u32 msk_set; + u32 msk_clr; + struct xbcw_ctrl ctrl; +} __packed; + +struct dummy_page { + u8 pad[SZ_4K]; +} __packed; + +struct mhu3_pbx_frame_reg { + struct ctrl_page ctrl; + struct pdbcw_page dbcw[MHUV3_DBCW_MAX]; + struct dummy_page ffcw; + struct dummy_page fcw; + u8 pad[SZ_4K * 11]; + struct dummy_page impdef; +} __packed; + +struct mhu3_mbx_frame_reg { + struct ctrl_page ctrl; + struct mdbcw_page dbcw[MHUV3_DBCW_MAX]; + struct dummy_page ffcw; + struct dummy_page fcw; + u8 pad[SZ_4K * 11]; + struct dummy_page impdef; +} __packed; + +/* Macro for reading a bitmask within a physically mapped packed struct */ +#define readl_relaxed_bitmask(_regptr, _bitmask) \ + ({ \ + unsigned long _rval; \ + _rval = readl_relaxed(_regptr); \ + FIELD_GET(_bitmask, _rval); \ + }) + +/* Macro for writing a bitmask within a physically mapped packed struct */ +#define writel_relaxed_bitmask(_value, _regptr, _bitmask) \ + ({ \ + unsigned long _rval; \ + typeof(_regptr) _rptr = _regptr; \ + typeof(_bitmask) _bmask = _bitmask; \ + _rval = readl_relaxed(_rptr); \ + _rval &= ~(_bmask); \ + _rval |= FIELD_PREP((unsigned long long)_bmask, _value);\ + writel_relaxed(_rval, _rptr); \ + }) + +/* ====== MHUv3 data structures ====== */ + +enum mhuv3_frame { + PBX_FRAME, + MBX_FRAME, +}; + +static char *mhuv3_str[] = { + "PBX", + "MBX" +}; + +enum mhuv3_extension_type { + DBE_EXT, + FCE_EXT, + FE_EXT, + NUM_EXT +}; + +static char *mhuv3_ext_str[] = { + "DBE", + "FCE", + "FE" +}; + +struct mhuv3; + +/** + * struct mhuv3_protocol_ops - MHUv3 operations + * + * @rx_startup: Receiver startup callback. + * @rx_shutdown: Receiver shutdown callback. + * @read_data: Read available Sender in-band LE data (if any). + * @rx_complete: Acknowledge data reception to the Sender. Any out-of-band data + * has to have been already retrieved before calling this. + * @tx_startup: Sender startup callback. + * @tx_shutdown: Sender shutdown callback. + * @last_tx_done: Report back to the Sender if the last transfer has completed. + * @send_data: Send data to the receiver. + * + * Each supported transport protocol provides its own implementation of + * these operations. + */ +struct mhuv3_protocol_ops { + int (*rx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan); + void (*rx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan); + void *(*read_data)(struct mhuv3 *mhu, struct mbox_chan *chan); + void (*rx_complete)(struct mhuv3 *mhu, struct mbox_chan *chan); + void (*tx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan); + void (*tx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan); + int (*last_tx_done)(struct mhuv3 *mhu, struct mbox_chan *chan); + int (*send_data)(struct mhuv3 *mhu, struct mbox_chan *chan, void *arg); +}; + +/** + * struct mhuv3_mbox_chan_priv - MHUv3 channel private information + * + * @ch_idx: Channel window index associated to this mailbox channel. + * @doorbell: Doorbell bit number within the @ch_idx window. + * Only relevant to Doorbell transport. + * @ops: Transport protocol specific operations for this channel. + * + * Transport specific data attached to mmailbox channel priv data. + */ +struct mhuv3_mbox_chan_priv { + u32 ch_idx; + u32 doorbell; + const struct mhuv3_protocol_ops *ops; +}; + +/** + * struct mhuv3_extension - MHUv3 extension descriptor + * + * @type: Type of extension + * @num_chans: Max number of channels found for this extension. + * @base_ch_idx: First channel number assigned to this extension, picked from + * the set of all mailbox channels descriptors created. + * @mbox_of_xlate: Extension specific helper to parse DT and lookup associated + * channel from the related 'mboxes' property. + * @combined_irq_setup: Extension specific helper to setup the combined irq. + * @channels_init: Extension specific helper to initialize channels. + * @chan_from_comb_irq_get: Extension specific helper to lookup which channel + * triggered the combined irq. + * @pending_db: Array of per-channel pending doorbells. + * @pending_lock: Protect access to pending_db. + */ +struct mhuv3_extension { + enum mhuv3_extension_type type; + unsigned int num_chans; + unsigned int base_ch_idx; + struct mbox_chan *(*mbox_of_xlate)(struct mhuv3 *mhu, + unsigned int channel, + unsigned int param); + void (*combined_irq_setup)(struct mhuv3 *mhu); + int (*channels_init)(struct mhuv3 *mhu); + struct mbox_chan *(*chan_from_comb_irq_get)(struct mhuv3 *mhu); + u32 pending_db[MHUV3_DBCW_MAX]; + /* Protect access to pending_db */ + spinlock_t pending_lock; +}; + +/** + * struct mhuv3 - MHUv3 mailbox controller data + * + * @frame: Frame type: MBX_FRAME or PBX_FRAME. + * @auto_op_full: Flag to indicate if the MHU supports AutoOp full mode. + * @major: MHUv3 controller architectural major version. + * @minor: MHUv3 controller architectural minor version. + * @implem: MHUv3 controller IIDR implementer. + * @rev: MHUv3 controller IIDR revision. + * @var: MHUv3 controller IIDR variant. + * @prod_id: MHUv3 controller IIDR product_id. + * @num_chans: The total number of channnels discovered across all extensions. + * @cmb_irq: Combined IRQ number if any found defined. + * @ctrl: A reference to the MHUv3 control page for this block. + * @pbx: Base address of the PBX register mapping region. + * @mbx: Base address of the MBX register mapping region. + * @ext: Array holding descriptors for any found implemented extension. + * @mbox: Mailbox controller belonging to the MHU frame. + */ +struct mhuv3 { + enum mhuv3_frame frame; + bool auto_op_full; + unsigned int major; + unsigned int minor; + unsigned int implem; + unsigned int rev; + unsigned int var; + unsigned int prod_id; + unsigned int num_chans; + int cmb_irq; + struct ctrl_page __iomem *ctrl; + union { + struct mhu3_pbx_frame_reg __iomem *pbx; + struct mhu3_mbx_frame_reg __iomem *mbx; + }; + struct mhuv3_extension *ext[NUM_EXT]; + struct mbox_controller mbox; +}; + +#define mhu_from_mbox(_mbox) container_of(_mbox, struct mhuv3, mbox) + +typedef int (*mhuv3_extension_initializer)(struct mhuv3 *mhu); + +/* =================== Doorbell transport protocol operations =============== */ + +static void mhuv3_doorbell_tx_startup(struct mhuv3 *mhu, struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + + /* Enable Transfer Acknowledgment events */ + writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack); +} + +static void mhuv3_doorbell_tx_shutdown(struct mhuv3 *mhu, struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + unsigned long flags; + + /* Disable Channel Transfer Ack events */ + writel_relaxed_bitmask(0x0, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack); + + /* Clear Channel Transfer Ack and pending doorbells */ + writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_clr, tfr_ack); + spin_lock_irqsave(&e->pending_lock, flags); + e->pending_db[priv->ch_idx] = 0; + spin_unlock_irqrestore(&e->pending_lock, flags); +} + +static int mhuv3_doorbell_rx_startup(struct mhuv3 *mhu, struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + + /* Unmask Channel Transfer events */ + writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_clr); + + return 0; +} + +static void mhuv3_doorbell_rx_shutdown(struct mhuv3 *mhu, + struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + + /* Mask Channel Transfer events */ + writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_set); +} + +static void mhuv3_doorbell_rx_complete(struct mhuv3 *mhu, struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + + /* Clearing the pending transfer generates the Channel Transfer Ack */ + writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].clr); +} + +static int mhuv3_doorbell_last_tx_done(struct mhuv3 *mhu, + struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + int done; + + done = !(readl_relaxed(&mhu->pbx->dbcw[priv->ch_idx].st) & + BIT(priv->doorbell)); + if (done) { + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + unsigned long flags; + + /* Take care to clear the pending doorbell also when polling */ + spin_lock_irqsave(&e->pending_lock, flags); + e->pending_db[priv->ch_idx] &= ~BIT(priv->doorbell); + spin_unlock_irqrestore(&e->pending_lock, flags); + } + + return done; +} + +static int mhuv3_doorbell_send_data(struct mhuv3 *mhu, struct mbox_chan *chan, + void *arg) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + + scoped_guard(spinlock_irqsave, &e->pending_lock) { + /* Only one in-flight Transfer is allowed per-doorbell */ + if (e->pending_db[priv->ch_idx] & BIT(priv->doorbell)) + return -EBUSY; + + e->pending_db[priv->ch_idx] |= BIT(priv->doorbell); + } + + writel_relaxed(BIT(priv->doorbell), &mhu->pbx->dbcw[priv->ch_idx].set); + + return 0; +} + +static const struct mhuv3_protocol_ops mhuv3_doorbell_ops = { + .tx_startup = mhuv3_doorbell_tx_startup, + .tx_shutdown = mhuv3_doorbell_tx_shutdown, + .rx_startup = mhuv3_doorbell_rx_startup, + .rx_shutdown = mhuv3_doorbell_rx_shutdown, + .rx_complete = mhuv3_doorbell_rx_complete, + .last_tx_done = mhuv3_doorbell_last_tx_done, + .send_data = mhuv3_doorbell_send_data, +}; + +/* Sender and receiver mailbox ops */ +static bool mhuv3_sender_last_tx_done(struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + return priv->ops->last_tx_done(mhu, chan); +} + +static int mhuv3_sender_send_data(struct mbox_chan *chan, void *data) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + if (!priv->ops->last_tx_done(mhu, chan)) + return -EBUSY; + + return priv->ops->send_data(mhu, chan, data); +} + +static int mhuv3_sender_startup(struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + if (priv->ops->tx_startup) + priv->ops->tx_startup(mhu, chan); + + return 0; +} + +static void mhuv3_sender_shutdown(struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + if (priv->ops->tx_shutdown) + priv->ops->tx_shutdown(mhu, chan); +} + +static const struct mbox_chan_ops mhuv3_sender_ops = { + .send_data = mhuv3_sender_send_data, + .startup = mhuv3_sender_startup, + .shutdown = mhuv3_sender_shutdown, + .last_tx_done = mhuv3_sender_last_tx_done, +}; + +static int mhuv3_receiver_startup(struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + return priv->ops->rx_startup(mhu, chan); +} + +static void mhuv3_receiver_shutdown(struct mbox_chan *chan) +{ + struct mhuv3_mbox_chan_priv *priv = chan->con_priv; + struct mhuv3 *mhu = mhu_from_mbox(chan->mbox); + + priv->ops->rx_shutdown(mhu, chan); +} + +static int mhuv3_receiver_send_data(struct mbox_chan *chan, void *data) +{ + dev_err(chan->mbox->dev, + "Trying to transmit on a MBX MHUv3 frame\n"); + return -EIO; +} + +static bool mhuv3_receiver_last_tx_done(struct mbox_chan *chan) +{ + dev_err(chan->mbox->dev, "Trying to Tx poll on a MBX MHUv3 frame\n"); + return true; +} + +static const struct mbox_chan_ops mhuv3_receiver_ops = { + .send_data = mhuv3_receiver_send_data, + .startup = mhuv3_receiver_startup, + .shutdown = mhuv3_receiver_shutdown, + .last_tx_done = mhuv3_receiver_last_tx_done, +}; + +static struct mbox_chan *mhuv3_dbe_mbox_of_xlate(struct mhuv3 *mhu, + unsigned int channel, + unsigned int doorbell) +{ + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + struct mbox_controller *mbox = &mhu->mbox; + struct mbox_chan *chans = mbox->chans; + + if (channel >= e->num_chans || doorbell >= MHUV3_FLAG_BITS) { + dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n", + channel, doorbell); + return ERR_PTR(-ENODEV); + } + + return &chans[e->base_ch_idx + channel * MHUV3_FLAG_BITS + doorbell]; +} + +static void mhuv3_dbe_combined_irq_setup(struct mhuv3 *mhu) +{ + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + int i; + + if (mhu->frame == PBX_FRAME) { + struct pdbcw_page __iomem *dbcw = mhu->pbx->dbcw; + + for (i = 0; i < e->num_chans; i++) { + writel_relaxed_bitmask(0x1, &dbcw[i].int_clr, tfr_ack); + writel_relaxed_bitmask(0x0, &dbcw[i].int_en, tfr_ack); + writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en); + } + } else { + struct mdbcw_page __iomem *dbcw = mhu->mbx->dbcw; + + for (i = 0; i < e->num_chans; i++) { + writel_relaxed(0xFFFFFFFF, &dbcw[i].clr); + writel_relaxed(0xFFFFFFFF, &dbcw[i].msk_set); + writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en); + } + } +} + +static int mhuv3_dbe_channels_init(struct mhuv3 *mhu) +{ + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + struct mbox_controller *mbox = &mhu->mbox; + struct mbox_chan *chans; + int i; + + chans = mbox->chans + mbox->num_chans; + e->base_ch_idx = mbox->num_chans; + for (i = 0; i < e->num_chans; i++) { + struct mhuv3_mbox_chan_priv *priv; + int k; + + for (k = 0; k < MHUV3_FLAG_BITS; k++) { + priv = devm_kmalloc(mbox->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ch_idx = i; + priv->ops = &mhuv3_doorbell_ops; + priv->doorbell = k; + chans++->con_priv = priv; + mbox->num_chans++; + } + } + + spin_lock_init(&e->pending_lock); + + return 0; +} + +static bool mhuv3_dbe_doorbell_lookup(struct mhuv3 *mhu, unsigned int channel, + unsigned int *db) +{ + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + struct device *dev = mhu->mbox.dev; + u32 st; + + if (mhu->frame == PBX_FRAME) { + u32 active_dbs, fired_dbs; + + st = readl_relaxed_bitmask(&mhu->pbx->dbcw[channel].int_st, + tfr_ack); + if (!st) + goto err_spurious; + + active_dbs = readl_relaxed(&mhu->pbx->dbcw[channel].st); + scoped_guard(spinlock_irqsave, &e->pending_lock) { + fired_dbs = e->pending_db[channel] & ~active_dbs; + if (!fired_dbs) + goto err_spurious; + + *db = __ffs(fired_dbs); + e->pending_db[channel] &= ~BIT(*db); + } + fired_dbs &= ~BIT(*db); + /* Clear TFR Ack if no more doorbells pending */ + if (!fired_dbs) + writel_relaxed_bitmask(0x1, + &mhu->pbx->dbcw[channel].int_clr, + tfr_ack); + } else { + st = readl_relaxed(&mhu->mbx->dbcw[channel].st_msk); + if (!st) + goto err_spurious; + + *db = __ffs(st); + } + + return true; + +err_spurious: + dev_warn(dev, "Spurious IRQ on %s channel:%d\n", + mhuv3_str[mhu->frame], channel); + + return false; +} + +static struct mbox_chan *mhuv3_dbe_chan_from_comb_irq_get(struct mhuv3 *mhu) +{ + struct mhuv3_extension *e = mhu->ext[DBE_EXT]; + struct device *dev = mhu->mbox.dev; + int i; + + for (i = 0; i < MHUV3_DBCH_CMB_INT_ST_REG_CNT; i++) { + unsigned int channel, db; + u32 cmb_st; + + cmb_st = readl_relaxed(&mhu->ctrl->dbch_int_st[i]); + if (!cmb_st) + continue; + + channel = i * MHUV3_FLAG_BITS + __ffs(cmb_st); + if (channel >= e->num_chans) { + dev_err(dev, "Invalid %s channel:%d\n", + mhuv3_str[mhu->frame], channel); + return ERR_PTR(-EIO); + } + + if (!mhuv3_dbe_doorbell_lookup(mhu, channel, &db)) + continue; + + dev_dbg(dev, "Found %s ch[%d]/db[%d]\n", + mhuv3_str[mhu->frame], channel, db); + + return &mhu->mbox.chans[channel * MHUV3_FLAG_BITS + db]; + } + + return ERR_PTR(-EIO); +} + +static int mhuv3_dbe_init(struct mhuv3 *mhu) +{ + struct device *dev = mhu->mbox.dev; + struct mhuv3_extension *e; + + if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, dbe_spt)) + return 0; + + dev_dbg(dev, "%s: Initializing DBE Extension.\n", mhuv3_str[mhu->frame]); + + e = devm_kzalloc(dev, sizeof(*e), GFP_KERNEL); + if (!e) + return -ENOMEM; + + e->type = DBE_EXT; + /* Note that, by the spec, the number of channels is (num_dbch + 1) */ + e->num_chans = + readl_relaxed_bitmask(&mhu->ctrl->dbch_cfg0, num_dbch) + 1; + e->mbox_of_xlate = mhuv3_dbe_mbox_of_xlate; + e->combined_irq_setup = mhuv3_dbe_combined_irq_setup; + e->channels_init = mhuv3_dbe_channels_init; + e->chan_from_comb_irq_get = mhuv3_dbe_chan_from_comb_irq_get; + + mhu->num_chans += e->num_chans * MHUV3_FLAG_BITS; + mhu->ext[DBE_EXT] = e; + + dev_dbg(dev, "%s: found %d DBE channels.\n", + mhuv3_str[mhu->frame], e->num_chans); + + return 0; +} + +static int mhuv3_fce_init(struct mhuv3 *mhu) +{ + struct device *dev = mhu->mbox.dev; + + if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fce_spt)) + return 0; + + dev_dbg(dev, "%s: FCE Extension not supported by driver.\n", + mhuv3_str[mhu->frame]); + + return 0; +} + +static int mhuv3_fe_init(struct mhuv3 *mhu) +{ + struct device *dev = mhu->mbox.dev; + + if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fe_spt)) + return 0; + + dev_dbg(dev, "%s: FE Extension not supported by driver.\n", + mhuv3_str[mhu->frame]); + + return 0; +} + +static mhuv3_extension_initializer mhuv3_extension_init[NUM_EXT] = { + mhuv3_dbe_init, + mhuv3_fce_init, + mhuv3_fe_init, +}; + +static int mhuv3_initialize_channels(struct device *dev, struct mhuv3 *mhu) +{ + struct mbox_controller *mbox = &mhu->mbox; + int i, ret = 0; + + mbox->chans = devm_kcalloc(dev, mhu->num_chans, + sizeof(*mbox->chans), GFP_KERNEL); + if (!mbox->chans) + return dev_err_probe(dev, -ENOMEM, + "Failed to initialize channels\n"); + + for (i = 0; i < NUM_EXT && !ret; i++) + if (mhu->ext[i]) + ret = mhu->ext[i]->channels_init(mhu); + + return ret; +} + +static struct mbox_chan *mhuv3_mbox_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *pa) +{ + struct mhuv3 *mhu = mhu_from_mbox(mbox); + unsigned int type, channel, param; + + if (pa->args_count != MHUV3_MBOX_CELLS) + return ERR_PTR(-EINVAL); + + type = pa->args[MHUV3_MBOX_CELL_TYPE]; + if (type >= NUM_EXT) + return ERR_PTR(-EINVAL); + + channel = pa->args[MHUV3_MBOX_CELL_CHWN]; + param = pa->args[MHUV3_MBOX_CELL_PARAM]; + + return mhu->ext[type]->mbox_of_xlate(mhu, channel, param); +} + +static void mhu_frame_cleanup_actions(void *data) +{ + struct mhuv3 *mhu = data; + + writel_relaxed_bitmask(0x0, &mhu->ctrl->x_ctrl, op_req); +} + +static int mhuv3_frame_init(struct mhuv3 *mhu, void __iomem *regs) +{ + struct device *dev = mhu->mbox.dev; + int i; + + mhu->ctrl = regs; + mhu->frame = readl_relaxed_bitmask(&mhu->ctrl->blk_id, id); + if (mhu->frame > MBX_FRAME) + return dev_err_probe(dev, -EINVAL, + "Invalid Frame type- %d\n", mhu->frame); + + mhu->major = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_major_rev); + mhu->minor = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_minor_rev); + mhu->implem = readl_relaxed_bitmask(&mhu->ctrl->iidr, implementer); + mhu->rev = readl_relaxed_bitmask(&mhu->ctrl->iidr, revision); + mhu->var = readl_relaxed_bitmask(&mhu->ctrl->iidr, variant); + mhu->prod_id = readl_relaxed_bitmask(&mhu->ctrl->iidr, product_id); + if (mhu->major != MHUV3_MAJOR_VERSION) + return dev_err_probe(dev, -EINVAL, + "Unsupported MHU %s block - major:%d minor:%d\n", + mhuv3_str[mhu->frame], mhu->major, + mhu->minor); + + mhu->auto_op_full = + !!readl_relaxed_bitmask(&mhu->ctrl->feat_spt1, auto_op_spt); + /* Request the PBX/MBX to remain operational */ + if (mhu->auto_op_full) { + writel_relaxed_bitmask(0x1, &mhu->ctrl->x_ctrl, op_req); + devm_add_action_or_reset(dev, mhu_frame_cleanup_actions, mhu); + } + + dev_dbg(dev, + "Found MHU %s block - major:%d minor:%d\n implem:0x%X rev:0x%X var:0x%X prod_id:0x%X", + mhuv3_str[mhu->frame], mhu->major, mhu->minor, + mhu->implem, mhu->rev, mhu->var, mhu->prod_id); + + if (mhu->frame == PBX_FRAME) + mhu->pbx = regs; + else + mhu->mbx = regs; + + for (i = 0; i < NUM_EXT; i++) { + int ret; + + /* + * Note that extensions initialization fails only when such + * extension initialization routine fails and the extensions + * was found to be supported in hardware and in software. + */ + ret = mhuv3_extension_init[i](mhu); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize %s %s\n", + mhuv3_str[mhu->frame], + mhuv3_ext_str[i]); + } + + return 0; +} + +static irqreturn_t mhuv3_pbx_comb_interrupt(int irq, void *arg) +{ + unsigned int i, found = 0; + struct mhuv3 *mhu = arg; + struct mbox_chan *chan; + struct device *dev; + int ret = IRQ_NONE; + + dev = mhu->mbox.dev; + for (i = 0; i < NUM_EXT; i++) { + struct mhuv3_mbox_chan_priv *priv; + + /* FCE does not participate to the PBX combined */ + if (i == FCE_EXT || !mhu->ext[i]) + continue; + + chan = mhu->ext[i]->chan_from_comb_irq_get(mhu); + if (IS_ERR(chan)) + continue; + + found++; + priv = chan->con_priv; + if (!chan->cl) { + dev_warn(dev, "TX Ack on UNBOUND channel (%u)\n", + priv->ch_idx); + continue; + } + + mbox_chan_txdone(chan, 0); + ret = IRQ_HANDLED; + } + + if (found == 0) + dev_warn_once(dev, "Failed to find channel for the TX interrupt\n"); + + return ret; +} + +static irqreturn_t mhuv3_mbx_comb_interrupt(int irq, void *arg) +{ + unsigned int i, found = 0; + struct mhuv3 *mhu = arg; + struct mbox_chan *chan; + struct device *dev; + int ret = IRQ_NONE; + + dev = mhu->mbox.dev; + for (i = 0; i < NUM_EXT; i++) { + struct mhuv3_mbox_chan_priv *priv; + void *data __free(kfree) = NULL; + + if (!mhu->ext[i]) + continue; + + /* Process any extension which could be source of the IRQ */ + chan = mhu->ext[i]->chan_from_comb_irq_get(mhu); + if (IS_ERR(chan)) + continue; + + found++; + /* From here on we need to call rx_complete even on error */ + priv = chan->con_priv; + if (!chan->cl) { + dev_warn(dev, "RX Data on UNBOUND channel (%u)\n", + priv->ch_idx); + goto rx_ack; + } + + /* Read optional in-band LE data first. */ + if (priv->ops->read_data) { + data = priv->ops->read_data(mhu, chan); + if (IS_ERR(data)) { + dev_err(dev, + "Failed to read in-band data. err:%ld\n", + PTR_ERR(no_free_ptr(data))); + goto rx_ack; + } + } + + mbox_chan_received_data(chan, data); + ret = IRQ_HANDLED; + + /* + * Acknowledge transfer after any possible optional + * out-of-band data has also been retrieved via + * mbox_chan_received_data(). + */ +rx_ack: + if (priv->ops->rx_complete) + priv->ops->rx_complete(mhu, chan); + } + + if (found == 0) + dev_warn_once(dev, "Failed to find channel for the RX interrupt\n"); + + return ret; +} + +static int mhuv3_setup_pbx(struct mhuv3 *mhu) +{ + struct device *dev = mhu->mbox.dev; + + mhu->mbox.ops = &mhuv3_sender_ops; + + if (mhu->cmb_irq > 0) { + int ret, i; + + ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL, + mhuv3_pbx_comb_interrupt, + IRQF_ONESHOT, "mhuv3-pbx", mhu); + if (ret) + return dev_err_probe(dev, ret, + "Failed to request PBX IRQ\n"); + + mhu->mbox.txdone_irq = true; + mhu->mbox.txdone_poll = false; + + for (i = 0; i < NUM_EXT; i++) + if (mhu->ext[i]) + mhu->ext[i]->combined_irq_setup(mhu); + + dev_dbg(dev, "MHUv3 PBX IRQs initialized.\n"); + + return 0; + } + + dev_info(dev, "Using PBX in Tx polling mode.\n"); + mhu->mbox.txdone_irq = false; + mhu->mbox.txdone_poll = true; + mhu->mbox.txpoll_period = 1; + + return 0; +} + +static int mhuv3_setup_mbx(struct mhuv3 *mhu) +{ + struct device *dev = mhu->mbox.dev; + int ret, i; + + mhu->mbox.ops = &mhuv3_receiver_ops; + + if (mhu->cmb_irq <= 0) + return dev_err_probe(dev, -EINVAL, + "MBX combined IRQ is missing !\n"); + + ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL, + mhuv3_mbx_comb_interrupt, IRQF_ONESHOT, + "mhuv3-mbx", mhu); + if (ret) + return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n"); + + for (i = 0; i < NUM_EXT; i++) + if (mhu->ext[i]) + mhu->ext[i]->combined_irq_setup(mhu); + + dev_dbg(dev, "MHUv3 MBX IRQs initialized.\n"); + + return ret; +} + +static int mhuv3_irqs_init(struct mhuv3 *mhu, struct platform_device *pdev) +{ + dev_dbg(mhu->mbox.dev, "Initializing %s block.\n", + mhuv3_str[mhu->frame]); + + if (mhu->frame == PBX_FRAME) { + mhu->cmb_irq = + platform_get_irq_byname_optional(pdev, "combined"); + return mhuv3_setup_pbx(mhu); + } + + mhu->cmb_irq = platform_get_irq_byname(pdev, "combined"); + return mhuv3_setup_mbx(mhu); +} + +static int mhuv3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *regs; + struct mhuv3 *mhu; + int ret; + + mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); + if (!mhu) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mhu->mbox.dev = dev; + ret = mhuv3_frame_init(mhu, regs); + if (ret) + return ret; + + ret = mhuv3_irqs_init(mhu, pdev); + if (ret) + return ret; + + mhu->mbox.of_xlate = mhuv3_mbox_of_xlate; + ret = mhuv3_initialize_channels(dev, mhu); + if (ret) + return ret; + + ret = devm_mbox_controller_register(dev, &mhu->mbox); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register ARM MHUv3 driver\n"); + + return ret; +} + +static const struct of_device_id mhuv3_of_match[] = { + { .compatible = "arm,mhuv3", .data = NULL }, + {} +}; +MODULE_DEVICE_TABLE(of, mhuv3_of_match); + +static struct platform_driver mhuv3_driver = { + .driver = { + .name = "arm-mhuv3-mailbox", + .of_match_table = mhuv3_of_match, + }, + .probe = mhuv3_probe, +}; +module_platform_driver(mhuv3_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARM MHUv3 Driver"); +MODULE_AUTHOR("Cristian Marussi <[email protected]>"); diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index 1768d3d5aaa0..242e7504a628 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -43,6 +43,7 @@ #include <linux/dma-direction.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> +#include <linux/workqueue.h> #define PDC_SUCCESS 0 @@ -293,8 +294,8 @@ struct pdc_state { unsigned int pdc_irq; - /* tasklet for deferred processing after DMA rx interrupt */ - struct tasklet_struct rx_tasklet; + /* work for deferred processing after DMA rx interrupt */ + struct work_struct rx_work; /* Number of bytes of receive status prior to each rx frame */ u32 rx_status_len; @@ -952,18 +953,18 @@ static irqreturn_t pdc_irq_handler(int irq, void *data) iowrite32(intstatus, pdcs->pdc_reg_vbase + PDC_INTSTATUS_OFFSET); /* Wakeup IRQ thread */ - tasklet_schedule(&pdcs->rx_tasklet); + queue_work(system_bh_wq, &pdcs->rx_work); return IRQ_HANDLED; } /** - * pdc_tasklet_cb() - Tasklet callback that runs the deferred processing after + * pdc_work_cb() - Work callback that runs the deferred processing after * a DMA receive interrupt. Reenables the receive interrupt. * @t: Pointer to the Altera sSGDMA channel structure */ -static void pdc_tasklet_cb(struct tasklet_struct *t) +static void pdc_work_cb(struct work_struct *t) { - struct pdc_state *pdcs = from_tasklet(pdcs, t, rx_tasklet); + struct pdc_state *pdcs = from_work(pdcs, t, rx_work); pdc_receive(pdcs); @@ -1577,8 +1578,8 @@ static int pdc_probe(struct platform_device *pdev) pdc_hw_init(pdcs); - /* Init tasklet for deferred DMA rx processing */ - tasklet_setup(&pdcs->rx_tasklet, pdc_tasklet_cb); + /* Init work for deferred DMA rx processing */ + INIT_WORK(&pdcs->rx_work, pdc_work_cb); err = pdc_interrupts_init(pdcs); if (err) @@ -1595,7 +1596,7 @@ static int pdc_probe(struct platform_device *pdev) return PDC_SUCCESS; cleanup_buf_pool: - tasklet_kill(&pdcs->rx_tasklet); + cancel_work_sync(&pdcs->rx_work); dma_pool_destroy(pdcs->rx_buf_pool); cleanup_ring_pool: @@ -1611,7 +1612,7 @@ static void pdc_remove(struct platform_device *pdev) pdc_free_debugfs(); - tasklet_kill(&pdcs->rx_tasklet); + cancel_work_sync(&pdcs->rx_work); pdc_hw_disable(pdcs); diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 5c1d09cad761..933727f89431 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -21,6 +21,7 @@ #include <linux/pm_runtime.h> #include <linux/suspend.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include "mailbox.h" @@ -80,7 +81,7 @@ struct imx_mu_con_priv { char irq_desc[IMX_MU_CHAN_NAME_SIZE]; enum imx_mu_chan_type type; struct mbox_chan *chan; - struct tasklet_struct txdb_tasklet; + struct work_struct txdb_work; }; struct imx_mu_priv { @@ -232,7 +233,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, break; case IMX_MU_TYPE_TXDB: imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); - tasklet_schedule(&cp->txdb_tasklet); + queue_work(system_bh_wq, &cp->txdb_work); break; case IMX_MU_TYPE_TXDB_V2: imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); @@ -420,7 +421,7 @@ static int imx_mu_seco_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, } /* Simulate hack for mbox framework */ - tasklet_schedule(&cp->txdb_tasklet); + queue_work(system_bh_wq, &cp->txdb_work); break; default: @@ -484,9 +485,9 @@ exit: return err; } -static void imx_mu_txdb_tasklet(unsigned long data) +static void imx_mu_txdb_work(struct work_struct *t) { - struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data; + struct imx_mu_con_priv *cp = from_work(cp, t, txdb_work); mbox_chan_txdone(cp->chan, 0); } @@ -570,8 +571,7 @@ static int imx_mu_startup(struct mbox_chan *chan) if (cp->type == IMX_MU_TYPE_TXDB) { /* Tx doorbell don't have ACK support */ - tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, - (unsigned long)cp); + INIT_WORK(&cp->txdb_work, imx_mu_txdb_work); return 0; } @@ -615,7 +615,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan) } if (cp->type == IMX_MU_TYPE_TXDB) { - tasklet_kill(&cp->txdb_tasklet); + cancel_work_sync(&cp->txdb_work); pm_runtime_put_sync(priv->dev); return; } diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index ead2200f39ba..4aa394e91109 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -465,7 +465,7 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan) struct cmdq_task *task, *tmp; unsigned long flags; - WARN_ON(pm_runtime_get_sync(cmdq->mbox.dev)); + WARN_ON(pm_runtime_get_sync(cmdq->mbox.dev) < 0); spin_lock_irqsave(&thread->chan->lock, flags); if (list_empty(&thread->task_busy_list)) @@ -765,6 +765,7 @@ static const struct of_device_id cmdq_of_ids[] = { {.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195}, {} }; +MODULE_DEVICE_TABLE(of, cmdq_of_ids); static struct platform_driver cmdq_drv = { .probe = cmdq_probe, diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index c961706fe61d..46747559b438 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -19,7 +19,6 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/omap-mailbox.h> #include <linux/mailbox_controller.h> #include <linux/mailbox_client.h> @@ -51,6 +50,11 @@ #define MBOX_INTR_CFG_TYPE1 0 #define MBOX_INTR_CFG_TYPE2 1 +typedef enum { + IRQ_TX = 1, + IRQ_RX = 2, +} omap_mbox_irq_t; + struct omap_mbox_fifo { unsigned long msg; unsigned long fifo_stat; @@ -61,14 +65,6 @@ struct omap_mbox_fifo { u32 intr_bit; }; -struct omap_mbox_queue { - spinlock_t lock; - struct kfifo fifo; - struct work_struct work; - struct omap_mbox *mbox; - bool full; -}; - struct omap_mbox_match_data { u32 intr_type; }; @@ -81,29 +77,11 @@ struct omap_mbox_device { u32 num_users; u32 num_fifos; u32 intr_type; - struct omap_mbox **mboxes; - struct mbox_controller controller; - struct list_head elem; -}; - -struct omap_mbox_fifo_info { - int tx_id; - int tx_usr; - int tx_irq; - - int rx_id; - int rx_usr; - int rx_irq; - - const char *name; - bool send_no_irq; }; struct omap_mbox { const char *name; int irq; - struct omap_mbox_queue *rxq; - struct device *dev; struct omap_mbox_device *parent; struct omap_mbox_fifo tx_fifo; struct omap_mbox_fifo rx_fifo; @@ -112,22 +90,6 @@ struct omap_mbox { bool send_no_irq; }; -/* global variables for the mailbox devices */ -static DEFINE_MUTEX(omap_mbox_devices_lock); -static LIST_HEAD(omap_mbox_devices); - -static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE; -module_param(mbox_kfifo_size, uint, S_IRUGO); -MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)"); - -static struct omap_mbox *mbox_chan_to_omap_mbox(struct mbox_chan *chan) -{ - if (!chan || !chan->con_priv) - return NULL; - - return (struct omap_mbox *)chan->con_priv; -} - static inline unsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs) { @@ -197,7 +159,7 @@ static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) return (int)(enable & status & bit); } -static void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +static void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { u32 l; struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? @@ -210,7 +172,7 @@ static void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) mbox_write_reg(mbox->parent, l, irqenable); } -static void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +static void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? &mbox->tx_fifo : &mbox->rx_fifo; @@ -227,87 +189,27 @@ static void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) mbox_write_reg(mbox->parent, bit, irqdisable); } -void omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq) -{ - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); - - if (WARN_ON(!mbox)) - return; - - _omap_mbox_enable_irq(mbox, irq); -} -EXPORT_SYMBOL(omap_mbox_enable_irq); - -void omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq) -{ - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); - - if (WARN_ON(!mbox)) - return; - - _omap_mbox_disable_irq(mbox, irq); -} -EXPORT_SYMBOL(omap_mbox_disable_irq); - -/* - * Message receiver(workqueue) - */ -static void mbox_rx_work(struct work_struct *work) -{ - struct omap_mbox_queue *mq = - container_of(work, struct omap_mbox_queue, work); - mbox_msg_t data; - u32 msg; - int len; - - while (kfifo_len(&mq->fifo) >= sizeof(msg)) { - len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); - WARN_ON(len != sizeof(msg)); - data = msg; - - mbox_chan_received_data(mq->mbox->chan, (void *)data); - spin_lock_irq(&mq->lock); - if (mq->full) { - mq->full = false; - _omap_mbox_enable_irq(mq->mbox, IRQ_RX); - } - spin_unlock_irq(&mq->lock); - } -} - /* * Mailbox interrupt handler */ static void __mbox_tx_interrupt(struct omap_mbox *mbox) { - _omap_mbox_disable_irq(mbox, IRQ_TX); + omap_mbox_disable_irq(mbox, IRQ_TX); ack_mbox_irq(mbox, IRQ_TX); mbox_chan_txdone(mbox->chan, 0); } static void __mbox_rx_interrupt(struct omap_mbox *mbox) { - struct omap_mbox_queue *mq = mbox->rxq; u32 msg; - int len; while (!mbox_fifo_empty(mbox)) { - if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { - _omap_mbox_disable_irq(mbox, IRQ_RX); - mq->full = true; - goto nomem; - } - msg = mbox_fifo_read(mbox); - - len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); - WARN_ON(len != sizeof(msg)); + mbox_chan_received_data(mbox->chan, (void *)(uintptr_t)msg); } - /* no more messages in the fifo. clear IRQ source. */ + /* clear IRQ source. */ ack_mbox_irq(mbox, IRQ_RX); -nomem: - schedule_work(&mbox->rxq->work); } static irqreturn_t mbox_interrupt(int irq, void *p) @@ -323,188 +225,34 @@ static irqreturn_t mbox_interrupt(int irq, void *p) return IRQ_HANDLED; } -static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, - void (*work)(struct work_struct *)) -{ - struct omap_mbox_queue *mq; - - if (!work) - return NULL; - - mq = kzalloc(sizeof(*mq), GFP_KERNEL); - if (!mq) - return NULL; - - spin_lock_init(&mq->lock); - - if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) - goto error; - - INIT_WORK(&mq->work, work); - return mq; - -error: - kfree(mq); - return NULL; -} - -static void mbox_queue_free(struct omap_mbox_queue *q) -{ - kfifo_free(&q->fifo); - kfree(q); -} - static int omap_mbox_startup(struct omap_mbox *mbox) { int ret = 0; - struct omap_mbox_queue *mq; - mq = mbox_queue_alloc(mbox, mbox_rx_work); - if (!mq) - return -ENOMEM; - mbox->rxq = mq; - mq->mbox = mbox; - - ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, - mbox->name, mbox); + ret = request_threaded_irq(mbox->irq, NULL, mbox_interrupt, + IRQF_ONESHOT, mbox->name, mbox); if (unlikely(ret)) { pr_err("failed to register mailbox interrupt:%d\n", ret); - goto fail_request_irq; + return ret; } if (mbox->send_no_irq) mbox->chan->txdone_method = TXDONE_BY_ACK; - _omap_mbox_enable_irq(mbox, IRQ_RX); + omap_mbox_enable_irq(mbox, IRQ_RX); return 0; - -fail_request_irq: - mbox_queue_free(mbox->rxq); - return ret; } static void omap_mbox_fini(struct omap_mbox *mbox) { - _omap_mbox_disable_irq(mbox, IRQ_RX); + omap_mbox_disable_irq(mbox, IRQ_RX); free_irq(mbox->irq, mbox); - flush_work(&mbox->rxq->work); - mbox_queue_free(mbox->rxq); -} - -static struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev, - const char *mbox_name) -{ - struct omap_mbox *_mbox, *mbox = NULL; - struct omap_mbox **mboxes = mdev->mboxes; - int i; - - if (!mboxes) - return NULL; - - for (i = 0; (_mbox = mboxes[i]); i++) { - if (!strcmp(_mbox->name, mbox_name)) { - mbox = _mbox; - break; - } - } - return mbox; -} - -struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl, - const char *chan_name) -{ - struct device *dev = cl->dev; - struct omap_mbox *mbox = NULL; - struct omap_mbox_device *mdev; - int ret; - - if (!dev) - return ERR_PTR(-ENODEV); - - if (dev->of_node) { - pr_err("%s: please use mbox_request_channel(), this API is supported only for OMAP non-DT usage\n", - __func__); - return ERR_PTR(-ENODEV); - } - - mutex_lock(&omap_mbox_devices_lock); - list_for_each_entry(mdev, &omap_mbox_devices, elem) { - mbox = omap_mbox_device_find(mdev, chan_name); - if (mbox) - break; - } - mutex_unlock(&omap_mbox_devices_lock); - - if (!mbox || !mbox->chan) - return ERR_PTR(-ENOENT); - - ret = mbox_bind_client(mbox->chan, cl); - if (ret) - return ERR_PTR(ret); - - return mbox->chan; -} -EXPORT_SYMBOL(omap_mbox_request_channel); - -static struct class omap_mbox_class = { .name = "mbox", }; - -static int omap_mbox_register(struct omap_mbox_device *mdev) -{ - int ret; - int i; - struct omap_mbox **mboxes; - - if (!mdev || !mdev->mboxes) - return -EINVAL; - - mboxes = mdev->mboxes; - for (i = 0; mboxes[i]; i++) { - struct omap_mbox *mbox = mboxes[i]; - - mbox->dev = device_create(&omap_mbox_class, mdev->dev, - 0, mbox, "%s", mbox->name); - if (IS_ERR(mbox->dev)) { - ret = PTR_ERR(mbox->dev); - goto err_out; - } - } - - mutex_lock(&omap_mbox_devices_lock); - list_add(&mdev->elem, &omap_mbox_devices); - mutex_unlock(&omap_mbox_devices_lock); - - ret = devm_mbox_controller_register(mdev->dev, &mdev->controller); - -err_out: - if (ret) { - while (i--) - device_unregister(mboxes[i]->dev); - } - return ret; -} - -static int omap_mbox_unregister(struct omap_mbox_device *mdev) -{ - int i; - struct omap_mbox **mboxes; - - if (!mdev || !mdev->mboxes) - return -EINVAL; - - mutex_lock(&omap_mbox_devices_lock); - list_del(&mdev->elem); - mutex_unlock(&omap_mbox_devices_lock); - - mboxes = mdev->mboxes; - for (i = 0; mboxes[i]; i++) - device_unregister(mboxes[i]->dev); - return 0; } static int omap_mbox_chan_startup(struct mbox_chan *chan) { - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); + struct omap_mbox *mbox = chan->con_priv; struct omap_mbox_device *mdev = mbox->parent; int ret = 0; @@ -519,7 +267,7 @@ static int omap_mbox_chan_startup(struct mbox_chan *chan) static void omap_mbox_chan_shutdown(struct mbox_chan *chan) { - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); + struct omap_mbox *mbox = chan->con_priv; struct omap_mbox_device *mdev = mbox->parent; mutex_lock(&mdev->cfg_lock); @@ -530,41 +278,40 @@ static void omap_mbox_chan_shutdown(struct mbox_chan *chan) static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg) { - int ret = -EBUSY; + if (mbox_fifo_full(mbox)) + return -EBUSY; - if (!mbox_fifo_full(mbox)) { - _omap_mbox_enable_irq(mbox, IRQ_RX); - mbox_fifo_write(mbox, msg); - ret = 0; - _omap_mbox_disable_irq(mbox, IRQ_RX); + omap_mbox_enable_irq(mbox, IRQ_RX); + mbox_fifo_write(mbox, msg); + omap_mbox_disable_irq(mbox, IRQ_RX); - /* we must read and ack the interrupt directly from here */ - mbox_fifo_read(mbox); - ack_mbox_irq(mbox, IRQ_RX); - } + /* we must read and ack the interrupt directly from here */ + mbox_fifo_read(mbox); + ack_mbox_irq(mbox, IRQ_RX); - return ret; + return 0; } static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg) { - int ret = -EBUSY; - - if (!mbox_fifo_full(mbox)) { - mbox_fifo_write(mbox, msg); - ret = 0; + if (mbox_fifo_full(mbox)) { + /* always enable the interrupt */ + omap_mbox_enable_irq(mbox, IRQ_TX); + return -EBUSY; } + mbox_fifo_write(mbox, msg); + /* always enable the interrupt */ - _omap_mbox_enable_irq(mbox, IRQ_TX); - return ret; + omap_mbox_enable_irq(mbox, IRQ_TX); + return 0; } static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) { - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); + struct omap_mbox *mbox = chan->con_priv; int ret; - u32 msg = omap_mbox_message(data); + u32 msg = (u32)(uintptr_t)(data); if (!mbox) return -EINVAL; @@ -666,8 +413,9 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, struct device_node *node; struct omap_mbox_device *mdev; struct omap_mbox *mbox; + int i; - mdev = container_of(controller, struct omap_mbox_device, controller); + mdev = dev_get_drvdata(controller->dev); if (WARN_ON(!mdev)) return ERR_PTR(-EINVAL); @@ -678,22 +426,29 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, return ERR_PTR(-ENODEV); } - mbox = omap_mbox_device_find(mdev, node->name); + for (i = 0; i < controller->num_chans; i++) { + mbox = controller->chans[i].con_priv; + if (!strcmp(mbox->name, node->name)) { + of_node_put(node); + return &controller->chans[i]; + } + } + of_node_put(node); - return mbox ? mbox->chan : ERR_PTR(-ENOENT); + return ERR_PTR(-ENOENT); } static int omap_mbox_probe(struct platform_device *pdev) { int ret; struct mbox_chan *chnls; - struct omap_mbox **list, *mbox, *mboxblk; - struct omap_mbox_fifo_info *finfo, *finfoblk; + struct omap_mbox *mbox; struct omap_mbox_device *mdev; struct omap_mbox_fifo *fifo; struct device_node *node = pdev->dev.of_node; struct device_node *child; const struct omap_mbox_match_data *match_data; + struct mbox_controller *controller; u32 intr_type, info_count; u32 num_users, num_fifos; u32 tmp[3]; @@ -722,40 +477,6 @@ static int omap_mbox_probe(struct platform_device *pdev) return -ENODEV; } - finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk), - GFP_KERNEL); - if (!finfoblk) - return -ENOMEM; - - finfo = finfoblk; - child = NULL; - for (i = 0; i < info_count; i++, finfo++) { - child = of_get_next_available_child(node, child); - ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp, - ARRAY_SIZE(tmp)); - if (ret) - return ret; - finfo->tx_id = tmp[0]; - finfo->tx_irq = tmp[1]; - finfo->tx_usr = tmp[2]; - - ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp, - ARRAY_SIZE(tmp)); - if (ret) - return ret; - finfo->rx_id = tmp[0]; - finfo->rx_irq = tmp[1]; - finfo->rx_usr = tmp[2]; - - finfo->name = child->name; - - finfo->send_no_irq = of_property_read_bool(child, "ti,mbox-send-noirq"); - - if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos || - finfo->tx_usr >= num_users || finfo->rx_usr >= num_users) - return -EINVAL; - } - mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; @@ -769,52 +490,67 @@ static int omap_mbox_probe(struct platform_device *pdev) if (!mdev->irq_ctx) return -ENOMEM; - /* allocate one extra for marking end of list */ - list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list), - GFP_KERNEL); - if (!list) - return -ENOMEM; - chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls), GFP_KERNEL); if (!chnls) return -ENOMEM; - mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox), - GFP_KERNEL); - if (!mboxblk) - return -ENOMEM; + child = NULL; + for (i = 0; i < info_count; i++) { + int tx_id, tx_irq, tx_usr; + int rx_id, rx_usr; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + child = of_get_next_available_child(node, child); + ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + tx_id = tmp[0]; + tx_irq = tmp[1]; + tx_usr = tmp[2]; + + ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + rx_id = tmp[0]; + /* rx_irq = tmp[1]; */ + rx_usr = tmp[2]; + + if (tx_id >= num_fifos || rx_id >= num_fifos || + tx_usr >= num_users || rx_usr >= num_users) + return -EINVAL; - mbox = mboxblk; - finfo = finfoblk; - for (i = 0; i < info_count; i++, finfo++) { fifo = &mbox->tx_fifo; - fifo->msg = MAILBOX_MESSAGE(finfo->tx_id); - fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id); - fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id); - fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr); - fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr); - fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr); + fifo->msg = MAILBOX_MESSAGE(tx_id); + fifo->fifo_stat = MAILBOX_FIFOSTATUS(tx_id); + fifo->intr_bit = MAILBOX_IRQ_NOTFULL(tx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, tx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, tx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, tx_usr); fifo = &mbox->rx_fifo; - fifo->msg = MAILBOX_MESSAGE(finfo->rx_id); - fifo->msg_stat = MAILBOX_MSGSTATUS(finfo->rx_id); - fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id); - fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr); - fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr); - fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr); - - mbox->send_no_irq = finfo->send_no_irq; + fifo->msg = MAILBOX_MESSAGE(rx_id); + fifo->msg_stat = MAILBOX_MSGSTATUS(rx_id); + fifo->intr_bit = MAILBOX_IRQ_NEWMSG(rx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, rx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, rx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, rx_usr); + + mbox->send_no_irq = of_property_read_bool(child, "ti,mbox-send-noirq"); mbox->intr_type = intr_type; mbox->parent = mdev; - mbox->name = finfo->name; - mbox->irq = platform_get_irq(pdev, finfo->tx_irq); + mbox->name = child->name; + mbox->irq = platform_get_irq(pdev, tx_irq); if (mbox->irq < 0) return mbox->irq; mbox->chan = &chnls[i]; chnls[i].con_priv = mbox; - list[i] = mbox++; } mutex_init(&mdev->cfg_lock); @@ -822,28 +558,30 @@ static int omap_mbox_probe(struct platform_device *pdev) mdev->num_users = num_users; mdev->num_fifos = num_fifos; mdev->intr_type = intr_type; - mdev->mboxes = list; + controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); + if (!controller) + return -ENOMEM; /* * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready * IRQ and is needed to run the Tx state machine */ - mdev->controller.txdone_irq = true; - mdev->controller.dev = mdev->dev; - mdev->controller.ops = &omap_mbox_chan_ops; - mdev->controller.chans = chnls; - mdev->controller.num_chans = info_count; - mdev->controller.of_xlate = omap_mbox_of_xlate; - ret = omap_mbox_register(mdev); + controller->txdone_irq = true; + controller->dev = mdev->dev; + controller->ops = &omap_mbox_chan_ops; + controller->chans = chnls; + controller->num_chans = info_count; + controller->of_xlate = omap_mbox_of_xlate; + ret = devm_mbox_controller_register(mdev->dev, controller); if (ret) return ret; platform_set_drvdata(pdev, mdev); - pm_runtime_enable(mdev->dev); + devm_pm_runtime_enable(mdev->dev); ret = pm_runtime_resume_and_get(mdev->dev); if (ret < 0) - goto unregister; + return ret; /* * just print the raw revision register, the format is not @@ -854,61 +592,20 @@ static int omap_mbox_probe(struct platform_device *pdev) ret = pm_runtime_put_sync(mdev->dev); if (ret < 0 && ret != -ENOSYS) - goto unregister; + return ret; - devm_kfree(&pdev->dev, finfoblk); return 0; - -unregister: - pm_runtime_disable(mdev->dev); - omap_mbox_unregister(mdev); - return ret; -} - -static void omap_mbox_remove(struct platform_device *pdev) -{ - struct omap_mbox_device *mdev = platform_get_drvdata(pdev); - - pm_runtime_disable(mdev->dev); - omap_mbox_unregister(mdev); } static struct platform_driver omap_mbox_driver = { .probe = omap_mbox_probe, - .remove_new = omap_mbox_remove, .driver = { .name = "omap-mailbox", .pm = &omap_mbox_pm_ops, .of_match_table = of_match_ptr(omap_mailbox_of_match), }, }; - -static int __init omap_mbox_init(void) -{ - int err; - - err = class_register(&omap_mbox_class); - if (err) - return err; - - /* kfifo size sanity check: alignment and minimal size */ - mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32)); - mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32)); - - err = platform_driver_register(&omap_mbox_driver); - if (err) - class_unregister(&omap_mbox_class); - - return err; -} -subsys_initcall(omap_mbox_init); - -static void __exit omap_mbox_exit(void) -{ - platform_driver_unregister(&omap_mbox_driver); - class_unregister(&omap_mbox_class); -} -module_exit(omap_mbox_exit); +module_platform_driver(omap_mbox_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index 25c65afc030a..7c90bac3de21 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -6,9 +6,11 @@ */ #include <linux/arm-smccc.h> +#include <linux/cpuhotplug.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/mailbox_controller.h> @@ -16,6 +18,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> /* IPI agent ID any */ @@ -52,6 +55,15 @@ #define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */ #define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */ +/* IPI Message Buffer Information */ +#define RESP_OFFSET 0x20U +#define DEST_OFFSET 0x40U +#define IPI_BUF_SIZE 0x20U +#define DST_BIT_POS 9U +#define SRC_BITMASK GENMASK(11, 8) + +#define MAX_SGI 16 + /** * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel * @is_opened: indicate if the IPI channel is opened @@ -72,6 +84,10 @@ struct zynqmp_ipi_mchan { unsigned int chan_type; }; +struct zynqmp_ipi_mbox; + +typedef int (*setup_ipi_fn)(struct zynqmp_ipi_mbox *ipi_mbox, struct device_node *node); + /** * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox * platform data. @@ -81,6 +97,7 @@ struct zynqmp_ipi_mchan { * @remote_id: remote IPI agent ID * @mbox: mailbox Controller * @mchans: array for channels, tx channel and rx channel. + * @setup_ipi_fn: Function Pointer to set up IPI Channels */ struct zynqmp_ipi_mbox { struct zynqmp_ipi_pdata *pdata; @@ -88,6 +105,7 @@ struct zynqmp_ipi_mbox { u32 remote_id; struct mbox_controller mbox; struct zynqmp_ipi_mchan mchans[2]; + setup_ipi_fn setup_ipi_fn; }; /** @@ -98,6 +116,7 @@ struct zynqmp_ipi_mbox { * @irq: IPI agent interrupt ID * @method: IPI SMC or HVC is going to be used * @local_id: local IPI agent ID + * @virq_sgi: IRQ number mapped to SGI * @num_mboxes: number of mailboxes of this IPI agent * @ipi_mboxes: IPI mailboxes of this IPI agent */ @@ -106,10 +125,13 @@ struct zynqmp_ipi_pdata { int irq; unsigned int method; u32 local_id; + int virq_sgi; int num_mboxes; struct zynqmp_ipi_mbox ipi_mboxes[] __counted_by(num_mboxes); }; +static DEFINE_PER_CPU(struct zynqmp_ipi_pdata *, per_cpu_pdata); + static struct device_driver zynqmp_ipi_mbox_driver = { .owner = THIS_MODULE, .name = "zynqmp-ipi-mbox", @@ -163,9 +185,11 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { if (mchan->is_opened) { msg = mchan->rx_buf; - msg->len = mchan->req_buf_size; - memcpy_fromio(msg->data, mchan->req_buf, - msg->len); + if (msg) { + msg->len = mchan->req_buf_size; + memcpy_fromio(msg->data, mchan->req_buf, + msg->len); + } mbox_chan_received_data(chan, (void *)msg); status = IRQ_HANDLED; } @@ -174,6 +198,14 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) return status; } +static irqreturn_t zynqmp_sgi_interrupt(int irq, void *data) +{ + struct zynqmp_ipi_pdata **pdata_ptr = data; + struct zynqmp_ipi_pdata *pdata = *pdata_ptr; + + return zynqmp_ipi_interrupt(irq, pdata); +} + /** * zynqmp_ipi_peek_data - Peek to see if there are any rx messages. * @@ -275,26 +307,26 @@ static int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data) if (mchan->chan_type == IPI_MB_CHNL_TX) { /* Send request message */ - if (msg && msg->len > mchan->req_buf_size) { + if (msg && msg->len > mchan->req_buf_size && mchan->req_buf) { dev_err(dev, "channel %d message length %u > max %lu\n", mchan->chan_type, (unsigned int)msg->len, mchan->req_buf_size); return -EINVAL; } - if (msg && msg->len) + if (msg && msg->len && mchan->req_buf) memcpy_toio(mchan->req_buf, msg->data, msg->len); /* Kick IPI mailbox to send message */ arg0 = SMC_IPI_MAILBOX_NOTIFY; zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); } else { /* Send response message */ - if (msg && msg->len > mchan->resp_buf_size) { + if (msg && msg->len > mchan->resp_buf_size && mchan->resp_buf) { dev_err(dev, "channel %d message length %u > max %lu\n", mchan->chan_type, (unsigned int)msg->len, mchan->resp_buf_size); return -EINVAL; } - if (msg && msg->len) + if (msg && msg->len && mchan->resp_buf) memcpy_toio(mchan->resp_buf, msg->data, msg->len); arg0 = SMC_IPI_MAILBOX_ACK; zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK, @@ -415,12 +447,6 @@ static struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox, return chan; } -static const struct of_device_id zynqmp_ipi_of_match[] = { - { .compatible = "xlnx,zynqmp-ipi-mailbox" }, - {}, -}; -MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); - /** * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node * @@ -470,12 +496,9 @@ static void zynqmp_ipi_mbox_dev_release(struct device *dev) static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, struct device_node *node) { - struct zynqmp_ipi_mchan *mchan; struct mbox_chan *chans; struct mbox_controller *mbox; - struct resource res; struct device *dev, *mdev; - const char *name; int ret; dev = ipi_mbox->pdata->dev; @@ -495,6 +518,73 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, } mdev = &ipi_mbox->dev; + /* Get the IPI remote agent ID */ + ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); + if (ret < 0) { + dev_err(dev, "No IPI remote ID is specified.\n"); + return ret; + } + + ret = ipi_mbox->setup_ipi_fn(ipi_mbox, node); + if (ret) { + dev_err(dev, "Failed to set up IPI Buffers.\n"); + return ret; + } + + mbox = &ipi_mbox->mbox; + mbox->dev = mdev; + mbox->ops = &zynqmp_ipi_chan_ops; + mbox->num_chans = 2; + mbox->txdone_irq = false; + mbox->txdone_poll = true; + mbox->txpoll_period = 5; + mbox->of_xlate = zynqmp_ipi_of_xlate; + chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); + if (!chans) + return -ENOMEM; + mbox->chans = chans; + chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; + chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; + ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX; + ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX; + ret = devm_mbox_controller_register(mdev, mbox); + if (ret) + dev_err(mdev, + "Failed to register mbox_controller(%d)\n", ret); + else + dev_info(mdev, + "Registered ZynqMP IPI mbox with TX/RX channels.\n"); + return ret; +} + +/** + * zynqmp_ipi_setup - set up IPI Buffers for classic flow + * + * @ipi_mbox: pointer to IPI mailbox private data structure + * @node: IPI mailbox device node + * + * This will be used to set up IPI Buffers for ZynqMP SOC if user + * wishes to use classic driver usage model on new SOC's with only + * buffered IPIs. + * + * Note that bufferless IPIs and mixed usage of buffered and bufferless + * IPIs are not supported with this flow. + * + * This will be invoked with compatible string "xlnx,zynqmp-ipi-mailbox". + * + * Return: 0 for success, negative value for failure + */ +static int zynqmp_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox, + struct device_node *node) +{ + struct zynqmp_ipi_mchan *mchan; + struct device *mdev; + struct resource res; + const char *name; + int ret; + + mdev = &ipi_mbox->dev; + mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; name = "local_request_region"; ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); @@ -569,39 +659,218 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, if (!mchan->rx_buf) return -ENOMEM; - /* Get the IPI remote agent ID */ - ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); - if (ret < 0) { - dev_err(dev, "No IPI remote ID is specified.\n"); + return 0; +} + +/** + * versal_ipi_setup - Set up IPIs to support mixed usage of + * Buffered and Bufferless IPIs. + * + * @ipi_mbox: pointer to IPI mailbox private data structure + * @node: IPI mailbox device node + * + * Return: 0 for success, negative value for failure + */ +static int versal_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox, + struct device_node *node) +{ + struct zynqmp_ipi_mchan *tx_mchan, *rx_mchan; + struct resource host_res, remote_res; + struct device_node *parent_node; + int host_idx, remote_idx; + struct device *mdev; + + tx_mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; + rx_mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; + parent_node = of_get_parent(node); + mdev = &ipi_mbox->dev; + + host_idx = zynqmp_ipi_mbox_get_buf_res(parent_node, "msg", &host_res); + remote_idx = zynqmp_ipi_mbox_get_buf_res(node, "msg", &remote_res); + + /* + * Only set up buffers if both sides claim to have msg buffers. + * This is because each buffered IPI's corresponding msg buffers + * are reserved for use by other buffered IPI's. + */ + if (!host_idx && !remote_idx) { + u32 host_src, host_dst, remote_src, remote_dst; + u32 buff_sz; + + buff_sz = resource_size(&host_res); + + host_src = host_res.start & SRC_BITMASK; + remote_src = remote_res.start & SRC_BITMASK; + + host_dst = (host_src >> DST_BIT_POS) * DEST_OFFSET; + remote_dst = (remote_src >> DST_BIT_POS) * DEST_OFFSET; + + /* Validate that IPI IDs is within IPI Message buffer space. */ + if (host_dst >= buff_sz || remote_dst >= buff_sz) { + dev_err(mdev, + "Invalid IPI Message buffer values: %x %x\n", + host_dst, remote_dst); + return -EINVAL; + } + + tx_mchan->req_buf = devm_ioremap(mdev, + host_res.start | remote_dst, + IPI_BUF_SIZE); + if (!tx_mchan->req_buf) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + return -ENOMEM; + } + + tx_mchan->resp_buf = devm_ioremap(mdev, + (remote_res.start | host_dst) + + RESP_OFFSET, IPI_BUF_SIZE); + if (!tx_mchan->resp_buf) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + return -ENOMEM; + } + + rx_mchan->req_buf = devm_ioremap(mdev, + remote_res.start | host_dst, + IPI_BUF_SIZE); + if (!rx_mchan->req_buf) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + return -ENOMEM; + } + + rx_mchan->resp_buf = devm_ioremap(mdev, + (host_res.start | remote_dst) + + RESP_OFFSET, IPI_BUF_SIZE); + if (!rx_mchan->resp_buf) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + return -ENOMEM; + } + + tx_mchan->resp_buf_size = IPI_BUF_SIZE; + tx_mchan->req_buf_size = IPI_BUF_SIZE; + tx_mchan->rx_buf = devm_kzalloc(mdev, IPI_BUF_SIZE + + sizeof(struct zynqmp_ipi_message), + GFP_KERNEL); + if (!tx_mchan->rx_buf) + return -ENOMEM; + + rx_mchan->resp_buf_size = IPI_BUF_SIZE; + rx_mchan->req_buf_size = IPI_BUF_SIZE; + rx_mchan->rx_buf = devm_kzalloc(mdev, IPI_BUF_SIZE + + sizeof(struct zynqmp_ipi_message), + GFP_KERNEL); + if (!rx_mchan->rx_buf) + return -ENOMEM; + } + + return 0; +} + +static int xlnx_mbox_cpuhp_start(unsigned int cpu) +{ + struct zynqmp_ipi_pdata *pdata; + + pdata = get_cpu_var(per_cpu_pdata); + put_cpu_var(per_cpu_pdata); + enable_percpu_irq(pdata->virq_sgi, IRQ_TYPE_NONE); + + return 0; +} + +static int xlnx_mbox_cpuhp_down(unsigned int cpu) +{ + struct zynqmp_ipi_pdata *pdata; + + pdata = get_cpu_var(per_cpu_pdata); + put_cpu_var(per_cpu_pdata); + disable_percpu_irq(pdata->virq_sgi); + + return 0; +} + +static void xlnx_disable_percpu_irq(void *data) +{ + struct zynqmp_ipi_pdata *pdata; + + pdata = *this_cpu_ptr(&per_cpu_pdata); + + disable_percpu_irq(pdata->virq_sgi); +} + +static int xlnx_mbox_init_sgi(struct platform_device *pdev, + int sgi_num, + struct zynqmp_ipi_pdata *pdata) +{ + int ret = 0; + int cpu; + + /* + * IRQ related structures are used for the following: + * for each SGI interrupt ensure its mapped by GIC IRQ domain + * and that each corresponding linux IRQ for the HW IRQ has + * a handler for when receiving an interrupt from the remote + * processor. + */ + struct irq_domain *domain; + struct irq_fwspec sgi_fwspec; + struct device_node *interrupt_parent = NULL; + struct device *dev = &pdev->dev; + + /* Find GIC controller to map SGIs. */ + interrupt_parent = of_irq_find_parent(dev->of_node); + if (!interrupt_parent) { + dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n"); + return -EINVAL; + } + + /* Each SGI needs to be associated with GIC's IRQ domain. */ + domain = irq_find_host(interrupt_parent); + of_node_put(interrupt_parent); + + /* Each mapping needs GIC domain when finding IRQ mapping. */ + sgi_fwspec.fwnode = domain->fwnode; + + /* + * When irq domain looks at mapping each arg is as follows: + * 3 args for: interrupt type (SGI), interrupt # (set later), type + */ + sgi_fwspec.param_count = 1; + + /* Set SGI's hwirq */ + sgi_fwspec.param[0] = sgi_num; + pdata->virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec); + + for_each_possible_cpu(cpu) + per_cpu(per_cpu_pdata, cpu) = pdata; + + ret = request_percpu_irq(pdata->virq_sgi, zynqmp_sgi_interrupt, pdev->name, + &per_cpu_pdata); + WARN_ON(ret); + if (ret) { + irq_dispose_mapping(pdata->virq_sgi); return ret; } - mbox = &ipi_mbox->mbox; - mbox->dev = mdev; - mbox->ops = &zynqmp_ipi_chan_ops; - mbox->num_chans = 2; - mbox->txdone_irq = false; - mbox->txdone_poll = true; - mbox->txpoll_period = 5; - mbox->of_xlate = zynqmp_ipi_of_xlate; - chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); - if (!chans) - return -ENOMEM; - mbox->chans = chans; - chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; - chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; - ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX; - ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX; - ret = devm_mbox_controller_register(mdev, mbox); - if (ret) - dev_err(mdev, - "Failed to register mbox_controller(%d)\n", ret); - else - dev_info(mdev, - "Registered ZynqMP IPI mbox with TX/RX channels.\n"); + irq_to_desc(pdata->virq_sgi); + irq_set_status_flags(pdata->virq_sgi, IRQ_PER_CPU); + + /* Setup function for the CPU hot-plug cases */ + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mailbox/sgi:starting", + xlnx_mbox_cpuhp_start, xlnx_mbox_cpuhp_down); + return ret; } +static void xlnx_mbox_cleanup_sgi(struct zynqmp_ipi_pdata *pdata) +{ + cpuhp_remove_state(CPUHP_AP_ONLINE_DYN); + + on_each_cpu(xlnx_disable_percpu_irq, NULL, 1); + + irq_clear_status_flags(pdata->virq_sgi, IRQ_PER_CPU); + free_percpu_irq(pdata->virq_sgi, &per_cpu_pdata); + irq_dispose_mapping(pdata->virq_sgi); +} + /** * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices * @@ -612,6 +881,9 @@ static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata) struct zynqmp_ipi_mbox *ipi_mbox; int i; + if (pdata->irq < MAX_SGI) + xlnx_mbox_cleanup_sgi(pdata); + i = pdata->num_mboxes; for (; i >= 0; i--) { ipi_mbox = &pdata->ipi_mboxes[i]; @@ -627,9 +899,11 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *nc, *np = pdev->dev.of_node; - struct zynqmp_ipi_pdata *pdata; + struct zynqmp_ipi_pdata __percpu *pdata; + struct of_phandle_args out_irq; struct zynqmp_ipi_mbox *mbox; int num_mboxes, ret = -EINVAL; + setup_ipi_fn ipi_fn; num_mboxes = of_get_available_child_count(np); if (num_mboxes == 0) { @@ -650,9 +924,18 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) return ret; } + ipi_fn = (setup_ipi_fn)device_get_match_data(&pdev->dev); + if (!ipi_fn) { + dev_err(dev, + "Mbox Compatible String is missing IPI Setup fn.\n"); + return -ENODEV; + } + pdata->num_mboxes = num_mboxes; mbox = pdata->ipi_mboxes; + mbox->setup_ipi_fn = ipi_fn; + for_each_available_child_of_node(np, nc) { mbox->pdata = pdata; ret = zynqmp_ipi_mbox_probe(mbox, nc); @@ -665,14 +948,32 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) mbox++; } - /* IPI IRQ */ - ret = platform_get_irq(pdev, 0); - if (ret < 0) + ret = of_irq_parse_one(dev_of_node(dev), 0, &out_irq); + if (ret < 0) { + dev_err(dev, "failed to parse interrupts\n"); goto free_mbox_dev; + } + ret = out_irq.args[1]; + + /* + * If Interrupt number is in SGI range, then request SGI else request + * IPI system IRQ. + */ + if (ret < MAX_SGI) { + pdata->irq = ret; + ret = xlnx_mbox_init_sgi(pdev, pdata->irq, pdata); + if (ret) + goto free_mbox_dev; + } else { + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto free_mbox_dev; + + pdata->irq = ret; + ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, + IRQF_SHARED, dev_name(dev), pdata); + } - pdata->irq = ret; - ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, - IRQF_SHARED, dev_name(dev), pdata); if (ret) { dev_err(dev, "IRQ %d is not requested successfully.\n", pdata->irq); @@ -695,6 +996,17 @@ static void zynqmp_ipi_remove(struct platform_device *pdev) zynqmp_ipi_free_mboxes(pdata); } +static const struct of_device_id zynqmp_ipi_of_match[] = { + { .compatible = "xlnx,zynqmp-ipi-mailbox", + .data = &zynqmp_ipi_setup, + }, + { .compatible = "xlnx,versal-ipi-mailbox", + .data = &versal_ipi_setup, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); + static struct platform_driver zynqmp_ipi_driver = { .probe = zynqmp_ipi_probe, .remove_new = zynqmp_ipi_remove, diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index cba09660148a..4d11fc664cb0 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -171,7 +171,7 @@ static const char *read_super(struct cache_sb *sb, struct block_device *bdev, struct page *page; unsigned int i; - page = read_cache_page_gfp(bdev->bd_inode->i_mapping, + page = read_cache_page_gfp(bdev->bd_mapping, SB_OFFSET >> PAGE_SHIFT, GFP_KERNEL); if (IS_ERR(page)) return "IO error"; @@ -2555,10 +2555,6 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, if (IS_ERR(bdev_file)) goto out_free_sb; - err = "failed to set blocksize"; - if (set_blocksize(file_bdev(bdev_file), 4096)) - goto out_blkdev_put; - err = read_super(sb, file_bdev(bdev_file), &sb_disk); if (err) goto out_blkdev_put; diff --git a/drivers/md/dm-vdo/dm-vdo-target.c b/drivers/md/dm-vdo/dm-vdo-target.c index 5a4b0a927f56..b423bec6458b 100644 --- a/drivers/md/dm-vdo/dm-vdo-target.c +++ b/drivers/md/dm-vdo/dm-vdo-target.c @@ -878,7 +878,7 @@ static int parse_device_config(int argc, char **argv, struct dm_target *ti, } if (config->version == 0) { - u64 device_size = i_size_read(config->owned_device->bdev->bd_inode); + u64 device_size = bdev_nr_bytes(config->owned_device->bdev); config->physical_blocks = device_size / VDO_BLOCK_SIZE; } @@ -1011,7 +1011,7 @@ static void vdo_status(struct dm_target *ti, status_type_t status_type, static block_count_t __must_check get_underlying_device_block_count(const struct vdo *vdo) { - return i_size_read(vdo_get_backing_device(vdo)->bd_inode) / VDO_BLOCK_SIZE; + return bdev_nr_bytes(vdo_get_backing_device(vdo)) / VDO_BLOCK_SIZE; } static int __must_check process_vdo_message_locked(struct vdo *vdo, unsigned int argc, diff --git a/drivers/md/dm-vdo/indexer/io-factory.c b/drivers/md/dm-vdo/indexer/io-factory.c index 515765d35794..1bee9d63dc0a 100644 --- a/drivers/md/dm-vdo/indexer/io-factory.c +++ b/drivers/md/dm-vdo/indexer/io-factory.c @@ -90,7 +90,7 @@ void uds_put_io_factory(struct io_factory *factory) size_t uds_get_writable_size(struct io_factory *factory) { - return i_size_read(factory->bdev->bd_inode); + return bdev_nr_bytes(factory->bdev); } /* Create a struct dm_bufio_client for an index region starting at offset. */ diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 8c00e0c695c5..9f4782bdbf4b 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -54,7 +54,7 @@ static int intel_lpss_pci_probe(struct pci_dev *pdev, if (ret) return ret; - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX); if (ret < 0) return ret; diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 4f8d962bb5b2..c61e8953511d 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -787,8 +787,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, error = pci_alloc_irq_vectors(pdev, num_irq_vectors, num_irq_vectors, PCI_IRQ_MSIX); if (error < 0) { - error = pci_alloc_irq_vectors(pdev, 1, 1, - PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY); + error = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (error < 0) goto err_unsubscribe_event; } else { diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index caacdc0a3819..b06c8dd51562 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -265,6 +265,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, struct file *bdev_file; struct block_device *bdev; struct block2mtd_dev *dev; + loff_t size; char *name; if (!devname) @@ -291,7 +292,8 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, goto err_free_block2mtd; } - if ((long)bdev->bd_inode->i_size % erase_size) { + size = bdev_nr_bytes(bdev); + if ((long)size % erase_size) { pr_err("erasesize must be a divisor of device size\n"); goto err_free_block2mtd; } @@ -309,7 +311,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, dev->mtd.name = name; - dev->mtd.size = bdev->bd_inode->i_size & PAGE_MASK; + dev->mtd.size = size & PAGE_MASK; dev->mtd.erasesize = erase_size; dev->mtd.writesize = 1; dev->mtd.writebufsize = PAGE_SIZE; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index f409d7bd1f1e..c5e5fac49779 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -170,7 +170,7 @@ static int xgbe_config_irqs(struct xgbe_prv_data *pdata) goto out; ret = pci_alloc_irq_vectors(pdata->pcidev, 1, 1, - PCI_IRQ_LEGACY | PCI_IRQ_MSI); + PCI_IRQ_INTX | PCI_IRQ_MSI); if (ret < 0) { dev_info(pdata->dev, "single IRQ enablement failed\n"); return ret; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h index 7e9c74b141ef..fc2b325f34e7 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h @@ -17,7 +17,7 @@ #define AQ_CFG_IS_POLLING_DEF 0U -#define AQ_CFG_FORCE_LEGACY_INT 0U +#define AQ_CFG_FORCE_INTX 0U #define AQ_CFG_INTERRUPT_MODERATION_OFF 0 #define AQ_CFG_INTERRUPT_MODERATION_ON 1 diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h index dbd284660135..f010bda61c96 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h @@ -104,7 +104,7 @@ struct aq_stats_s { }; #define AQ_HW_IRQ_INVALID 0U -#define AQ_HW_IRQ_LEGACY 1U +#define AQ_HW_IRQ_INTX 1U #define AQ_HW_IRQ_MSI 2U #define AQ_HW_IRQ_MSIX 3U diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index d6d6d5d37ff3..fe0e3e2a8117 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -127,7 +127,7 @@ void aq_nic_cfg_start(struct aq_nic_s *self) cfg->irq_type = aq_pci_func_get_irq_type(self); - if ((cfg->irq_type == AQ_HW_IRQ_LEGACY) || + if ((cfg->irq_type == AQ_HW_IRQ_INTX) || (cfg->aq_hw_caps->vecs == 1U) || (cfg->vecs == 1U)) { cfg->is_rss = 0U; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index baa5f8cc31f2..43c71f6b314f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -200,7 +200,7 @@ unsigned int aq_pci_func_get_irq_type(struct aq_nic_s *self) if (self->pdev->msi_enabled) return AQ_HW_IRQ_MSI; - return AQ_HW_IRQ_LEGACY; + return AQ_HW_IRQ_INTX; } static void aq_pci_free_irq_vectors(struct aq_nic_s *self) @@ -298,11 +298,8 @@ static int aq_pci_probe(struct pci_dev *pdev, numvecs += AQ_HW_SERVICE_IRQS; /*enable interrupts */ -#if !AQ_CFG_FORCE_LEGACY_INT - err = pci_alloc_irq_vectors(self->pdev, 1, numvecs, - PCI_IRQ_MSIX | PCI_IRQ_MSI | - PCI_IRQ_LEGACY); - +#if !AQ_CFG_FORCE_INTX + err = pci_alloc_irq_vectors(self->pdev, 1, numvecs, PCI_IRQ_ALL_TYPES); if (err < 0) goto err_hwinit; numvecs = err; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c index 9dfd68f0fda9..8de2cdd09213 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c @@ -352,7 +352,7 @@ static int hw_atl_a0_hw_init(struct aq_hw_s *self, const u8 *mac_addr) { static u32 aq_hw_atl_igcr_table_[4][2] = { [AQ_HW_IRQ_INVALID] = { 0x20000000U, 0x20000000U }, - [AQ_HW_IRQ_LEGACY] = { 0x20000080U, 0x20000080U }, + [AQ_HW_IRQ_INTX] = { 0x20000080U, 0x20000080U }, [AQ_HW_IRQ_MSI] = { 0x20000021U, 0x20000025U }, [AQ_HW_IRQ_MSIX] = { 0x20000022U, 0x20000026U }, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index 54e70f07b573..56c46266bb0a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -562,7 +562,7 @@ static int hw_atl_b0_hw_init(struct aq_hw_s *self, const u8 *mac_addr) { static u32 aq_hw_atl_igcr_table_[4][2] = { [AQ_HW_IRQ_INVALID] = { 0x20000000U, 0x20000000U }, - [AQ_HW_IRQ_LEGACY] = { 0x20000080U, 0x20000080U }, + [AQ_HW_IRQ_INTX] = { 0x20000080U, 0x20000080U }, [AQ_HW_IRQ_MSI] = { 0x20000021U, 0x20000025U }, [AQ_HW_IRQ_MSIX] = { 0x20000022U, 0x20000026U }, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c index 220400a633f5..b0ed572e88c6 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c @@ -534,7 +534,7 @@ static int hw_atl2_hw_init(struct aq_hw_s *self, const u8 *mac_addr) { static u32 aq_hw_atl2_igcr_table_[4][2] = { [AQ_HW_IRQ_INVALID] = { 0x20000000U, 0x20000000U }, - [AQ_HW_IRQ_LEGACY] = { 0x20000080U, 0x20000080U }, + [AQ_HW_IRQ_INTX] = { 0x20000080U, 0x20000080U }, [AQ_HW_IRQ_MSI] = { 0x20000021U, 0x20000025U }, [AQ_HW_IRQ_MSIX] = { 0x20000022U, 0x20000026U }, }; diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 3d28654e5df7..ad6d6abd885f 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -901,7 +901,7 @@ static int alx_init_intr(struct alx_priv *alx) int ret; ret = pci_alloc_irq_vectors(alx->hw.pdev, 1, 1, - PCI_IRQ_MSI | PCI_IRQ_LEGACY); + PCI_IRQ_MSI | PCI_IRQ_INTX); if (ret < 0) return ret; diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 5abbea91bc07..e5ea827a21e7 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5106,7 +5106,7 @@ static int rtl_alloc_irq(struct rtl8169_private *tp) rtl_lock_config_regs(tp); fallthrough; case RTL_GIGA_MAC_VER_07 ... RTL_GIGA_MAC_VER_17: - flags = PCI_IRQ_LEGACY; + flags = PCI_IRQ_INTX; break; default: flags = PCI_IRQ_ALL_TYPES; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 07ba3a270a14..68bde91b67a0 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1674,14 +1674,14 @@ static int wx_set_interrupt_capability(struct wx *wx) /* minmum one for queue, one for misc*/ nvecs = 1; nvecs = pci_alloc_irq_vectors(pdev, nvecs, - nvecs, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + nvecs, PCI_IRQ_MSI | PCI_IRQ_INTX); if (nvecs == 1) { if (pdev->msi_enabled) wx_err(wx, "Fallback to MSI.\n"); else - wx_err(wx, "Fallback to LEGACY.\n"); + wx_err(wx, "Fallback to INTx.\n"); } else { - wx_err(wx, "Failed to allocate MSI/LEGACY interrupts. Error: %d\n", nvecs); + wx_err(wx, "Failed to allocate MSI/INTx interrupts. Error: %d\n", nvecs); return nvecs; } @@ -2127,7 +2127,7 @@ void wx_write_eitr(struct wx_q_vector *q_vector) * wx_configure_vectors - Configure vectors for hardware * @wx: board private structure * - * wx_configure_vectors sets up the hardware to properly generate MSI-X/MSI/LEGACY + * wx_configure_vectors sets up the hardware to properly generate MSI-X/MSI/INTx * interrupts. **/ void wx_configure_vectors(struct wx *wx) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index a378bc48b1d2..f0441b3d7dcb 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -394,14 +394,14 @@ static irqreturn_t ath10k_ahb_interrupt_handler(int irq, void *arg) if (!ath10k_pci_irq_pending(ar)) return IRQ_NONE; - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); napi_schedule(&ar->napi); return IRQ_HANDLED; } -static int ath10k_ahb_request_irq_legacy(struct ath10k *ar) +static int ath10k_ahb_request_irq_intx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); @@ -415,12 +415,12 @@ static int ath10k_ahb_request_irq_legacy(struct ath10k *ar) ar_ahb->irq, ret); return ret; } - ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY; + ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_INTX; return 0; } -static void ath10k_ahb_release_irq_legacy(struct ath10k *ar) +static void ath10k_ahb_release_irq_intx(struct ath10k *ar) { struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); @@ -430,7 +430,7 @@ static void ath10k_ahb_release_irq_legacy(struct ath10k *ar) static void ath10k_ahb_irq_disable(struct ath10k *ar) { ath10k_ce_disable_interrupts(ar); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); } static int ath10k_ahb_resource_init(struct ath10k *ar) @@ -621,7 +621,7 @@ static int ath10k_ahb_hif_start(struct ath10k *ar) ath10k_core_napi_enable(ar); ath10k_ce_enable_interrupts(ar); - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_rx_post(ar); @@ -775,7 +775,7 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ath10k_pci_init_napi(ar); - ret = ath10k_ahb_request_irq_legacy(ar); + ret = ath10k_ahb_request_irq_intx(ar); if (ret) goto err_free_pipes; @@ -806,7 +806,7 @@ err_halt_device: ath10k_ahb_clock_disable(ar); err_free_irq: - ath10k_ahb_release_irq_legacy(ar); + ath10k_ahb_release_irq_intx(ar); err_free_pipes: ath10k_pci_release_resource(ar); @@ -828,7 +828,7 @@ static void ath10k_ahb_remove(struct platform_device *pdev) ath10k_core_unregister(ar); ath10k_ahb_irq_disable(ar); - ath10k_ahb_release_irq_legacy(ar); + ath10k_ahb_release_irq_intx(ar); ath10k_pci_release_resource(ar); ath10k_ahb_halt_chip(ar); ath10k_ahb_clock_disable(ar); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 3777fe5d7d69..c52a16f8078f 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -721,7 +721,7 @@ bool ath10k_pci_irq_pending(struct ath10k *ar) return false; } -void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +void ath10k_pci_disable_and_clear_intx_irq(struct ath10k *ar) { /* IMPORTANT: INTR_CLR register has to be set after * INTR_ENABLE is set to 0, otherwise interrupt can not be @@ -739,7 +739,7 @@ void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) PCIE_INTR_ENABLE_ADDRESS); } -void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +void ath10k_pci_enable_intx_irq(struct ath10k *ar) { ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, @@ -1935,7 +1935,7 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar) static void ath10k_pci_irq_disable(struct ath10k *ar) { ath10k_ce_disable_interrupts(ar); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); } @@ -1949,7 +1949,7 @@ static void ath10k_pci_irq_sync(struct ath10k *ar) static void ath10k_pci_irq_enable(struct ath10k *ar) { ath10k_ce_enable_interrupts(ar); - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_irq_msi_fw_unmask(ar); } @@ -3111,11 +3111,11 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_NONE; } - if ((ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) && + if ((ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_INTX) && !ath10k_pci_irq_pending(ar)) return IRQ_NONE; - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); napi_schedule(&ar->napi); @@ -3152,7 +3152,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget) napi_schedule(ctx); goto out; } - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_irq_msi_fw_unmask(ar); } @@ -3177,7 +3177,7 @@ static int ath10k_pci_request_irq_msi(struct ath10k *ar) return 0; } -static int ath10k_pci_request_irq_legacy(struct ath10k *ar) +static int ath10k_pci_request_irq_intx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -3199,8 +3199,8 @@ static int ath10k_pci_request_irq(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); switch (ar_pci->oper_irq_mode) { - case ATH10K_PCI_IRQ_LEGACY: - return ath10k_pci_request_irq_legacy(ar); + case ATH10K_PCI_IRQ_INTX: + return ath10k_pci_request_irq_intx(ar); case ATH10K_PCI_IRQ_MSI: return ath10k_pci_request_irq_msi(ar); default: @@ -3232,7 +3232,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) ath10k_pci_irq_mode); /* Try MSI */ - if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_LEGACY) { + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_INTX) { ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_MSI; ret = pci_enable_msi(ar_pci->pdev); if (ret == 0) @@ -3250,7 +3250,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) * For now, fix the race by repeating the write in below * synchronization checking. */ - ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY; + ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_INTX; ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); @@ -3258,7 +3258,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) return 0; } -static void ath10k_pci_deinit_irq_legacy(struct ath10k *ar) +static void ath10k_pci_deinit_irq_intx(struct ath10k *ar) { ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, 0); @@ -3269,8 +3269,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); switch (ar_pci->oper_irq_mode) { - case ATH10K_PCI_IRQ_LEGACY: - ath10k_pci_deinit_irq_legacy(ar); + case ATH10K_PCI_IRQ_INTX: + ath10k_pci_deinit_irq_intx(ar); break; default: pci_disable_msi(ar_pci->pdev); @@ -3307,14 +3307,14 @@ int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (val & FW_IND_INITIALIZED) break; - if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) + if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_INTX) /* Fix potential race by repeating CORE_BASE writes */ - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); mdelay(10); } while (time_before(jiffies, timeout)); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); if (val == 0xffffffff) { diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 27bb4cf2dfea..4c3f536f2ea1 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -101,7 +101,7 @@ struct ath10k_pci_supp_chip { enum ath10k_pci_irq_mode { ATH10K_PCI_IRQ_AUTO = 0, - ATH10K_PCI_IRQ_LEGACY = 1, + ATH10K_PCI_IRQ_INTX = 1, ATH10K_PCI_IRQ_MSI = 2, }; @@ -243,9 +243,9 @@ int ath10k_pci_init_pipes(struct ath10k *ar); int ath10k_pci_init_config(struct ath10k *ar); void ath10k_pci_rx_post(struct ath10k *ar); void ath10k_pci_flush(struct ath10k *ar); -void ath10k_pci_enable_legacy_irq(struct ath10k *ar); +void ath10k_pci_enable_intx_irq(struct ath10k *ar); bool ath10k_pci_irq_pending(struct ath10k *ar); -void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar); +void ath10k_pci_disable_and_clear_intx_irq(struct ath10k *ar); void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar); int ath10k_pci_wait_for_target_init(struct ath10k *ar); int ath10k_pci_setup_resource(struct ath10k *ar); diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index 7a093f3d5f74..30232f7e3ec5 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1613,7 +1613,7 @@ static struct rtw_hci_ops rtw_pci_ops = { static int rtw_pci_request_irq(struct rtw_dev *rtwdev, struct pci_dev *pdev) { - unsigned int flags = PCI_IRQ_LEGACY; + unsigned int flags = PCI_IRQ_INTX; int ret; if (!rtw_disable_msi) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 7b00476a5dee..03bbcf9b6737 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3637,7 +3637,7 @@ static int rtw89_pci_request_irq(struct rtw89_dev *rtwdev, unsigned long flags = 0; int ret; - flags |= PCI_IRQ_LEGACY | PCI_IRQ_MSI; + flags |= PCI_IRQ_INTX | PCI_IRQ_MSI; ret = pci_alloc_irq_vectors(pdev, 1, 1, flags); if (ret < 0) { rtw89_err(rtwdev, "failed to alloc irq vectors, ret %d\n", ret); diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 48823b53ede3..48dfb1a69a77 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2129,7 +2129,7 @@ static int idt_init_isr(struct idt_ntb_dev *ndev) int ret; /* Allocate just one interrupt vector for the ISR */ - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_INTX); if (ret != 1) { dev_err(&pdev->dev, "Failed to allocate IRQ vector"); return ret; diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 6449056b57dd..30f031de9cfe 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -36,10 +36,13 @@ DEFINE_RAW_SPINLOCK(pci_lock); int noinline pci_bus_read_config_##size \ (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \ { \ - int res; \ unsigned long flags; \ u32 data = 0; \ - if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ + int res; \ + \ + if (PCI_##size##_BAD) \ + return PCIBIOS_BAD_REGISTER_NUMBER; \ + \ pci_lock_config(flags); \ res = bus->ops->read(bus, devfn, pos, len, &data); \ if (res) \ @@ -47,6 +50,7 @@ int noinline pci_bus_read_config_##size \ else \ *value = (type)data; \ pci_unlock_config(flags); \ + \ return res; \ } @@ -54,12 +58,16 @@ int noinline pci_bus_read_config_##size \ int noinline pci_bus_write_config_##size \ (struct pci_bus *bus, unsigned int devfn, int pos, type value) \ { \ - int res; \ unsigned long flags; \ - if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ + int res; \ + \ + if (PCI_##size##_BAD) \ + return PCIBIOS_BAD_REGISTER_NUMBER; \ + \ pci_lock_config(flags); \ res = bus->ops->write(bus, devfn, pos, len, value); \ pci_unlock_config(flags); \ + \ return res; \ } @@ -216,24 +224,27 @@ static noinline void pci_wait_cfg(struct pci_dev *dev) } /* Returns 0 on success, negative values indicate error. */ -#define PCI_USER_READ_CONFIG(size, type) \ +#define PCI_USER_READ_CONFIG(size, type) \ int pci_user_read_config_##size \ (struct pci_dev *dev, int pos, type *val) \ { \ - int ret = PCIBIOS_SUCCESSFUL; \ u32 data = -1; \ + int ret; \ + \ if (PCI_##size##_BAD) \ return -EINVAL; \ - raw_spin_lock_irq(&pci_lock); \ + \ + raw_spin_lock_irq(&pci_lock); \ if (unlikely(dev->block_cfg_access)) \ pci_wait_cfg(dev); \ ret = dev->bus->ops->read(dev->bus, dev->devfn, \ - pos, sizeof(type), &data); \ - raw_spin_unlock_irq(&pci_lock); \ + pos, sizeof(type), &data); \ + raw_spin_unlock_irq(&pci_lock); \ if (ret) \ PCI_SET_ERROR_RESPONSE(val); \ else \ *val = (type)data; \ + \ return pcibios_err_to_errno(ret); \ } \ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); @@ -243,15 +254,18 @@ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); int pci_user_write_config_##size \ (struct pci_dev *dev, int pos, type val) \ { \ - int ret = PCIBIOS_SUCCESSFUL; \ + int ret; \ + \ if (PCI_##size##_BAD) \ return -EINVAL; \ - raw_spin_lock_irq(&pci_lock); \ + \ + raw_spin_lock_irq(&pci_lock); \ if (unlikely(dev->block_cfg_access)) \ pci_wait_cfg(dev); \ ret = dev->bus->ops->write(dev->bus, dev->devfn, \ - pos, sizeof(type), val); \ - raw_spin_unlock_irq(&pci_lock); \ + pos, sizeof(type), val); \ + raw_spin_unlock_irq(&pci_lock); \ + \ return pcibios_err_to_errno(ret); \ } \ EXPORT_SYMBOL_GPL(pci_user_write_config_##size); @@ -275,6 +289,8 @@ void pci_cfg_access_lock(struct pci_dev *dev) { might_sleep(); + lock_map_acquire(&dev->cfg_access_lock); + raw_spin_lock_irq(&pci_lock); if (dev->block_cfg_access) pci_wait_cfg(dev); @@ -329,6 +345,8 @@ void pci_cfg_access_unlock(struct pci_dev *dev) raw_spin_unlock_irqrestore(&pci_lock, flags); wake_up_all(&pci_cfg_wait); + + lock_map_release(&dev->cfg_access_lock); } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 81c50dc64da9..e0cc4560dfde 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -99,14 +99,11 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS; } else { bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); - bool is_64bits = sz > SZ_2G; + bool is_64bits = !!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64); if (is_64bits && (bar & 1)) return -EINVAL; - if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) - epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - if (is_64bits && is_prefetch) ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; else if (is_prefetch) @@ -746,6 +743,8 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) spin_lock_init(&ep->lock); + pci_epc_init_notify(epc); + return 0; free_epc_mem: diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 0e406677060d..d2d17d37d3e0 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -467,6 +467,15 @@ static int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, return ret; } + ret = dw_pcie_ep_init_registers(ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(ep); + return ret; + } + + dw_pcie_ep_init_notify(ep); + return 0; } diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 99a60270b26c..917c69edee1d 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1123,6 +1123,16 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, dev_err(dev, "failed to initialize endpoint\n"); return ret; } + + ret = dw_pcie_ep_init_registers(ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(ep); + return ret; + } + + dw_pcie_ep_init_notify(ep); + /* Start LTSSM. */ imx6_pcie_ltssm_enable(dev); diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 844de4418724..d3a7d14ee685 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -1286,6 +1286,15 @@ static int ks_pcie_probe(struct platform_device *pdev) ret = dw_pcie_ep_init(&pci->ep); if (ret < 0) goto err_get_sync; + + ret = dw_pcie_ep_init_registers(&pci->ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + goto err_ep_init; + } + + dw_pcie_ep_init_notify(&pci->ep); + break; default: dev_err(dev, "INVALID device type %d\n", mode); @@ -1295,6 +1304,8 @@ static int ks_pcie_probe(struct platform_device *pdev) return 0; +err_ep_init: + dw_pcie_ep_deinit(&pci->ep); err_get_sync: pm_runtime_put(dev); pm_runtime_disable(dev); diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 1f6ee1460ec2..7dde6d5fa4d8 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -279,6 +279,15 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) if (ret) return ret; + ret = dw_pcie_ep_init_registers(&pci->ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(&pci->ep); + return ret; + } + + dw_pcie_ep_init_notify(&pci->ep); + return ls_pcie_ep_interrupt_init(pcie, pdev); } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 9ed0a9ba7619..a4630b92489b 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -441,7 +441,20 @@ static int artpec6_pcie_probe(struct platform_device *pdev) pci->ep.ops = &pcie_ep_ops; - return dw_pcie_ep_init(&pci->ep); + ret = dw_pcie_ep_init(&pci->ep); + if (ret) + return ret; + + ret = dw_pcie_ep_init_registers(&pci->ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(&pci->ep); + return ret; + } + + dw_pcie_ep_init_notify(&pci->ep); + + break; default: dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); } diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 746a11dcb67f..47391d7d3a73 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -15,6 +15,10 @@ #include <linux/pci-epc.h> #include <linux/pci-epf.h> +/** + * dw_pcie_ep_linkup - Notify EPF drivers about Link Up event + * @ep: DWC EP device + */ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) { struct pci_epc *epc = ep->epc; @@ -23,6 +27,10 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup); +/** + * dw_pcie_ep_init_notify - Notify EPF drivers about EPC initialization complete + * @ep: DWC EP device + */ void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) { struct pci_epc *epc = ep->epc; @@ -31,6 +39,14 @@ void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify); +/** + * dw_pcie_ep_get_func_from_ep - Get the struct dw_pcie_ep_func corresponding to + * the endpoint function + * @ep: DWC EP device + * @func_no: Function number of the endpoint device + * + * Return: struct dw_pcie_ep_func if success, NULL otherwise. + */ struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) { @@ -61,6 +77,11 @@ static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no, dw_pcie_dbi_ro_wr_dis(pci); } +/** + * dw_pcie_ep_reset_bar - Reset endpoint BAR + * @pci: DWC PCI device + * @bar: BAR number of the endpoint + */ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { u8 func_no, funcs; @@ -440,6 +461,13 @@ static const struct pci_epc_ops epc_ops = { .get_features = dw_pcie_ep_get_features, }; +/** + * dw_pcie_ep_raise_intx_irq - Raise INTx IRQ to the host + * @ep: DWC EP device + * @func_no: Function number of the endpoint + * + * Return: 0 if success, errono otherwise. + */ int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -451,6 +479,14 @@ int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) } EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_intx_irq); +/** + * dw_pcie_ep_raise_msi_irq - Raise MSI IRQ to the host + * @ep: DWC EP device + * @func_no: Function number of the endpoint + * @interrupt_num: Interrupt number to be raised + * + * Return: 0 if success, errono otherwise. + */ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num) { @@ -500,6 +536,15 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, } EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_msi_irq); +/** + * dw_pcie_ep_raise_msix_irq_doorbell - Raise MSI-X to the host using Doorbell + * method + * @ep: DWC EP device + * @func_no: Function number of the endpoint device + * @interrupt_num: Interrupt number to be raised + * + * Return: 0 if success, errno otherwise. + */ int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num) { @@ -519,6 +564,14 @@ int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no, return 0; } +/** + * dw_pcie_ep_raise_msix_irq - Raise MSI-X to the host + * @ep: DWC EP device + * @func_no: Function number of the endpoint device + * @interrupt_num: Interrupt number to be raised + * + * Return: 0 if success, errno otherwise. + */ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num) { @@ -566,22 +619,42 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } -void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +/** + * dw_pcie_ep_cleanup - Cleanup DWC EP resources after fundamental reset + * @ep: DWC EP device + * + * Cleans up the DWC EP specific resources like eDMA etc... after fundamental + * reset like PERST#. Note that this API is only applicable for drivers + * supporting PERST# or any other methods of fundamental reset. + */ +void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct pci_epc *epc = ep->epc; dw_pcie_edma_remove(pci); + ep->epc->init_complete = false; +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup); + +/** + * dw_pcie_ep_deinit - Deinitialize the endpoint device + * @ep: DWC EP device + * + * Deinitialize the endpoint device. EPC device is not destroyed since that will + * be taken care by Devres. + */ +void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) +{ + struct pci_epc *epc = ep->epc; + + dw_pcie_ep_cleanup(ep); pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, epc->mem->window.page_size); pci_epc_mem_exit(epc); - - if (ep->ops->deinit) - ep->ops->deinit(ep); } -EXPORT_SYMBOL_GPL(dw_pcie_ep_exit); +EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) { @@ -601,14 +674,27 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) return 0; } -int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) +/** + * dw_pcie_ep_init_registers - Initialize DWC EP specific registers + * @ep: DWC EP device + * + * Initialize the registers (CSRs) specific to DWC EP. This API should be called + * only when the endpoint receives an active refclk (either from host or + * generated locally). + */ +int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_pcie_ep_func *ep_func; + struct device *dev = pci->dev; + struct pci_epc *epc = ep->epc; unsigned int offset, ptm_cap_base; unsigned int nbars; u8 hdr_type; + u8 func_no; + int i, ret; + void *addr; u32 reg; - int i; hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) & PCI_HEADER_TYPE_MASK; @@ -619,6 +705,58 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) return -EIO; } + dw_pcie_version_detect(pci); + + dw_pcie_iatu_detect(pci); + + ret = dw_pcie_edma_detect(pci); + if (ret) + return ret; + + if (!ep->ib_window_map) { + ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows, + GFP_KERNEL); + if (!ep->ib_window_map) + goto err_remove_edma; + } + + if (!ep->ob_window_map) { + ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows, + GFP_KERNEL); + if (!ep->ob_window_map) + goto err_remove_edma; + } + + if (!ep->outbound_addr) { + addr = devm_kcalloc(dev, pci->num_ob_windows, sizeof(phys_addr_t), + GFP_KERNEL); + if (!addr) + goto err_remove_edma; + ep->outbound_addr = addr; + } + + for (func_no = 0; func_no < epc->max_functions; func_no++) { + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (ep_func) + continue; + + ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL); + if (!ep_func) + goto err_remove_edma; + + ep_func->func_no = func_no; + ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSI); + ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSIX); + + list_add_tail(&ep_func->list, &ep->func_list); + } + + if (ep->ops->init) + ep->ops->init(ep); + offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); @@ -658,22 +796,32 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) dw_pcie_dbi_ro_wr_dis(pci); return 0; + +err_remove_edma: + dw_pcie_edma_remove(pci); + + return ret; } -EXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete); +EXPORT_SYMBOL_GPL(dw_pcie_ep_init_registers); +/** + * dw_pcie_ep_init - Initialize the endpoint device + * @ep: DWC EP device + * + * Initialize the endpoint device. Allocate resources and create the EPC + * device with the endpoint framework. + * + * Return: 0 if success, errno otherwise. + */ int dw_pcie_ep_init(struct dw_pcie_ep *ep) { int ret; - void *addr; - u8 func_no; struct resource *res; struct pci_epc *epc; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev; struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; - const struct pci_epc_features *epc_features; - struct dw_pcie_ep_func *ep_func; INIT_LIST_HEAD(&ep->func_list); @@ -691,26 +839,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) if (ep->ops->pre_init) ep->ops->pre_init(ep); - dw_pcie_version_detect(pci); - - dw_pcie_iatu_detect(pci); - - ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows, - GFP_KERNEL); - if (!ep->ib_window_map) - return -ENOMEM; - - ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows, - GFP_KERNEL); - if (!ep->ob_window_map) - return -ENOMEM; - - addr = devm_kcalloc(dev, pci->num_ob_windows, sizeof(phys_addr_t), - GFP_KERNEL); - if (!addr) - return -ENOMEM; - ep->outbound_addr = addr; - epc = devm_pci_epc_create(dev, &epc_ops); if (IS_ERR(epc)) { dev_err(dev, "Failed to create epc device\n"); @@ -724,28 +852,11 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) if (ret < 0) epc->max_functions = 1; - for (func_no = 0; func_no < epc->max_functions; func_no++) { - ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL); - if (!ep_func) - return -ENOMEM; - - ep_func->func_no = func_no; - ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no, - PCI_CAP_ID_MSI); - ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no, - PCI_CAP_ID_MSIX); - - list_add_tail(&ep_func->list, &ep->func_list); - } - - if (ep->ops->init) - ep->ops->init(ep); - ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, ep->page_size); if (ret < 0) { dev_err(dev, "Failed to initialize address space\n"); - goto err_ep_deinit; + return ret; } ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, @@ -756,36 +867,11 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) goto err_exit_epc_mem; } - ret = dw_pcie_edma_detect(pci); - if (ret) - goto err_free_epc_mem; - - if (ep->ops->get_features) { - epc_features = ep->ops->get_features(ep); - if (epc_features->core_init_notifier) - return 0; - } - - ret = dw_pcie_ep_init_complete(ep); - if (ret) - goto err_remove_edma; - return 0; -err_remove_edma: - dw_pcie_edma_remove(pci); - -err_free_epc_mem: - pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, - epc->mem->window.page_size); - err_exit_epc_mem: pci_epc_mem_exit(epc); -err_ep_deinit: - if (ep->ops->deinit) - ep->ops->deinit(ep); - return ret; } EXPORT_SYMBOL_GPL(dw_pcie_ep_init); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 778588b4be70..8490c5d6ff9f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -145,6 +145,17 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) pci->ep.ops = &pcie_ep_ops; ret = dw_pcie_ep_init(&pci->ep); + if (ret) + return ret; + + ret = dw_pcie_ep_init_registers(&pci->ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(&pci->ep); + } + + dw_pcie_ep_init_notify(&pci->ep); + break; default: dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 26dae4837462..f8e5431a207b 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -333,7 +333,6 @@ struct dw_pcie_rp { struct dw_pcie_ep_ops { void (*pre_init)(struct dw_pcie_ep *ep); void (*init)(struct dw_pcie_ep *ep); - void (*deinit)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, unsigned int type, u16 interrupt_num); const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); @@ -670,9 +669,10 @@ static inline void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, #ifdef CONFIG_PCIE_DW_EP void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); int dw_pcie_ep_init(struct dw_pcie_ep *ep); -int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep); +int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep); void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep); -void dw_pcie_ep_exit(struct dw_pcie_ep *ep); +void dw_pcie_ep_deinit(struct dw_pcie_ep *ep); +void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep); int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num); @@ -693,7 +693,7 @@ static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) return 0; } -static inline int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) +static inline int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) { return 0; } @@ -702,7 +702,11 @@ static inline void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) { } -static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +static inline void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) +{ +} + +static inline void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep) { } diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 5e8e54f597dd..98bbc83182b4 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -396,6 +396,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) struct keembay_pcie *pcie; struct dw_pcie *pci; enum dw_pcie_device_mode mode; + int ret; data = device_get_match_data(dev); if (!data) @@ -430,11 +431,26 @@ static int keembay_pcie_probe(struct platform_device *pdev) return -ENODEV; pci->ep.ops = &keembay_pcie_ep_ops; - return dw_pcie_ep_init(&pci->ep); + ret = dw_pcie_ep_init(&pci->ep); + if (ret) + return ret; + + ret = dw_pcie_ep_init_registers(&pci->ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(&pci->ep); + return ret; + } + + dw_pcie_ep_init_notify(&pci->ep); + + break; default: dev_err(dev, "Invalid device type %d\n", pcie->mode); return -ENODEV; } + + return 0; } static const struct keembay_pcie_of_data keembay_pcie_rc_of_data = { diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 36e5e80cd22f..2fb8c15e7a91 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -463,7 +463,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) PARF_INT_ALL_LINK_UP | PARF_INT_ALL_EDMA; writel_relaxed(val, pcie_ep->parf + PARF_INT_ALL_MASK); - ret = dw_pcie_ep_init_complete(&pcie_ep->pci.ep); + ret = dw_pcie_ep_init_registers(&pcie_ep->pci.ep); if (ret) { dev_err(dev, "Failed to complete initialization: %d\n", ret); goto err_disable_resources; @@ -507,6 +507,7 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci) return; } + dw_pcie_ep_cleanup(&pci->ep); qcom_pcie_disable_resources(pcie_ep); pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED; } @@ -774,7 +775,6 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) static const struct pci_epc_features qcom_pcie_epc_features = { .linkup_notifier = true, - .core_init_notifier = true, .msi_capable = true, .msix_capable = false, .align = SZ_4K, diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 0be760ed420b..cfeccc2f9ee1 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -352,11 +352,8 @@ static void rcar_gen4_pcie_ep_init(struct dw_pcie_ep *ep) dw_pcie_ep_reset_bar(pci, bar); } -static void rcar_gen4_pcie_ep_deinit(struct dw_pcie_ep *ep) +static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar) { - struct dw_pcie *dw = to_dw_pcie_from_ep(ep); - struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); - writel(0, rcar->base + PCIEDMAINTSTSEN); rcar_gen4_pcie_common_deinit(rcar); } @@ -410,7 +407,6 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, static const struct dw_pcie_ep_ops pcie_ep_ops = { .pre_init = rcar_gen4_pcie_ep_pre_init, .init = rcar_gen4_pcie_ep_init, - .deinit = rcar_gen4_pcie_ep_deinit, .raise_irq = rcar_gen4_pcie_ep_raise_irq, .get_features = rcar_gen4_pcie_ep_get_features, .get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset, @@ -420,18 +416,36 @@ static const struct dw_pcie_ep_ops pcie_ep_ops = { static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar) { struct dw_pcie_ep *ep = &rcar->dw.ep; + struct device *dev = rcar->dw.dev; + int ret; if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_EP)) return -ENODEV; ep->ops = &pcie_ep_ops; - return dw_pcie_ep_init(ep); + ret = dw_pcie_ep_init(ep); + if (ret) { + rcar_gen4_pcie_ep_deinit(rcar); + return ret; + } + + ret = dw_pcie_ep_init_registers(ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(ep); + rcar_gen4_pcie_ep_deinit(rcar); + } + + dw_pcie_ep_init_notify(ep); + + return ret; } static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar) { - dw_pcie_ep_exit(&rcar->dw.ep); + dw_pcie_ep_deinit(&rcar->dw.ep); + rcar_gen4_pcie_ep_deinit(rcar); } /* Common */ diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 1f7b662cb8e1..93f5433c5c55 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1715,6 +1715,8 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) if (ret) dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret); + dw_pcie_ep_cleanup(&pcie->pci.ep); + reset_control_assert(pcie->core_rst); tegra_pcie_disable_phy(pcie); @@ -1895,7 +1897,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) val = (upper_32_bits(ep->msi_mem_phys) & MSIX_ADDR_MATCH_HIGH_OFF_MASK); dw_pcie_writel_dbi(pci, MSIX_ADDR_MATCH_HIGH_OFF, val); - ret = dw_pcie_ep_init_complete(ep); + ret = dw_pcie_ep_init_registers(ep); if (ret) { dev_err(dev, "Failed to complete initialization: %d\n", ret); goto fail_init_complete; @@ -2004,7 +2006,6 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features tegra_pcie_epc_features = { .linkup_notifier = true, - .core_init_notifier = true, .msi_capable = false, .msix_capable = false, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, @@ -2273,11 +2274,14 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) ret = tegra_pcie_config_ep(pcie, pdev); if (ret < 0) goto fail; + else + return 0; break; default: dev_err(dev, "Invalid PCIe device type %d\n", pcie->of_data->mode); + ret = -EINVAL; } fail: diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index 639bc2e12476..a2b844268e28 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -399,7 +399,20 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev) return ret; priv->pci.ep.ops = &uniphier_pcie_ep_ops; - return dw_pcie_ep_init(&priv->pci.ep); + ret = dw_pcie_ep_init(&priv->pci.ep); + if (ret) + return ret; + + ret = dw_pcie_ep_init_registers(&priv->pci.ep); + if (ret) { + dev_err(dev, "Failed to initialize DWC endpoint registers\n"); + dw_pcie_ep_deinit(&priv->pci.ep); + return ret; + } + + dw_pcie_ep_init_notify(&priv->pci.ep); + + return 0; } static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { diff --git a/drivers/pci/controller/pcie-mt7621.c b/drivers/pci/controller/pcie-mt7621.c index 79e225edb42a..d97b956e6e57 100644 --- a/drivers/pci/controller/pcie-mt7621.c +++ b/drivers/pci/controller/pcie-mt7621.c @@ -202,7 +202,7 @@ static int mt7621_pcie_parse_port(struct mt7621_pcie *pcie, struct mt7621_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); - char name[10]; + char name[11]; int err; port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); diff --git a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c index 05967c6c0b42..047e2cef5afc 100644 --- a/drivers/pci/controller/pcie-rcar-ep.c +++ b/drivers/pci/controller/pcie-rcar-ep.c @@ -542,6 +542,8 @@ static int rcar_pcie_ep_probe(struct platform_device *pdev) goto err_pm_put; } + pci_epc_init_notify(epc); + return 0; err_pm_put: diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index c9046e97a1d2..136274533656 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -98,10 +98,8 @@ static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, /* All functions share the same vendor ID with function 0 */ if (fn == 0) { - u32 vid_regs = (hdr->vendorid & GENMASK(15, 0)) | - (hdr->subsys_vendor_id & GENMASK(31, 16)) << 16; - - rockchip_pcie_write(rockchip, vid_regs, + rockchip_pcie_write(rockchip, + hdr->vendorid | hdr->subsys_vendor_id << 16, PCIE_CORE_CONFIG_VENDOR); } @@ -153,7 +151,7 @@ static int rockchip_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS; } else { bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); - bool is_64bits = sz > SZ_2G; + bool is_64bits = !!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64); if (is_64bits && (bar & 1)) return -EINVAL; @@ -609,6 +607,8 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) rockchip_pcie_write(rockchip, PCIE_CLIENT_CONF_ENABLE, PCIE_CLIENT_CONFIG); + pci_epc_init_notify(epc); + return 0; err_epc_mem_exit: pci_epc_mem_exit(epc); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index e3aab5edaf70..652d63df9d22 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -383,11 +383,13 @@ static void pci_doe_task_complete(struct pci_doe_task *task) complete(task->private); } -static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 *index, u16 *vid, +static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u16 *vid, u8 *protocol) { u32 request_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, - *index); + *index) | + FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_REQ_3_VER, + (capver >= 2) ? 2 : 0); __le32 request_pl_le = cpu_to_le32(request_pl); __le32 response_pl_le; u32 response_pl; @@ -421,13 +423,17 @@ static int pci_doe_cache_protocols(struct pci_doe_mb *doe_mb) { u8 index = 0; u8 xa_idx = 0; + u32 hdr = 0; + + pci_read_config_dword(doe_mb->pdev, doe_mb->cap_offset, &hdr); do { int rc; u16 vid; u8 prot; - rc = pci_doe_discovery(doe_mb, &index, &vid, &prot); + rc = pci_doe_discovery(doe_mb, PCI_EXT_CAP_VER(hdr), &index, + &vid, &prot); if (rc) return rc; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index cd4ffb39dcdc..977fb79c1567 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -690,50 +690,35 @@ static void pci_epf_test_unbind(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; - struct pci_epf_bar *epf_bar; int bar; cancel_delayed_work(&epf_test->cmd_handler); pci_epf_test_clean_dma_chan(epf_test); for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { - epf_bar = &epf->bar[bar]; + if (!epf_test->reg[bar]) + continue; - if (epf_test->reg[bar]) { - pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, - epf_bar); - pci_epf_free_space(epf, epf_test->reg[bar], bar, - PRIMARY_INTERFACE); - } + pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, + &epf->bar[bar]); + pci_epf_free_space(epf, epf_test->reg[bar], bar, + PRIMARY_INTERFACE); } } static int pci_epf_test_set_bar(struct pci_epf *epf) { - int bar, add; - int ret; - struct pci_epf_bar *epf_bar; + int bar, ret; struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; - const struct pci_epc_features *epc_features; - - epc_features = epf_test->epc_features; - for (bar = 0; bar < PCI_STD_NUM_BARS; bar += add) { - epf_bar = &epf->bar[bar]; - /* - * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 - * if the specific implementation required a 64-bit BAR, - * even if we only requested a 32-bit BAR. - */ - add = (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ? 2 : 1; - - if (epc_features->bar[bar].type == BAR_RESERVED) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!epf_test->reg[bar]) continue; ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, - epf_bar); + &epf->bar[bar]); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar, PRIMARY_INTERFACE); @@ -753,6 +738,7 @@ static int pci_epf_test_core_init(struct pci_epf *epf) const struct pci_epc_features *epc_features; struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; + bool linkup_notifier = false; bool msix_capable = false; bool msi_capable = true; int ret; @@ -795,6 +781,10 @@ static int pci_epf_test_core_init(struct pci_epf *epf) } } + linkup_notifier = epc_features->linkup_notifier; + if (!linkup_notifier) + queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); + return 0; } @@ -817,14 +807,13 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct device *dev = &epf->dev; - struct pci_epf_bar *epf_bar; size_t msix_table_size = 0; size_t test_reg_bar_size; size_t pba_size = 0; bool msix_capable; void *base; - int bar, add; enum pci_barno test_reg_bar = epf_test->test_reg_bar; + enum pci_barno bar; const struct pci_epc_features *epc_features; size_t test_reg_size; @@ -849,16 +838,14 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) } epf_test->reg[test_reg_bar] = base; - for (bar = 0; bar < PCI_STD_NUM_BARS; bar += add) { - epf_bar = &epf->bar[bar]; - add = (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ? 2 : 1; + for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) { + bar = pci_epc_get_next_free_bar(epc_features, bar); + if (bar == NO_BAR) + break; if (bar == test_reg_bar) continue; - if (epc_features->bar[bar].type == BAR_RESERVED) - continue; - base = pci_epf_alloc_space(epf, bar_size[bar], bar, epc_features, PRIMARY_INTERFACE); if (!base) @@ -870,19 +857,6 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) return 0; } -static void pci_epf_configure_bar(struct pci_epf *epf, - const struct pci_epc_features *epc_features) -{ - struct pci_epf_bar *epf_bar; - int i; - - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - epf_bar = &epf->bar[i]; - if (epc_features->bar[i].only_64bit) - epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - } -} - static int pci_epf_test_bind(struct pci_epf *epf) { int ret; @@ -890,8 +864,6 @@ static int pci_epf_test_bind(struct pci_epf *epf) const struct pci_epc_features *epc_features; enum pci_barno test_reg_bar = BAR_0; struct pci_epc *epc = epf->epc; - bool linkup_notifier = false; - bool core_init_notifier = false; if (WARN_ON_ONCE(!epc)) return -EINVAL; @@ -902,12 +874,9 @@ static int pci_epf_test_bind(struct pci_epf *epf) return -EOPNOTSUPP; } - linkup_notifier = epc_features->linkup_notifier; - core_init_notifier = epc_features->core_init_notifier; test_reg_bar = pci_epc_get_first_free_bar(epc_features); if (test_reg_bar < 0) return -EINVAL; - pci_epf_configure_bar(epf, epc_features); epf_test->test_reg_bar = test_reg_bar; epf_test->epc_features = epc_features; @@ -916,21 +885,12 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (ret) return ret; - if (!core_init_notifier) { - ret = pci_epf_test_core_init(epf); - if (ret) - return ret; - } - epf_test->dma_supported = true; ret = pci_epf_test_init_dma_chan(epf_test); if (ret) epf_test->dma_supported = false; - if (!linkup_notifier && !core_init_notifier) - queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); - return 0; } diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 0ea64e24ed61..3b21e28f9b59 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -64,6 +64,9 @@ static int pci_secondary_epc_epf_link(struct config_item *epf_item, return ret; } + /* Send any pending EPC initialization complete to the EPF driver */ + pci_epc_notify_pending_init(epc, epf); + return 0; } @@ -125,6 +128,9 @@ static int pci_primary_epc_epf_link(struct config_item *epf_item, return ret; } + /* Send any pending EPC initialization complete to the EPF driver */ + pci_epc_notify_pending_init(epc, epf); + return 0; } @@ -230,6 +236,9 @@ static int pci_epc_epf_link(struct config_item *epc_item, return ret; } + /* Send any pending EPC initialization complete to the EPF driver */ + pci_epc_notify_pending_init(epc, epf); + return 0; } diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index da3fc0795b0b..47d27ec7439d 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -748,11 +748,33 @@ void pci_epc_init_notify(struct pci_epc *epc) epf->event_ops->core_init(epf); mutex_unlock(&epf->lock); } + epc->init_complete = true; mutex_unlock(&epc->list_lock); } EXPORT_SYMBOL_GPL(pci_epc_init_notify); /** + * pci_epc_notify_pending_init() - Notify the pending EPC device initialization + * complete to the EPF device + * @epc: the EPC device whose core initialization is pending to be notified + * @epf: the EPF device to be notified + * + * Invoke to notify the pending EPC device initialization complete to the EPF + * device. This is used to deliver the notification if the EPC initialization + * got completed before the EPF driver bind. + */ +void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf) +{ + if (epc->init_complete) { + mutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->core_init) + epf->event_ops->core_init(epf); + mutex_unlock(&epf->lock); + } +} +EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init); + +/** * pci_epc_bme_notify() - Notify the EPF device that the EPC device has received * the BME event from the Root complex * @epc: the EPC device that received the BME event diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 0a28a0b0911b..323f2a60ab16 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -255,6 +255,8 @@ EXPORT_SYMBOL_GPL(pci_epf_free_space); * @type: Identifies if the allocation is for primary EPC or secondary EPC * * Invoke to allocate memory for the PCI EPF register space. + * Flag PCI_BASE_ADDRESS_MEM_TYPE_64 will automatically get set if the BAR + * can only be a 64-bit BAR, or if the requested size is larger than 2 GB. */ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, const struct pci_epc_features *epc_features, @@ -304,9 +306,10 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, epf_bar[bar].addr = space; epf_bar[bar].size = size; epf_bar[bar].barno = bar; - epf_bar[bar].flags |= upper_32_bits(size) ? - PCI_BASE_ADDRESS_MEM_TYPE_64 : - PCI_BASE_ADDRESS_MEM_TYPE_32; + if (upper_32_bits(size) || epc_features->bar[bar].only_64bit) + epf_bar[bar].flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + else + epf_bar[bar].flags |= PCI_BASE_ADDRESS_MEM_TYPE_32; return space; } diff --git a/drivers/pci/hotplug/TODO b/drivers/pci/hotplug/TODO index fdb8dd6ea24d..9d428b0ea524 100644 --- a/drivers/pci/hotplug/TODO +++ b/drivers/pci/hotplug/TODO @@ -6,6 +6,8 @@ cpcihp: ->set_power callbacks in struct cpci_hp_controller_ops. Why were they introduced? Can they be removed from the struct? +* Returned code from pci_hp_add_bridge() is not checked. + cpqphp: * The driver spawns a kthread cpqhp_event_thread() which is woken by the @@ -16,6 +18,8 @@ cpqphp: * A large portion of cpqphp_ctrl.c and cpqphp_pci.c concerns resource management. Doesn't this duplicate functionality in the core? +* Returned code from pci_hp_add_bridge() is not checked. + ibmphp: * Implementations of hotplug_slot_ops callbacks such as get_adapter_present() @@ -43,13 +47,7 @@ ibmphp: * A large portion of ibmphp_res.c and ibmphp_pci.c concerns resource management. Doesn't this duplicate functionality in the core? -sgi_hotplug: - -* Several functions access the pci_slot member in struct hotplug_slot even - though pci_hotplug.h declares it private. See sn_hp_destroy() for an - example. Either the pci_slot member should no longer be declared private - or sgi_hotplug should store a pointer to it in struct slot. Probably the - former. +* Returned code from pci_hp_add_bridge() is not checked. shpchp: diff --git a/drivers/pci/msi/api.c b/drivers/pci/msi/api.c index be679aa5db64..b956ce591f96 100644 --- a/drivers/pci/msi/api.c +++ b/drivers/pci/msi/api.c @@ -213,8 +213,8 @@ EXPORT_SYMBOL(pci_disable_msix); * * %PCI_IRQ_MSIX Allow trying MSI-X vector allocations * * %PCI_IRQ_MSI Allow trying MSI vector allocations * - * * %PCI_IRQ_LEGACY Allow trying legacy INTx interrupts, if - * and only if @min_vecs == 1 + * * %PCI_IRQ_INTX Allow trying INTx interrupts, if and + * only if @min_vecs == 1 * * * %PCI_IRQ_AFFINITY Auto-manage IRQs affinity by spreading * the vectors around available CPUs @@ -279,8 +279,8 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, return nvecs; } - /* use legacy IRQ if allowed */ - if (flags & PCI_IRQ_LEGACY) { + /* use INTx IRQ if allowed */ + if (flags & PCI_IRQ_INTX) { if (min_vecs == 1 && dev->irq) { /* * Invoke the affinity spreading logic to ensure that @@ -366,56 +366,6 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) EXPORT_SYMBOL(pci_irq_get_affinity); /** - * pci_ims_alloc_irq - Allocate an interrupt on a PCI/IMS interrupt domain - * @dev: The PCI device to operate on - * @icookie: Pointer to an IMS implementation specific cookie for this - * IMS instance (PASID, queue ID, pointer...). - * The cookie content is copied into the MSI descriptor for the - * interrupt chip callbacks or domain specific setup functions. - * @affdesc: Optional pointer to an interrupt affinity descriptor - * - * There is no index for IMS allocations as IMS is an implementation - * specific storage and does not have any direct associations between - * index, which might be a pure software construct, and device - * functionality. This association is established by the driver either via - * the index - if there is a hardware table - or in case of purely software - * managed IMS implementation the association happens via the - * irq_write_msi_msg() callback of the implementation specific interrupt - * chip, which utilizes the provided @icookie to store the MSI message in - * the appropriate place. - * - * Return: A struct msi_map - * - * On success msi_map::index contains the allocated index (>= 0) and - * msi_map::virq the allocated Linux interrupt number (> 0). - * - * On fail msi_map::index contains the error code and msi_map::virq - * is set to 0. - */ -struct msi_map pci_ims_alloc_irq(struct pci_dev *dev, union msi_instance_cookie *icookie, - const struct irq_affinity_desc *affdesc) -{ - return msi_domain_alloc_irq_at(&dev->dev, MSI_SECONDARY_DOMAIN, MSI_ANY_INDEX, - affdesc, icookie); -} -EXPORT_SYMBOL_GPL(pci_ims_alloc_irq); - -/** - * pci_ims_free_irq - Allocate an interrupt on a PCI/IMS interrupt domain - * which was allocated via pci_ims_alloc_irq() - * @dev: The PCI device to operate on - * @map: A struct msi_map describing the interrupt to free as - * returned from pci_ims_alloc_irq() - */ -void pci_ims_free_irq(struct pci_dev *dev, struct msi_map map) -{ - if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0)) - return; - msi_domain_free_irqs_range(&dev->dev, MSI_SECONDARY_DOMAIN, map.index, map.index); -} -EXPORT_SYMBOL_GPL(pci_ims_free_irq); - -/** * pci_free_irq_vectors() - Free previously allocated IRQs for a device * @dev: the PCI device to operate on * diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index cfd84a899c82..03d2dd25790d 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -355,65 +355,6 @@ bool pci_msi_domain_supports(struct pci_dev *pdev, unsigned int feature_mask, return (supported & feature_mask) == feature_mask; } -/** - * pci_create_ims_domain - Create a secondary IMS domain for a PCI device - * @pdev: The PCI device to operate on - * @template: The MSI info template which describes the domain - * @hwsize: The size of the hardware entry table or 0 if the domain - * is purely software managed - * @data: Optional pointer to domain specific data to be stored - * in msi_domain_info::data - * - * Return: True on success, false otherwise - * - * An IMS domain is expected to have the following constraints: - * - The index space is managed by the core code - * - * - There is no requirement for consecutive index ranges - * - * - The interrupt chip must provide the following callbacks: - * - irq_mask() - * - irq_unmask() - * - irq_write_msi_msg() - * - * - The interrupt chip must provide the following optional callbacks - * when the irq_mask(), irq_unmask() and irq_write_msi_msg() callbacks - * cannot operate directly on hardware, e.g. in the case that the - * interrupt message store is in queue memory: - * - irq_bus_lock() - * - irq_bus_unlock() - * - * These callbacks are invoked from preemptible task context and are - * allowed to sleep. In this case the mandatory callbacks above just - * store the information. The irq_bus_unlock() callback is supposed - * to make the change effective before returning. - * - * - Interrupt affinity setting is handled by the underlying parent - * interrupt domain and communicated to the IMS domain via - * irq_write_msi_msg(). - * - * The domain is automatically destroyed when the PCI device is removed. - */ -bool pci_create_ims_domain(struct pci_dev *pdev, const struct msi_domain_template *template, - unsigned int hwsize, void *data) -{ - struct irq_domain *domain = dev_get_msi_domain(&pdev->dev); - - if (!domain || !irq_domain_is_msi_parent(domain)) - return false; - - if (template->info.bus_token != DOMAIN_BUS_PCI_DEVICE_IMS || - !(template->info.flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS) || - !(template->info.flags & MSI_FLAG_FREE_MSI_DESCS) || - !template->chip.irq_mask || !template->chip.irq_unmask || - !template->chip.irq_write_msi_msg || template->chip.irq_set_affinity) - return false; - - return msi_create_device_irq_domain(&pdev->dev, MSI_SECONDARY_DOMAIN, template, - hwsize, data, NULL); -} -EXPORT_SYMBOL_GPL(pci_create_ims_domain); - /* * Users of the generic MSI infrastructure expect a device to have a single ID, * so with DMA aliases we have to pick the least-worst compromise. Devices with diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 682fa877478f..c5625dd9bf49 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -86,9 +86,11 @@ static int pcim_setup_msi_release(struct pci_dev *dev) return 0; ret = devm_add_action(&dev->dev, pcim_msi_release, dev); - if (!ret) - dev->is_msi_managed = true; - return ret; + if (ret) + return ret; + + dev->is_msi_managed = true; + return 0; } /* @@ -99,9 +101,10 @@ static int pci_setup_msi_context(struct pci_dev *dev) { int ret = msi_setup_device_data(&dev->dev); - if (!ret) - ret = pcim_setup_msi_release(dev); - return ret; + if (ret) + return ret; + + return pcim_setup_msi_release(dev); } /* diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index c2c7334152bc..03539e505372 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -238,6 +238,8 @@ static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs, return 0; int_map = kcalloc(map_sz, sizeof(u32), GFP_KERNEL); + if (!int_map) + return -ENOMEM; mapp = int_map; list_for_each_entry(child, &pdev->subordinate->devices, bus_list) { diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e5f243dd4288..59e0949fb079 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -142,8 +142,8 @@ enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT; * the dfl or actual value as it sees fit. Don't forget this is * measured in 32-bit words, not bytes. */ -u8 pci_dfl_cache_line_size = L1_CACHE_BYTES >> 2; -u8 pci_cache_line_size; +u8 pci_dfl_cache_line_size __ro_after_init = L1_CACHE_BYTES >> 2; +u8 pci_cache_line_size __ro_after_init ; /* * If we set up a device for bus mastering, we need to check the latency @@ -1277,6 +1277,11 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) for (;;) { u32 id; + if (pci_dev_is_disconnected(dev)) { + pci_dbg(dev, "disconnected; not waiting\n"); + return -ENOTTY; + } + pci_read_config_dword(dev, PCI_COMMAND, &id); if (!PCI_POSSIBLE_ERROR(id)) break; @@ -2111,20 +2116,6 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) } /** - * pci_enable_device_io - Initialize a device for use with IO space - * @dev: PCI device to be initialized - * - * Initialize device before it's used by a driver. Ask low-level code - * to enable I/O resources. Wake up the device if it was suspended. - * Beware, this function can fail. - */ -int pci_enable_device_io(struct pci_dev *dev) -{ - return pci_enable_device_flags(dev, IORESOURCE_IO); -} -EXPORT_SYMBOL(pci_enable_device_io); - -/** * pci_enable_device_mem - Initialize a device for use with Memory space * @dev: PCI device to be initialized * @@ -2962,6 +2953,18 @@ static const struct dmi_system_id bridge_d3_blacklist[] = { DMI_MATCH(DMI_BOARD_VERSION, "Continental Z2"), }, }, + { + /* + * Changing power state of root port dGPU is connected fails + * https://gitlab.freedesktop.org/drm/amd/-/issues/3229 + */ + .ident = "Hewlett-Packard HP Pavilion 17 Notebook PC/1972", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_BOARD_NAME, "1972"), + DMI_MATCH(DMI_BOARD_VERSION, "95.33"), + }, + }, #endif { } }; @@ -4625,11 +4628,12 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt) /* * Ensure the updated LNKCTL parameters are used during link - * training by checking that there is no ongoing link training to - * avoid LTSSM race as recommended in Implementation Note at the - * end of PCIe r6.0.1 sec 7.5.3.7. + * training by checking that there is no ongoing link training that + * may have started before link parameters were changed, so as to + * avoid LTSSM race as recommended in Implementation Note at the end + * of PCIe r6.1 sec 7.5.3.7. */ - rc = pcie_wait_for_link_status(pdev, use_lt, !use_lt); + rc = pcie_wait_for_link_status(pdev, true, false); if (rc) return rc; @@ -4879,6 +4883,7 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { + lock_map_assert_held(&dev->cfg_access_lock); pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); @@ -4927,16 +4932,96 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe) return pci_reset_hotplug_slot(dev->slot->hotplug, probe); } +static u16 cxl_port_dvsec(struct pci_dev *dev) +{ + return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL, + PCI_DVSEC_CXL_PORT); +} + +static bool cxl_sbr_masked(struct pci_dev *dev) +{ + u16 dvsec, reg; + int rc; + + dvsec = cxl_port_dvsec(dev); + if (!dvsec) + return false; + + rc = pci_read_config_word(dev, dvsec + PCI_DVSEC_CXL_PORT_CTL, ®); + if (rc || PCI_POSSIBLE_ERROR(reg)) + return false; + + /* + * Per CXL spec r3.1, sec 8.1.5.2, when "Unmask SBR" is 0, the SBR + * bit in Bridge Control has no effect. When 1, the Port generates + * hot reset when the SBR bit is set to 1. + */ + if (reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR) + return false; + + return true; +} + static int pci_reset_bus_function(struct pci_dev *dev, bool probe) { + struct pci_dev *bridge = pci_upstream_bridge(dev); int rc; + /* + * If "dev" is below a CXL port that has SBR control masked, SBR + * won't do anything, so return error. + */ + if (bridge && cxl_sbr_masked(bridge)) { + if (probe) + return 0; + + return -ENOTTY; + } + rc = pci_dev_reset_slot_function(dev, probe); if (rc != -ENOTTY) return rc; return pci_parent_bus_reset(dev, probe); } +static int cxl_reset_bus_function(struct pci_dev *dev, bool probe) +{ + struct pci_dev *bridge; + u16 dvsec, reg, val; + int rc; + + bridge = pci_upstream_bridge(dev); + if (!bridge) + return -ENOTTY; + + dvsec = cxl_port_dvsec(bridge); + if (!dvsec) + return -ENOTTY; + + if (probe) + return 0; + + rc = pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, ®); + if (rc) + return -ENOTTY; + + if (reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR) { + val = reg; + } else { + val = reg | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR; + pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, + val); + } + + rc = pci_reset_bus_function(dev, probe); + + if (reg != val) + pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, + reg); + + return rc; +} + void pci_dev_lock(struct pci_dev *dev) { /* block PM suspend, driver probe, etc. */ @@ -5021,6 +5106,7 @@ static const struct pci_reset_fn_method pci_reset_fn_methods[] = { { pci_af_flr, .name = "af_flr" }, { pci_pm_reset, .name = "pm" }, { pci_reset_bus_function, .name = "bus" }, + { cxl_reset_bus_function, .name = "cxl_bus" }, }; static ssize_t reset_method_show(struct device *dev, @@ -5245,11 +5331,20 @@ void pci_init_reset_methods(struct pci_dev *dev) */ int pci_reset_function(struct pci_dev *dev) { + struct pci_dev *bridge; int rc; if (!pci_reset_supported(dev)) return -ENOTTY; + /* + * If there's no upstream bridge, no locking is needed since there is + * no upstream bridge configuration to hold consistent. + */ + bridge = pci_upstream_bridge(dev); + if (bridge) + pci_dev_lock(bridge); + pci_dev_lock(dev); pci_dev_save_and_disable(dev); @@ -5258,6 +5353,9 @@ int pci_reset_function(struct pci_dev *dev) pci_dev_restore(dev); pci_dev_unlock(dev); + if (bridge) + pci_dev_unlock(bridge); + return rc; } EXPORT_SYMBOL_GPL(pci_reset_function); @@ -6065,8 +6163,9 @@ EXPORT_SYMBOL(pcie_get_width_cap); * and width, multiplying them, and applying encoding overhead. The result * is in Mb/s, i.e., megabits/second of raw bandwidth. */ -u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, - enum pcie_link_width *width) +static u32 pcie_bandwidth_capable(struct pci_dev *dev, + enum pci_bus_speed *speed, + enum pcie_link_width *width) { *speed = pcie_get_speed_cap(dev); *width = pcie_get_width_cap(dev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 17fed1846847..fd44565c4756 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -293,8 +293,6 @@ void pci_bus_put(struct pci_bus *bus); const char *pci_speed_string(enum pci_bus_speed speed); enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev); -u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, - enum pcie_link_width *width); void __pcie_print_link_status(struct pci_dev *dev, bool verbose); void pcie_report_downtraining(struct pci_dev *dev); void pcie_update_link_speed(struct pci_bus *bus, u16 link_status); diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 8999fcebde6a..17919b99fa66 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -47,7 +47,7 @@ config PCIEAER_INJECT error injection can fake almost all kinds of errors with the help of a user space helper tool aer-inject, which can be gotten from: - https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/ + https://github.com/intel/aer-inject.git config PCIEAER_CXL bool "PCI Express CXL RAS support" diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 2dab275d252f..f81b2303bf6a 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -6,7 +6,7 @@ * trigger various real hardware errors. Software based error * injection can fake almost all kinds of errors with the help of a * user space helper tool aer-inject, which can be gotten from: - * https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/ + * https://github.com/intel/aer-inject.git * * Copyright 2009 Intel Corporation. * Huang Ying <[email protected]> diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 47761c7ef267..cee2365e54b8 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -8,6 +8,8 @@ */ #include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/limits.h> #include <linux/math.h> @@ -189,21 +191,18 @@ void pci_restore_aspm_l1ss_state(struct pci_dev *pdev) #endif #define MODULE_PARAM_PREFIX "pcie_aspm." -/* Note: those are not register definitions */ -#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ -#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ -#define ASPM_STATE_L1 (4) /* L1 state */ -#define ASPM_STATE_L1_1 (8) /* ASPM L1.1 state */ -#define ASPM_STATE_L1_2 (0x10) /* ASPM L1.2 state */ -#define ASPM_STATE_L1_1_PCIPM (0x20) /* PCI PM L1.1 state */ -#define ASPM_STATE_L1_2_PCIPM (0x40) /* PCI PM L1.2 state */ -#define ASPM_STATE_L1_SS_PCIPM (ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM) -#define ASPM_STATE_L1_2_MASK (ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM) -#define ASPM_STATE_L1SS (ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\ - ASPM_STATE_L1_2_MASK) -#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) -#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \ - ASPM_STATE_L1SS) +/* Note: these are not register definitions */ +#define PCIE_LINK_STATE_L0S_UP BIT(0) /* Upstream direction L0s state */ +#define PCIE_LINK_STATE_L0S_DW BIT(1) /* Downstream direction L0s state */ +static_assert(PCIE_LINK_STATE_L0S == (PCIE_LINK_STATE_L0S_UP | PCIE_LINK_STATE_L0S_DW)); + +#define PCIE_LINK_STATE_L1_SS_PCIPM (PCIE_LINK_STATE_L1_1_PCIPM |\ + PCIE_LINK_STATE_L1_2_PCIPM) +#define PCIE_LINK_STATE_L1_2_MASK (PCIE_LINK_STATE_L1_2 |\ + PCIE_LINK_STATE_L1_2_PCIPM) +#define PCIE_LINK_STATE_L1SS (PCIE_LINK_STATE_L1_1 |\ + PCIE_LINK_STATE_L1_1_PCIPM |\ + PCIE_LINK_STATE_L1_2_MASK) struct pcie_link_state { struct pci_dev *pdev; /* Upstream component of the Link */ @@ -275,10 +274,10 @@ static int policy_to_aspm_state(struct pcie_link_state *link) return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ - return (ASPM_STATE_L0S | ASPM_STATE_L1); + return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; case POLICY_POWER_SUPERSAVE: /* Enable Everything */ - return ASPM_STATE_ALL; + return PCIE_LINK_STATE_ASPM_ALL; case POLICY_DEFAULT: return link->aspm_default; } @@ -581,14 +580,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) latency_dw_l1 = calc_l1_latency(lnkcap_dw); /* Check upstream direction L0s latency */ - if ((link->aspm_capable & ASPM_STATE_L0S_UP) && + if ((link->aspm_capable & PCIE_LINK_STATE_L0S_UP) && (latency_up_l0s > acceptable_l0s)) - link->aspm_capable &= ~ASPM_STATE_L0S_UP; + link->aspm_capable &= ~PCIE_LINK_STATE_L0S_UP; /* Check downstream direction L0s latency */ - if ((link->aspm_capable & ASPM_STATE_L0S_DW) && + if ((link->aspm_capable & PCIE_LINK_STATE_L0S_DW) && (latency_dw_l0s > acceptable_l0s)) - link->aspm_capable &= ~ASPM_STATE_L0S_DW; + link->aspm_capable &= ~PCIE_LINK_STATE_L0S_DW; /* * Check L1 latency. * Every switch on the path to root complex need 1 @@ -603,9 +602,9 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) * substate latencies (and hence do not do any check). */ latency = max_t(u32, latency_up_l1, latency_dw_l1); - if ((link->aspm_capable & ASPM_STATE_L1) && + if ((link->aspm_capable & PCIE_LINK_STATE_L1) && (latency + l1_switch_latency > acceptable_l1)) - link->aspm_capable &= ~ASPM_STATE_L1; + link->aspm_capable &= ~PCIE_LINK_STATE_L1; l1_switch_latency += NSEC_PER_USEC; link = link->parent; @@ -741,13 +740,13 @@ static void aspm_l1ss_init(struct pcie_link_state *link) child_l1ss_cap &= ~PCI_L1SS_CAP_ASPM_L1_2; if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1) - link->aspm_support |= ASPM_STATE_L1_1; + link->aspm_support |= PCIE_LINK_STATE_L1_1; if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2) - link->aspm_support |= ASPM_STATE_L1_2; + link->aspm_support |= PCIE_LINK_STATE_L1_2; if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1) - link->aspm_support |= ASPM_STATE_L1_1_PCIPM; + link->aspm_support |= PCIE_LINK_STATE_L1_1_PCIPM; if (parent_l1ss_cap & child_l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2) - link->aspm_support |= ASPM_STATE_L1_2_PCIPM; + link->aspm_support |= PCIE_LINK_STATE_L1_2_PCIPM; if (parent_l1ss_cap) pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, @@ -757,15 +756,15 @@ static void aspm_l1ss_init(struct pcie_link_state *link) &child_l1ss_ctl1); if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1) - link->aspm_enabled |= ASPM_STATE_L1_1; + link->aspm_enabled |= PCIE_LINK_STATE_L1_1; if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2) - link->aspm_enabled |= ASPM_STATE_L1_2; + link->aspm_enabled |= PCIE_LINK_STATE_L1_2; if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1) - link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM; + link->aspm_enabled |= PCIE_LINK_STATE_L1_1_PCIPM; if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2) - link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; + link->aspm_enabled |= PCIE_LINK_STATE_L1_2_PCIPM; - if (link->aspm_support & ASPM_STATE_L1_2_MASK) + if (link->aspm_support & PCIE_LINK_STATE_L1_2_MASK) aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap); } @@ -778,8 +777,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) if (blacklist) { /* Set enabled/disable so that we will disable ASPM later */ - link->aspm_enabled = ASPM_STATE_ALL; - link->aspm_disable = ASPM_STATE_ALL; + link->aspm_enabled = PCIE_LINK_STATE_ASPM_ALL; + link->aspm_disable = PCIE_LINK_STATE_ASPM_ALL; return; } @@ -814,19 +813,19 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) * support L0s. */ if (parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPM_L0S) - link->aspm_support |= ASPM_STATE_L0S; + link->aspm_support |= PCIE_LINK_STATE_L0S; if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) - link->aspm_enabled |= ASPM_STATE_L0S_UP; + link->aspm_enabled |= PCIE_LINK_STATE_L0S_UP; if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) - link->aspm_enabled |= ASPM_STATE_L0S_DW; + link->aspm_enabled |= PCIE_LINK_STATE_L0S_DW; /* Setup L1 state */ if (parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPM_L1) - link->aspm_support |= ASPM_STATE_L1; + link->aspm_support |= PCIE_LINK_STATE_L1; if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1) - link->aspm_enabled |= ASPM_STATE_L1; + link->aspm_enabled |= PCIE_LINK_STATE_L1; aspm_l1ss_init(link); @@ -876,7 +875,7 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) * If needed, disable L1, and it gets enabled later * in pcie_config_aspm_link(). */ - if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) { + if (enable_req & (PCIE_LINK_STATE_L1_1 | PCIE_LINK_STATE_L1_2)) { pcie_capability_clear_word(child, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_ASPM_L1); pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, @@ -884,13 +883,13 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) } val = 0; - if (state & ASPM_STATE_L1_1) + if (state & PCIE_LINK_STATE_L1_1) val |= PCI_L1SS_CTL1_ASPM_L1_1; - if (state & ASPM_STATE_L1_2) + if (state & PCIE_LINK_STATE_L1_2) val |= PCI_L1SS_CTL1_ASPM_L1_2; - if (state & ASPM_STATE_L1_1_PCIPM) + if (state & PCIE_LINK_STATE_L1_1_PCIPM) val |= PCI_L1SS_CTL1_PCIPM_L1_1; - if (state & ASPM_STATE_L1_2_PCIPM) + if (state & PCIE_LINK_STATE_L1_2_PCIPM) val |= PCI_L1SS_CTL1_PCIPM_L1_2; /* Enable what we need to enable */ @@ -916,29 +915,29 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) state &= (link->aspm_capable & ~link->aspm_disable); /* Can't enable any substates if L1 is not enabled */ - if (!(state & ASPM_STATE_L1)) - state &= ~ASPM_STATE_L1SS; + if (!(state & PCIE_LINK_STATE_L1)) + state &= ~PCIE_LINK_STATE_L1SS; /* Spec says both ports must be in D0 before enabling PCI PM substates*/ if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) { - state &= ~ASPM_STATE_L1_SS_PCIPM; - state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM); + state &= ~PCIE_LINK_STATE_L1_SS_PCIPM; + state |= (link->aspm_enabled & PCIE_LINK_STATE_L1_SS_PCIPM); } /* Nothing to do if the link is already in the requested state */ if (link->aspm_enabled == state) return; /* Convert ASPM state to upstream/downstream ASPM register state */ - if (state & ASPM_STATE_L0S_UP) + if (state & PCIE_LINK_STATE_L0S_UP) dwstream |= PCI_EXP_LNKCTL_ASPM_L0S; - if (state & ASPM_STATE_L0S_DW) + if (state & PCIE_LINK_STATE_L0S_DW) upstream |= PCI_EXP_LNKCTL_ASPM_L0S; - if (state & ASPM_STATE_L1) { + if (state & PCIE_LINK_STATE_L1) { upstream |= PCI_EXP_LNKCTL_ASPM_L1; dwstream |= PCI_EXP_LNKCTL_ASPM_L1; } - if (link->aspm_capable & ASPM_STATE_L1SS) + if (link->aspm_capable & PCIE_LINK_STATE_L1SS) pcie_config_aspm_l1ss(link, state); /* @@ -947,11 +946,11 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) * upstream component first and then downstream, and vice * versa for disabling ASPM L1. Spec doesn't mention L0S. */ - if (state & ASPM_STATE_L1) + if (state & PCIE_LINK_STATE_L1) pcie_config_aspm_dev(parent, upstream); list_for_each_entry(child, &linkbus->devices, bus_list) pcie_config_aspm_dev(child, dwstream); - if (!(state & ASPM_STATE_L1)) + if (!(state & PCIE_LINK_STATE_L1)) pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; @@ -1324,6 +1323,28 @@ static struct pcie_link_state *pcie_aspm_get_link(struct pci_dev *pdev) return bridge->link_state; } +static u8 pci_calc_aspm_disable_mask(int state) +{ + state &= ~PCIE_LINK_STATE_CLKPM; + + /* L1 PM substates require L1 */ + if (state & PCIE_LINK_STATE_L1) + state |= PCIE_LINK_STATE_L1SS; + + return state; +} + +static u8 pci_calc_aspm_enable_mask(int state) +{ + state &= ~PCIE_LINK_STATE_CLKPM; + + /* L1 PM substates require L1 */ + if (state & PCIE_LINK_STATE_L1SS) + state |= PCIE_LINK_STATE_L1; + + return state; +} + static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked) { struct pcie_link_state *link = pcie_aspm_get_link(pdev); @@ -1346,19 +1367,7 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked if (!locked) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - if (state & PCIE_LINK_STATE_L0S) - link->aspm_disable |= ASPM_STATE_L0S; - if (state & PCIE_LINK_STATE_L1) - /* L1 PM substates require L1 */ - link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS; - if (state & PCIE_LINK_STATE_L1_1) - link->aspm_disable |= ASPM_STATE_L1_1; - if (state & PCIE_LINK_STATE_L1_2) - link->aspm_disable |= ASPM_STATE_L1_2; - if (state & PCIE_LINK_STATE_L1_1_PCIPM) - link->aspm_disable |= ASPM_STATE_L1_1_PCIPM; - if (state & PCIE_LINK_STATE_L1_2_PCIPM) - link->aspm_disable |= ASPM_STATE_L1_2_PCIPM; + link->aspm_disable |= pci_calc_aspm_disable_mask(state); pcie_config_aspm_link(link, policy_to_aspm_state(link)); if (state & PCIE_LINK_STATE_CLKPM) @@ -1414,20 +1423,7 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) if (!locked) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - link->aspm_default = 0; - if (state & PCIE_LINK_STATE_L0S) - link->aspm_default |= ASPM_STATE_L0S; - if (state & PCIE_LINK_STATE_L1) - link->aspm_default |= ASPM_STATE_L1; - /* L1 PM substates require L1 */ - if (state & PCIE_LINK_STATE_L1_1) - link->aspm_default |= ASPM_STATE_L1_1 | ASPM_STATE_L1; - if (state & PCIE_LINK_STATE_L1_2) - link->aspm_default |= ASPM_STATE_L1_2 | ASPM_STATE_L1; - if (state & PCIE_LINK_STATE_L1_1_PCIPM) - link->aspm_default |= ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1; - if (state & PCIE_LINK_STATE_L1_2_PCIPM) - link->aspm_default |= ASPM_STATE_L1_2_PCIPM | ASPM_STATE_L1; + link->aspm_default = pci_calc_aspm_enable_mask(state); pcie_config_aspm_link(link, policy_to_aspm_state(link)); link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0; @@ -1563,12 +1559,12 @@ static ssize_t aspm_attr_store_common(struct device *dev, if (state_enable) { link->aspm_disable &= ~state; /* need to enable L1 for substates */ - if (state & ASPM_STATE_L1SS) - link->aspm_disable &= ~ASPM_STATE_L1; + if (state & PCIE_LINK_STATE_L1SS) + link->aspm_disable &= ~PCIE_LINK_STATE_L1; } else { link->aspm_disable |= state; - if (state & ASPM_STATE_L1) - link->aspm_disable |= ASPM_STATE_L1SS; + if (state & PCIE_LINK_STATE_L1) + link->aspm_disable |= PCIE_LINK_STATE_L1SS; } pcie_config_aspm_link(link, policy_to_aspm_state(link)); @@ -1582,12 +1578,12 @@ static ssize_t aspm_attr_store_common(struct device *dev, #define ASPM_ATTR(_f, _s) \ static ssize_t _f##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ -{ return aspm_attr_show_common(dev, attr, buf, ASPM_STATE_##_s); } \ +{ return aspm_attr_show_common(dev, attr, buf, PCIE_LINK_STATE_##_s); } \ \ static ssize_t _f##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t len) \ -{ return aspm_attr_store_common(dev, attr, buf, len, ASPM_STATE_##_s); } +{ return aspm_attr_store_common(dev, attr, buf, len, PCIE_LINK_STATE_##_s); } ASPM_ATTR(l0s_aspm, L0S) ASPM_ATTR(l1_aspm, L1) @@ -1654,12 +1650,12 @@ static umode_t aspm_ctrl_attrs_are_visible(struct kobject *kobj, struct pci_dev *pdev = to_pci_dev(dev); struct pcie_link_state *link = pcie_aspm_get_link(pdev); static const u8 aspm_state_map[] = { - ASPM_STATE_L0S, - ASPM_STATE_L1, - ASPM_STATE_L1_1, - ASPM_STATE_L1_2, - ASPM_STATE_L1_1_PCIPM, - ASPM_STATE_L1_2_PCIPM, + PCIE_LINK_STATE_L0S, + PCIE_LINK_STATE_L1, + PCIE_LINK_STATE_L1_1, + PCIE_LINK_STATE_L1_2, + PCIE_LINK_STATE_L1_1_PCIPM, + PCIE_LINK_STATE_L1_2_PCIPM, }; if (aspm_disabled || !link) diff --git a/drivers/pci/pcie/edr.c b/drivers/pci/pcie/edr.c index 5f4914d313a1..e86298dbbcff 100644 --- a/drivers/pci/pcie/edr.c +++ b/drivers/pci/pcie/edr.c @@ -32,10 +32,10 @@ static int acpi_enable_dpc(struct pci_dev *pdev) int status = 0; /* - * Behavior when calling unsupported _DSM functions is undefined, - * so check whether EDR_PORT_DPC_ENABLE_DSM is supported. + * Per PCI Firmware r3.3, sec 4.6.12, EDR_PORT_DPC_ENABLE_DSM is + * optional. Return success if it's not implemented. */ - if (!acpi_check_dsm(adev->handle, &pci_acpi_dsm_guid, 5, + if (!acpi_check_dsm(adev->handle, &pci_acpi_dsm_guid, 6, 1ULL << EDR_PORT_DPC_ENABLE_DSM)) return 0; @@ -46,12 +46,7 @@ static int acpi_enable_dpc(struct pci_dev *pdev) argv4.package.count = 1; argv4.package.elements = &req; - /* - * Per Downstream Port Containment Related Enhancements ECN to PCI - * Firmware Specification r3.2, sec 4.6.12, EDR_PORT_DPC_ENABLE_DSM is - * optional. Return success if it's not implemented. - */ - obj = acpi_evaluate_dsm(adev->handle, &pci_acpi_dsm_guid, 5, + obj = acpi_evaluate_dsm(adev->handle, &pci_acpi_dsm_guid, 6, EDR_PORT_DPC_ENABLE_DSM, &argv4); if (!obj) return 0; @@ -85,8 +80,9 @@ static struct pci_dev *acpi_dpc_port_get(struct pci_dev *pdev) u16 port; /* - * Behavior when calling unsupported _DSM functions is undefined, - * so check whether EDR_PORT_DPC_ENABLE_DSM is supported. + * If EDR_PORT_LOCATE_DSM is not implemented under the target of + * EDR, the target is the port that experienced the containment + * event (PCI Firmware r3.3, sec 4.6.13). */ if (!acpi_check_dsm(adev->handle, &pci_acpi_dsm_guid, 5, 1ULL << EDR_PORT_LOCATE_DSM)) @@ -104,6 +100,16 @@ static struct pci_dev *acpi_dpc_port_get(struct pci_dev *pdev) } /* + * Bit 31 represents the success/failure of the operation. If bit + * 31 is set, the operation failed. + */ + if (obj->integer.value & BIT(31)) { + ACPI_FREE(obj); + pci_err(pdev, "Locate Port _DSM failed\n"); + return NULL; + } + + /* * Firmware returns DPC port BDF details in following format: * 15:8 = bus * 7:3 = device diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 705893b5f7b0..31090770fffc 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -116,9 +116,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data) device_lock(&dev->dev); pdrv = dev->driver; - if (!pdrv || - !pdrv->err_handler || - !pdrv->err_handler->mmio_enabled) + if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->mmio_enabled) goto out; err_handler = pdrv->err_handler; @@ -137,9 +135,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data) device_lock(&dev->dev); pdrv = dev->driver; - if (!pdrv || - !pdrv->err_handler || - !pdrv->err_handler->slot_reset) + if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset) goto out; err_handler = pdrv->err_handler; @@ -158,9 +154,7 @@ static int report_resume(struct pci_dev *dev, void *data) device_lock(&dev->dev); pdrv = dev->driver; if (!pci_dev_set_io_state(dev, pci_channel_io_normal) || - !pdrv || - !pdrv->err_handler || - !pdrv->err_handler->resume) + !pdrv || !pdrv->err_handler || !pdrv->err_handler->resume) goto out; err_handler = pdrv->err_handler; diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 14a4b89a3b83..bb65dfe43409 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -187,15 +187,15 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) * interrupt. */ if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) - goto legacy_irq; + goto intx_irq; /* Try to use MSI-X or MSI if supported */ if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) return 0; -legacy_irq: - /* fall back to legacy IRQ */ - ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); +intx_irq: + /* fall back to INTX IRQ */ + ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); if (ret < 0) return -ENODEV; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 1325fbae2f28..8e696e547565 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -95,7 +95,7 @@ static void release_pcibus_dev(struct device *dev) kfree(pci_bus); } -static struct class pcibus_class = { +static const struct class pcibus_class = { .name = "pci_bus", .dev_release = &release_pcibus_dev, .dev_groups = pcibus_groups, @@ -1482,6 +1482,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, } out: + /* Clear errors in the Secondary Status Register */ + pci_write_config_word(dev, PCI_SEC_STATUS, 0xffff); + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl); pm_runtime_put(&dev->dev); @@ -2543,6 +2546,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; + lockdep_register_key(&dev->cfg_access_key); + lockdep_init_map(&dev->cfg_access_lock, dev_name(&dev->dev), + &dev->cfg_access_key, 0); dma_set_max_seg_size(&dev->dev, 65536); dma_set_seg_boundary(&dev->dev, 0xffffffff); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index eff7f5df08e2..568410e64ce6 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6253,3 +6253,23 @@ static void pci_fixup_d3cold_delay_1sec(struct pci_dev *pdev) pdev->d3cold_delay = 1000; } DECLARE_PCI_FIXUP_FINAL(0x5555, 0x0004, pci_fixup_d3cold_delay_1sec); + +#ifdef CONFIG_PCIEAER +static void pci_mask_replay_timer_timeout(struct pci_dev *pdev) +{ + struct pci_dev *parent = pci_upstream_bridge(pdev); + u32 val; + + if (!parent || !parent->aer_cap) + return; + + pci_info(parent, "mask Replay Timer Timeout Correctable Errors due to %s hardware defect", + pci_name(pdev)); + + pci_read_config_dword(parent, parent->aer_cap + PCI_ERR_COR_MASK, &val); + val |= PCI_ERR_COR_REP_TIMER; + pci_write_config_dword(parent, parent->aer_cap + PCI_ERR_COR_MASK, val); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout); +#endif diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index 308c9969642e..a1b742b1a735 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -345,7 +345,7 @@ static ssize_t cxl_pmu_event_sysfs_show(struct device *dev, /* For CXL spec defined events */ #define CXL_PMU_EVENT_CXL_ATTR(_name, _gid, _msk) \ - CXL_PMU_EVENT_ATTR(_name, PCI_DVSEC_VENDOR_ID_CXL, _gid, _msk) + CXL_PMU_EVENT_ATTR(_name, PCI_VENDOR_ID_CXL, _gid, _msk) static struct attribute *cxl_pmu_event_attrs[] = { CXL_PMU_EVENT_CXL_ATTR(clock_ticks, CXL_PMU_GID_CLOCK_TICKS, BIT(0)), diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 853958fb2c06..45aaaea14fb4 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -35,6 +35,12 @@ config PHY_FSL_IMX8M_PCIE Enable this to add support for the PCIE PHY as found on i.MX8M family of SOCs. +config PHY_FSL_SAMSUNG_HDMI_PHY + tristate "Samsung HDMI PHY support" + depends on OF && HAS_IOMEM && COMMON_CLK + help + Enable this to add support for the Samsung HDMI PHY in i.MX8MP. + endif config PHY_FSL_LYNX_28G diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index cedb328bc4d2..c4386bfdb853 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o +obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c new file mode 100644 index 000000000000..9048cdc760c2 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + * Copyright 2022 Pengutronix, Lucas Stach <[email protected]> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#define PHY_REG_00 0x00 +#define PHY_REG_01 0x04 +#define PHY_REG_02 0x08 +#define PHY_REG_08 0x20 +#define PHY_REG_09 0x24 +#define PHY_REG_10 0x28 +#define PHY_REG_11 0x2c + +#define PHY_REG_12 0x30 +#define REG12_CK_DIV_MASK GENMASK(5, 4) + +#define PHY_REG_13 0x34 +#define REG13_TG_CODE_LOW_MASK GENMASK(7, 0) + +#define PHY_REG_14 0x38 +#define REG14_TOL_MASK GENMASK(7, 4) +#define REG14_RP_CODE_MASK GENMASK(3, 1) +#define REG14_TG_CODE_HIGH_MASK GENMASK(0, 0) + +#define PHY_REG_15 0x3c +#define PHY_REG_16 0x40 +#define PHY_REG_17 0x44 +#define PHY_REG_18 0x48 +#define PHY_REG_19 0x4c +#define PHY_REG_20 0x50 + +#define PHY_REG_21 0x54 +#define REG21_SEL_TX_CK_INV BIT(7) +#define REG21_PMS_S_MASK GENMASK(3, 0) + +#define PHY_REG_22 0x58 +#define PHY_REG_23 0x5c +#define PHY_REG_24 0x60 +#define PHY_REG_25 0x64 +#define PHY_REG_26 0x68 +#define PHY_REG_27 0x6c +#define PHY_REG_28 0x70 +#define PHY_REG_29 0x74 +#define PHY_REG_30 0x78 +#define PHY_REG_31 0x7c +#define PHY_REG_32 0x80 + +/* + * REG33 does not match the ref manual. According to Sandor Yu from NXP, + * "There is a doc issue on the i.MX8MP latest RM" + * REG33 is being used per guidance from Sandor + */ + +#define PHY_REG_33 0x84 +#define REG33_MODE_SET_DONE BIT(7) +#define REG33_FIX_DA BIT(1) + +#define PHY_REG_34 0x88 +#define REG34_PHY_READY BIT(7) +#define REG34_PLL_LOCK BIT(6) +#define REG34_PHY_CLK_READY BIT(5) + +#define PHY_REG_35 0x8c +#define PHY_REG_36 0x90 +#define PHY_REG_37 0x94 +#define PHY_REG_38 0x98 +#define PHY_REG_39 0x9c +#define PHY_REG_40 0xa0 +#define PHY_REG_41 0xa4 +#define PHY_REG_42 0xa8 +#define PHY_REG_43 0xac +#define PHY_REG_44 0xb0 +#define PHY_REG_45 0xb4 +#define PHY_REG_46 0xb8 +#define PHY_REG_47 0xbc + +#define PHY_PLL_DIV_REGS_NUM 6 + +struct phy_config { + u32 pixclk; + u8 pll_div_regs[PHY_PLL_DIV_REGS_NUM]; +}; + +static const struct phy_config phy_pll_cfg[] = { + { + .pixclk = 22250000, + .pll_div_regs = { 0x4b, 0xf1, 0x89, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 23750000, + .pll_div_regs = { 0x50, 0xf1, 0x86, 0x85, 0x80, 0x40 }, + }, { + .pixclk = 24000000, + .pll_div_regs = { 0x50, 0xf0, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 24024000, + .pll_div_regs = { 0x50, 0xf1, 0x99, 0x02, 0x80, 0x40 }, + }, { + .pixclk = 25175000, + .pll_div_regs = { 0x54, 0xfc, 0xcc, 0x91, 0x80, 0x40 }, + }, { + .pixclk = 25200000, + .pll_div_regs = { 0x54, 0xf0, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 26750000, + .pll_div_regs = { 0x5a, 0xf2, 0x89, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 27000000, + .pll_div_regs = { 0x5a, 0xf0, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 27027000, + .pll_div_regs = { 0x5a, 0xf2, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 29500000, + .pll_div_regs = { 0x62, 0xf4, 0x95, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 30750000, + .pll_div_regs = { 0x66, 0xf4, 0x82, 0x01, 0x88, 0x45 }, + }, { + .pixclk = 30888000, + .pll_div_regs = { 0x66, 0xf4, 0x99, 0x18, 0x88, 0x45 }, + }, { + .pixclk = 33750000, + .pll_div_regs = { 0x70, 0xf4, 0x82, 0x01, 0x80, 0x40 }, + }, { + .pixclk = 35000000, + .pll_div_regs = { 0x58, 0xb8, 0x8b, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 36000000, + .pll_div_regs = { 0x5a, 0xb0, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 36036000, + .pll_div_regs = { 0x5a, 0xb2, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 40000000, + .pll_div_regs = { 0x64, 0xb0, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 43200000, + .pll_div_regs = { 0x5a, 0x90, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 43243200, + .pll_div_regs = { 0x5a, 0x92, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 44500000, + .pll_div_regs = { 0x5c, 0x92, 0x98, 0x11, 0x84, 0x41 }, + }, { + .pixclk = 47000000, + .pll_div_regs = { 0x62, 0x94, 0x95, 0x82, 0x80, 0x40 }, + }, { + .pixclk = 47500000, + .pll_div_regs = { 0x63, 0x96, 0xa1, 0x82, 0x80, 0x40 }, + }, { + .pixclk = 50349650, + .pll_div_regs = { 0x54, 0x7c, 0xc3, 0x8f, 0x80, 0x40 }, + }, { + .pixclk = 50400000, + .pll_div_regs = { 0x54, 0x70, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 53250000, + .pll_div_regs = { 0x58, 0x72, 0x84, 0x03, 0x82, 0x41 }, + }, { + .pixclk = 53500000, + .pll_div_regs = { 0x5a, 0x72, 0x89, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 54000000, + .pll_div_regs = { 0x5a, 0x70, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 54054000, + .pll_div_regs = { 0x5a, 0x72, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 59000000, + .pll_div_regs = { 0x62, 0x74, 0x95, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 59340659, + .pll_div_regs = { 0x62, 0x74, 0xdb, 0x52, 0x88, 0x47 }, + }, { + .pixclk = 59400000, + .pll_div_regs = { 0x63, 0x70, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 61500000, + .pll_div_regs = { 0x66, 0x74, 0x82, 0x01, 0x88, 0x45 }, + }, { + .pixclk = 63500000, + .pll_div_regs = { 0x69, 0x74, 0x89, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 67500000, + .pll_div_regs = { 0x54, 0x52, 0x87, 0x03, 0x80, 0x40 }, + }, { + .pixclk = 70000000, + .pll_div_regs = { 0x58, 0x58, 0x8b, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 72000000, + .pll_div_regs = { 0x5a, 0x50, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 72072000, + .pll_div_regs = { 0x5a, 0x52, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 74176000, + .pll_div_regs = { 0x5d, 0x58, 0xdb, 0xA2, 0x88, 0x41 }, + }, { + .pixclk = 74250000, + .pll_div_regs = { 0x5c, 0x52, 0x90, 0x0d, 0x84, 0x41 }, + }, { + .pixclk = 78500000, + .pll_div_regs = { 0x62, 0x54, 0x87, 0x01, 0x80, 0x40 }, + }, { + .pixclk = 80000000, + .pll_div_regs = { 0x64, 0x50, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 82000000, + .pll_div_regs = { 0x66, 0x54, 0x82, 0x01, 0x88, 0x45 }, + }, { + .pixclk = 82500000, + .pll_div_regs = { 0x67, 0x54, 0x88, 0x01, 0x90, 0x49 }, + }, { + .pixclk = 89000000, + .pll_div_regs = { 0x70, 0x54, 0x84, 0x83, 0x80, 0x40 }, + }, { + .pixclk = 90000000, + .pll_div_regs = { 0x70, 0x54, 0x82, 0x01, 0x80, 0x40 }, + }, { + .pixclk = 94000000, + .pll_div_regs = { 0x4e, 0x32, 0xa7, 0x10, 0x80, 0x40 }, + }, { + .pixclk = 95000000, + .pll_div_regs = { 0x50, 0x31, 0x86, 0x85, 0x80, 0x40 }, + }, { + .pixclk = 98901099, + .pll_div_regs = { 0x52, 0x3a, 0xdb, 0x4c, 0x88, 0x47 }, + }, { + .pixclk = 99000000, + .pll_div_regs = { 0x52, 0x32, 0x82, 0x01, 0x88, 0x47 }, + }, { + .pixclk = 100699300, + .pll_div_regs = { 0x54, 0x3c, 0xc3, 0x8f, 0x80, 0x40 }, + }, { + .pixclk = 100800000, + .pll_div_regs = { 0x54, 0x30, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 102500000, + .pll_div_regs = { 0x55, 0x32, 0x8c, 0x05, 0x90, 0x4b }, + }, { + .pixclk = 104750000, + .pll_div_regs = { 0x57, 0x32, 0x98, 0x07, 0x90, 0x49 }, + }, { + .pixclk = 106500000, + .pll_div_regs = { 0x58, 0x32, 0x84, 0x03, 0x82, 0x41 }, + }, { + .pixclk = 107000000, + .pll_div_regs = { 0x5a, 0x32, 0x89, 0x88, 0x80, 0x40 }, + }, { + .pixclk = 108000000, + .pll_div_regs = { 0x5a, 0x30, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 108108000, + .pll_div_regs = { 0x5a, 0x32, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 118000000, + .pll_div_regs = { 0x62, 0x34, 0x95, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 118800000, + .pll_div_regs = { 0x63, 0x30, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 123000000, + .pll_div_regs = { 0x66, 0x34, 0x82, 0x01, 0x88, 0x45 }, + }, { + .pixclk = 127000000, + .pll_div_regs = { 0x69, 0x34, 0x89, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 135000000, + .pll_div_regs = { 0x70, 0x34, 0x82, 0x01, 0x80, 0x40 }, + }, { + .pixclk = 135580000, + .pll_div_regs = { 0x71, 0x39, 0xe9, 0x82, 0x9c, 0x5b }, + }, { + .pixclk = 137520000, + .pll_div_regs = { 0x72, 0x38, 0x99, 0x10, 0x85, 0x41 }, + }, { + .pixclk = 138750000, + .pll_div_regs = { 0x73, 0x35, 0x88, 0x05, 0x90, 0x4d }, + }, { + .pixclk = 140000000, + .pll_div_regs = { 0x75, 0x36, 0xa7, 0x90, 0x80, 0x40 }, + }, { + .pixclk = 144000000, + .pll_div_regs = { 0x78, 0x30, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 148352000, + .pll_div_regs = { 0x7b, 0x35, 0xdb, 0x39, 0x90, 0x45 }, + }, { + .pixclk = 148500000, + .pll_div_regs = { 0x7b, 0x35, 0x84, 0x03, 0x90, 0x45 }, + }, { + .pixclk = 154000000, + .pll_div_regs = { 0x40, 0x18, 0x83, 0x01, 0x00, 0x40 }, + }, { + .pixclk = 157000000, + .pll_div_regs = { 0x41, 0x11, 0xa7, 0x14, 0x80, 0x40 }, + }, { + .pixclk = 160000000, + .pll_div_regs = { 0x42, 0x12, 0xa1, 0x20, 0x80, 0x40 }, + }, { + .pixclk = 162000000, + .pll_div_regs = { 0x43, 0x18, 0x8b, 0x08, 0x96, 0x55 }, + }, { + .pixclk = 164000000, + .pll_div_regs = { 0x45, 0x11, 0x83, 0x82, 0x90, 0x4b }, + }, { + .pixclk = 165000000, + .pll_div_regs = { 0x45, 0x11, 0x84, 0x81, 0x90, 0x4b }, + }, { + .pixclk = 180000000, + .pll_div_regs = { 0x4b, 0x10, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 185625000, + .pll_div_regs = { 0x4e, 0x12, 0x9a, 0x95, 0x80, 0x40 }, + }, { + .pixclk = 188000000, + .pll_div_regs = { 0x4e, 0x12, 0xa7, 0x10, 0x80, 0x40 }, + }, { + .pixclk = 198000000, + .pll_div_regs = { 0x52, 0x12, 0x82, 0x01, 0x88, 0x47 }, + }, { + .pixclk = 205000000, + .pll_div_regs = { 0x55, 0x12, 0x8c, 0x05, 0x90, 0x4b }, + }, { + .pixclk = 209500000, + .pll_div_regs = { 0x57, 0x12, 0x98, 0x07, 0x90, 0x49 }, + }, { + .pixclk = 213000000, + .pll_div_regs = { 0x58, 0x12, 0x84, 0x03, 0x82, 0x41 }, + }, { + .pixclk = 216000000, + .pll_div_regs = { 0x5a, 0x10, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 216216000, + .pll_div_regs = { 0x5a, 0x12, 0xfd, 0x0c, 0x80, 0x40 }, + }, { + .pixclk = 237600000, + .pll_div_regs = { 0x63, 0x10, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 254000000, + .pll_div_regs = { 0x69, 0x14, 0x89, 0x08, 0x80, 0x40 }, + }, { + .pixclk = 277500000, + .pll_div_regs = { 0x73, 0x15, 0x88, 0x05, 0x90, 0x4d }, + }, { + .pixclk = 288000000, + .pll_div_regs = { 0x78, 0x10, 0x00, 0x00, 0x80, 0x00 }, + }, { + .pixclk = 297000000, + .pll_div_regs = { 0x7b, 0x15, 0x84, 0x03, 0x90, 0x45 }, + }, +}; + +struct reg_settings { + u8 reg; + u8 val; +}; + +static const struct reg_settings common_phy_cfg[] = { + { PHY_REG_00, 0x00 }, { PHY_REG_01, 0xd1 }, + { PHY_REG_08, 0x4f }, { PHY_REG_09, 0x30 }, + { PHY_REG_10, 0x33 }, { PHY_REG_11, 0x65 }, + /* REG12 pixclk specific */ + /* REG13 pixclk specific */ + /* REG14 pixclk specific */ + { PHY_REG_15, 0x80 }, { PHY_REG_16, 0x6c }, + { PHY_REG_17, 0xf2 }, { PHY_REG_18, 0x67 }, + { PHY_REG_19, 0x00 }, { PHY_REG_20, 0x10 }, + /* REG21 pixclk specific */ + { PHY_REG_22, 0x30 }, { PHY_REG_23, 0x32 }, + { PHY_REG_24, 0x60 }, { PHY_REG_25, 0x8f }, + { PHY_REG_26, 0x00 }, { PHY_REG_27, 0x00 }, + { PHY_REG_28, 0x08 }, { PHY_REG_29, 0x00 }, + { PHY_REG_30, 0x00 }, { PHY_REG_31, 0x00 }, + { PHY_REG_32, 0x00 }, { PHY_REG_33, 0x80 }, + { PHY_REG_34, 0x00 }, { PHY_REG_35, 0x00 }, + { PHY_REG_36, 0x00 }, { PHY_REG_37, 0x00 }, + { PHY_REG_38, 0x00 }, { PHY_REG_39, 0x00 }, + { PHY_REG_40, 0x00 }, { PHY_REG_41, 0xe0 }, + { PHY_REG_42, 0x83 }, { PHY_REG_43, 0x0f }, + { PHY_REG_44, 0x3E }, { PHY_REG_45, 0xf8 }, + { PHY_REG_46, 0x00 }, { PHY_REG_47, 0x00 } +}; + +struct fsl_samsung_hdmi_phy { + struct device *dev; + void __iomem *regs; + struct clk *apbclk; + struct clk *refclk; + + /* clk provider */ + struct clk_hw hw; + const struct phy_config *cur_cfg; +}; + +static inline struct fsl_samsung_hdmi_phy * +to_fsl_samsung_hdmi_phy(struct clk_hw *hw) +{ + return container_of(hw, struct fsl_samsung_hdmi_phy, hw); +} + +static void +fsl_samsung_hdmi_phy_configure_pixclk(struct fsl_samsung_hdmi_phy *phy, + const struct phy_config *cfg) +{ + u8 div = 0x1; + + switch (cfg->pixclk) { + case 22250000 ... 33750000: + div = 0xf; + break; + case 35000000 ... 40000000: + div = 0xb; + break; + case 43200000 ... 47500000: + div = 0x9; + break; + case 50349650 ... 63500000: + div = 0x7; + break; + case 67500000 ... 90000000: + div = 0x5; + break; + case 94000000 ... 148500000: + div = 0x3; + break; + case 154000000 ... 297000000: + div = 0x1; + break; + } + + writeb(REG21_SEL_TX_CK_INV | FIELD_PREP(REG21_PMS_S_MASK, div), + phy->regs + PHY_REG_21); +} + +static void +fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy, + const struct phy_config *cfg) +{ + u32 pclk = cfg->pixclk; + u32 fld_tg_code; + u32 pclk_khz; + u8 div = 1; + + switch (cfg->pixclk) { + case 22250000 ... 47500000: + div = 1; + break; + case 50349650 ... 99000000: + div = 2; + break; + case 100699300 ... 198000000: + div = 4; + break; + case 205000000 ... 297000000: + div = 8; + break; + } + + writeb(FIELD_PREP(REG12_CK_DIV_MASK, ilog2(div)), phy->regs + PHY_REG_12); + + /* + * Calculation for the frequency lock detector target code (fld_tg_code) + * is based on reference manual register description of PHY_REG13 + * (13.10.3.1.14.2): + * 1st) Calculate int_pllclk which is determinded by FLD_CK_DIV + * 2nd) Increase resolution to avoid rounding issues + * 3th) Do the div (256 / Freq. of int_pllclk) * 24 + * 4th) Reduce the resolution and always round up since the NXP + * settings rounding up always too. TODO: Check if that is + * correct. + */ + pclk /= div; + pclk_khz = pclk / 1000; + fld_tg_code = 256 * 1000 * 1000 / pclk_khz * 24; + fld_tg_code = DIV_ROUND_UP(fld_tg_code, 1000); + + /* FLD_TOL and FLD_RP_CODE taken from downstream driver */ + writeb(FIELD_PREP(REG13_TG_CODE_LOW_MASK, fld_tg_code), + phy->regs + PHY_REG_13); + writeb(FIELD_PREP(REG14_TOL_MASK, 2) | + FIELD_PREP(REG14_RP_CODE_MASK, 2) | + FIELD_PREP(REG14_TG_CODE_HIGH_MASK, fld_tg_code >> 8), + phy->regs + PHY_REG_14); +} + +static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy, + const struct phy_config *cfg) +{ + int i, ret; + u8 val; + + /* HDMI PHY init */ + writeb(REG33_FIX_DA, phy->regs + PHY_REG_33); + + /* common PHY registers */ + for (i = 0; i < ARRAY_SIZE(common_phy_cfg); i++) + writeb(common_phy_cfg[i].val, phy->regs + common_phy_cfg[i].reg); + + /* set individual PLL registers PHY_REG2 ... PHY_REG7 */ + for (i = 0; i < PHY_PLL_DIV_REGS_NUM; i++) + writeb(cfg->pll_div_regs[i], phy->regs + PHY_REG_02 + i * 4); + + fsl_samsung_hdmi_phy_configure_pixclk(phy, cfg); + fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg); + + writeb(REG33_FIX_DA | REG33_MODE_SET_DONE, phy->regs + PHY_REG_33); + + ret = readb_poll_timeout(phy->regs + PHY_REG_34, val, + val & REG34_PLL_LOCK, 50, 20000); + if (ret) + dev_err(phy->dev, "PLL failed to lock\n"); + + return ret; +} + +static unsigned long phy_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw); + + if (!phy->cur_cfg) + return 74250000; + + return phy->cur_cfg->pixclk; +} + +static long phy_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate) +{ + int i; + + for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--) + if (phy_pll_cfg[i].pixclk <= rate) + return phy_pll_cfg[i].pixclk; + + return -EINVAL; +} + +static int phy_clk_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw); + int i; + + for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--) + if (phy_pll_cfg[i].pixclk <= rate) + break; + + if (i < 0) + return -EINVAL; + + phy->cur_cfg = &phy_pll_cfg[i]; + + return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg); +} + +static const struct clk_ops phy_clk_ops = { + .recalc_rate = phy_clk_recalc_rate, + .round_rate = phy_clk_round_rate, + .set_rate = phy_clk_set_rate, +}; + +static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy) +{ + struct device *dev = phy->dev; + struct device_node *np = dev->of_node; + struct clk_init_data init; + const char *parent_name; + struct clk *phyclk; + int ret; + + parent_name = __clk_get_name(phy->refclk); + + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + init.name = "hdmi_pclk"; + init.ops = &phy_clk_ops; + + phy->hw.init = &init; + + phyclk = devm_clk_register(dev, &phy->hw); + if (IS_ERR(phyclk)) + return dev_err_probe(dev, PTR_ERR(phyclk), + "failed to register clock\n"); + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, phyclk); + if (ret) + return dev_err_probe(dev, ret, + "failed to register clock provider\n"); + + return 0; +} + +static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev) +{ + struct fsl_samsung_hdmi_phy *phy; + int ret; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + platform_set_drvdata(pdev, phy); + phy->dev = &pdev->dev; + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->apbclk = devm_clk_get(phy->dev, "apb"); + if (IS_ERR(phy->apbclk)) + return dev_err_probe(phy->dev, PTR_ERR(phy->apbclk), + "failed to get apb clk\n"); + + phy->refclk = devm_clk_get(phy->dev, "ref"); + if (IS_ERR(phy->refclk)) + return dev_err_probe(phy->dev, PTR_ERR(phy->refclk), + "failed to get ref clk\n"); + + ret = clk_prepare_enable(phy->apbclk); + if (ret) { + dev_err(phy->dev, "failed to enable apbclk\n"); + return ret; + } + + pm_runtime_get_noresume(phy->dev); + pm_runtime_set_active(phy->dev); + pm_runtime_enable(phy->dev); + + ret = phy_clk_register(phy); + if (ret) { + dev_err(&pdev->dev, "register clk failed\n"); + goto register_clk_failed; + } + + pm_runtime_put(phy->dev); + + return 0; + +register_clk_failed: + clk_disable_unprepare(phy->apbclk); + + return ret; +} + +static void fsl_samsung_hdmi_phy_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); +} + +static int __maybe_unused fsl_samsung_hdmi_phy_suspend(struct device *dev) +{ + struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev); + + clk_disable_unprepare(phy->apbclk); + + return 0; +} + +static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev) +{ + struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev); + int ret = 0; + + ret = clk_prepare_enable(phy->apbclk); + if (ret) { + dev_err(phy->dev, "failed to enable apbclk\n"); + return ret; + } + + if (phy->cur_cfg) + ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg); + + return ret; + +} + +static DEFINE_RUNTIME_DEV_PM_OPS(fsl_samsung_hdmi_phy_pm_ops, + fsl_samsung_hdmi_phy_suspend, + fsl_samsung_hdmi_phy_resume, NULL); + +static const struct of_device_id fsl_samsung_hdmi_phy_of_match[] = { + { + .compatible = "fsl,imx8mp-hdmi-phy", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, fsl_samsung_hdmi_phy_of_match); + +static struct platform_driver fsl_samsung_hdmi_phy_driver = { + .probe = fsl_samsung_hdmi_phy_probe, + .remove_new = fsl_samsung_hdmi_phy_remove, + .driver = { + .name = "fsl-samsung-hdmi-phy", + .of_match_table = fsl_samsung_hdmi_phy_of_match, + .pm = pm_ptr(&fsl_samsung_hdmi_phy_pm_ops), + }, +}; +module_platform_driver(fsl_samsung_hdmi_phy_driver); + +MODULE_AUTHOR("Sandor Yu <[email protected]>"); +MODULE_DESCRIPTION("SAMSUNG HDMI 2.0 Transmitter PHY Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 3849b7c87d28..60e00057e8bc 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -13,6 +13,17 @@ config PHY_MTK_PCIE callback for PCIe GEN3 port, it supports software efuse initialization. +config PHY_MTK_XFI_TPHY + tristate "MediaTek 10GE SerDes XFI T-PHY driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say 'Y' here to add support for MediaTek XFI T-PHY driver. + The driver provides access to the Ethernet SerDes T-PHY supporting + 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes + via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet. + config PHY_MTK_TPHY tristate "MediaTek T-PHY Driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index f6e24a47e081..1b8088df71e8 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o +obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o diff --git a/drivers/phy/mediatek/phy-mtk-xfi-tphy.c b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c new file mode 100644 index 000000000000..1a0b7484f525 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MediaTek 10GE SerDes XFI T-PHY driver + * + * Copyright (c) 2024 Daniel Golle <[email protected]> + * Bc-bocun Chen <[email protected]> + * based on mtk_usxgmii.c and mtk_sgmii.c found in MediaTek's SDK (GPL-2.0) + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen <[email protected]> + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> + +#include "phy-mtk-io.h" + +#define MTK_XFI_TPHY_NUM_CLOCKS 2 + +#define REG_DIG_GLB_70 0x0070 +#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x)) +#define XTP_PCS_MODE_MASK GENMASK(17, 16) +#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x)) +#define XTP_PCS_RST_B BIT(15) +#define XTP_FRC_PCS_RST_B BIT(14) +#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12) +#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x)) +#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10) +#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x)) +#define XTP_FRC_PCS_PWD_ASYNC BIT(8) +#define XTP_PCS_UPDT BIT(4) +#define XTP_PCS_IN_FR_RG BIT(0) + +#define REG_DIG_GLB_F4 0x00f4 +#define XFI_DPHY_PCS_SEL BIT(0) +#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1) +#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0) +#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5) + +#define REG_DIG_LN_TRX_40 0x3040 +#define XTP_LN_FRC_TX_DATA_EN BIT(29) +#define XTP_LN_TX_DATA_EN BIT(28) + +#define REG_DIG_LN_TRX_B0 0x30b0 +#define XTP_LN_FRC_TX_MACCK_EN BIT(5) +#define XTP_LN_TX_MACCK_EN BIT(4) + +#define REG_ANA_GLB_D0 0x90d0 +#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1) +#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x)) +#define XTP_GLB_USXGMII_EN BIT(0) + +/** + * struct mtk_xfi_tphy - run-time data of the XFI phy instance + * @base: IO memory area to access phy registers. + * @dev: Kernel device used to output prefixed debug info. + * @reset: Reset control corresponding to the phy instance. + * @clocks: All clocks required for the phy to operate. + * @da_war: Enables work-around for 10GBase-R mode. + */ +struct mtk_xfi_tphy { + void __iomem *base; + struct device *dev; + struct reset_control *reset; + struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS]; + bool da_war; +}; + +/** + * mtk_xfi_tphy_setup() - Setup phy for specified interface mode. + * @xfi_tphy: XFI phy instance. + * @interface: Ethernet interface mode + * + * The setup function is the condensed result of combining the 5 functions which + * setup the phy in MediaTek's GPL licensed public SDK sources. They can be found + * in mtk_sgmii.c[1] as well as mtk_usxgmii.c[2]. + * + * Many magic values have been replaced by register and bit definitions, however, + * that has not been possible in all cases. While the vendor driver uses a + * sequence of 32-bit writes, here we try to only modify the actually required + * bits. + * + * [1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/b72d6cba92bf9e29fb035c03052fa1e86664a25b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c + * + * [2]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/dec96a1d9b82cdcda4a56453fd0b453d4cab4b85/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c + */ +static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy, + phy_interface_t interface) +{ + bool is_1g, is_2p5g, is_5g, is_10g, da_war, use_lynxi_pcs; + + /* shorthands for specific clock speeds depending on interface mode */ + is_1g = interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_SGMII; + is_2p5g = interface == PHY_INTERFACE_MODE_2500BASEX; + is_5g = interface == PHY_INTERFACE_MODE_5GBASER; + is_10g = interface == PHY_INTERFACE_MODE_10GBASER || + interface == PHY_INTERFACE_MODE_USXGMII; + + /* Is overriding 10GBase-R tuning value required? */ + da_war = xfi_tphy->da_war && (interface == PHY_INTERFACE_MODE_10GBASER); + + /* configure input mux to either + * - USXGMII PCS (64b/66b coding) for 5G/10G + * - LynxI PCS (8b/10b coding) for 1G/2.5G + */ + use_lynxi_pcs = is_1g || is_2p5g; + + dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface)); + + /* Setup PLL setting */ + mtk_phy_update_bits(xfi_tphy->base + 0x9024, 0x100000, is_10g ? 0x0 : 0x100000); + mtk_phy_update_bits(xfi_tphy->base + 0x2020, 0x202000, is_5g ? 0x202000 : 0x0); + mtk_phy_update_bits(xfi_tphy->base + 0x2030, 0x500, is_1g ? 0x0 : 0x500); + mtk_phy_update_bits(xfi_tphy->base + 0x2034, 0xa00, is_1g ? 0x0 : 0xa00); + mtk_phy_update_bits(xfi_tphy->base + 0x2040, 0x340000, is_1g ? 0x200000 : 0x140000); + + /* Setup RXFE BW setting */ + mtk_phy_update_bits(xfi_tphy->base + 0x50f0, 0xc10, is_1g ? 0x410 : is_5g ? 0x800 : 0x400); + mtk_phy_update_bits(xfi_tphy->base + 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000); + + /* Setup RX CDR setting */ + mtk_phy_update_bits(xfi_tphy->base + 0x506c, 0x30000, is_5g ? 0x0 : 0x30000); + mtk_phy_update_bits(xfi_tphy->base + 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000); + mtk_phy_update_bits(xfi_tphy->base + 0x5074, 0x180000, is_5g ? 0x180000 : 0x0); + mtk_phy_update_bits(xfi_tphy->base + 0x5078, 0xf000400, is_5g ? 0x8000000 : + 0x7000400); + mtk_phy_update_bits(xfi_tphy->base + 0x507c, 0x5000500, is_5g ? 0x4000400 : + 0x1000100); + mtk_phy_update_bits(xfi_tphy->base + 0x5080, 0x1410, is_1g ? 0x400 : is_5g ? 0x1010 : 0x0); + mtk_phy_update_bits(xfi_tphy->base + 0x5084, 0x30300, is_1g ? 0x30300 : + is_5g ? 0x30100 : + 0x100); + mtk_phy_update_bits(xfi_tphy->base + 0x5088, 0x60200, is_1g ? 0x20200 : + is_5g ? 0x40000 : + 0x20000); + + /* Setting RXFE adaptation range setting */ + mtk_phy_update_bits(xfi_tphy->base + 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000); + mtk_phy_update_bits(xfi_tphy->base + 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000); + mtk_phy_update_bits(xfi_tphy->base + 0x50ec, 0xa00, is_1g ? 0x200 : 0x800); + mtk_phy_update_bits(xfi_tphy->base + 0x50a8, 0xee0000, is_5g ? 0x800000 : + 0x6e0000); + mtk_phy_update_bits(xfi_tphy->base + 0x6004, 0x190000, is_5g ? 0x0 : 0x190000); + + if (is_10g) + writel(0x01423342, xfi_tphy->base + 0x00f8); + else if (is_5g) + writel(0x00a132a1, xfi_tphy->base + 0x00f8); + else if (is_2p5g) + writel(0x009c329c, xfi_tphy->base + 0x00f8); + else + writel(0x00fa32fa, xfi_tphy->base + 0x00f8); + + /* Force SGDT_OUT off and select PCS */ + mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_F4, + XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL, + XFI_DPHY_AD_SGDT_FRC_EN | + (use_lynxi_pcs ? XFI_DPHY_PCS_SEL_SGMII : + XFI_DPHY_PCS_SEL_USXGMII)); + + /* Force GLB_CKDET_OUT */ + mtk_phy_set_bits(xfi_tphy->base + 0x0030, 0xc00); + + /* Force AEQ on */ + writel(XTP_PCS_RX_EQ_IN_PROGRESS(2) | XTP_PCS_PWD_SYNC(2) | XTP_PCS_PWD_ASYNC(2), + xfi_tphy->base + REG_DIG_GLB_70); + + usleep_range(1, 5); + writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40); + + /* Setup TX DA default value */ + mtk_phy_update_bits(xfi_tphy->base + 0x30b0, 0x30, 0x20); + writel(0x00008a01, xfi_tphy->base + 0x3028); + writel(0x0000a884, xfi_tphy->base + 0x302c); + writel(0x00083002, xfi_tphy->base + 0x3024); + + /* Setup RG default value */ + if (use_lynxi_pcs) { + writel(0x00011110, xfi_tphy->base + 0x3010); + writel(0x40704000, xfi_tphy->base + 0x3048); + } else { + writel(0x00022220, xfi_tphy->base + 0x3010); + writel(0x0f020a01, xfi_tphy->base + 0x5064); + writel(0x06100600, xfi_tphy->base + 0x50b4); + if (interface == PHY_INTERFACE_MODE_USXGMII) + writel(0x40704000, xfi_tphy->base + 0x3048); + else + writel(0x47684100, xfi_tphy->base + 0x3048); + } + + if (is_1g) + writel(0x0000c000, xfi_tphy->base + 0x3064); + + /* Setup RX EQ initial value */ + mtk_phy_update_bits(xfi_tphy->base + 0x3050, 0xa8000000, + (interface != PHY_INTERFACE_MODE_10GBASER) ? 0xa8000000 : 0x0); + mtk_phy_update_bits(xfi_tphy->base + 0x3054, 0xaa, + (interface != PHY_INTERFACE_MODE_10GBASER) ? 0xaa : 0x0); + + if (!use_lynxi_pcs) + writel(0x00000f00, xfi_tphy->base + 0x306c); + else if (is_2p5g) + writel(0x22000f00, xfi_tphy->base + 0x306c); + else + writel(0x20200f00, xfi_tphy->base + 0x306c); + + mtk_phy_update_bits(xfi_tphy->base + 0xa008, 0x10000, da_war ? 0x10000 : 0x0); + + mtk_phy_update_bits(xfi_tphy->base + 0xa060, 0x50000, use_lynxi_pcs ? 0x50000 : 0x40000); + + /* Setup PHYA speed */ + mtk_phy_update_bits(xfi_tphy->base + REG_ANA_GLB_D0, + XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN, + is_10g ? XTP_GLB_USXGMII_SEL(0) : + is_5g ? XTP_GLB_USXGMII_SEL(1) : + is_2p5g ? XTP_GLB_USXGMII_SEL(2) : + XTP_GLB_USXGMII_SEL(3)); + mtk_phy_set_bits(xfi_tphy->base + REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN); + + /* Release reset */ + mtk_phy_set_bits(xfi_tphy->base + REG_DIG_GLB_70, + XTP_PCS_RST_B | XTP_FRC_PCS_RST_B); + usleep_range(150, 500); + + /* Switch to P0 */ + mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70, + XTP_PCS_IN_FR_RG | + XTP_FRC_PCS_PWD_ASYNC | + XTP_PCS_PWD_ASYNC_MASK | + XTP_PCS_PWD_SYNC_MASK | + XTP_PCS_UPDT, + XTP_PCS_IN_FR_RG | + XTP_FRC_PCS_PWD_ASYNC | + XTP_PCS_UPDT); + usleep_range(1, 5); + + mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT); + usleep_range(15, 50); + + if (use_lynxi_pcs) { + /* Switch to Gen2 */ + mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70, + XTP_PCS_MODE_MASK | XTP_PCS_UPDT, + XTP_PCS_MODE(1) | XTP_PCS_UPDT); + } else { + /* Switch to Gen3 */ + mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70, + XTP_PCS_MODE_MASK | XTP_PCS_UPDT, + XTP_PCS_MODE(2) | XTP_PCS_UPDT); + } + usleep_range(1, 5); + + mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT); + + usleep_range(100, 500); + + /* Enable MAC CK */ + mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN); + mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN); + + /* Enable TX data */ + mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_40, + XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN); + usleep_range(400, 1000); +} + +/** + * mtk_xfi_tphy_set_mode() - Setup phy for specified interface mode. + * + * @phy: Phy instance. + * @mode: Only PHY_MODE_ETHERNET is supported. + * @submode: An Ethernet interface mode. + * + * Validate selected mode and call function mtk_xfi_tphy_setup(). + * + * Return: + * * %0 - OK + * * %-EINVAL - invalid mode + */ +static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int + submode) +{ + struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_5GBASER: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + mtk_xfi_tphy_setup(xfi_tphy, submode); + return 0; + default: + return -EINVAL; + } +} + +/** + * mtk_xfi_tphy_reset() - Reset the phy. + * + * @phy: Phy instance. + * + * Reset the phy using the external reset controller. + * + * Return: + * %0 - OK + */ +static int mtk_xfi_tphy_reset(struct phy *phy) +{ + struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); + + reset_control_assert(xfi_tphy->reset); + usleep_range(100, 500); + reset_control_deassert(xfi_tphy->reset); + usleep_range(1, 10); + + return 0; +} + +/** + * mtk_xfi_tphy_power_on() - Power-on the phy. + * + * @phy: Phy instance. + * + * Prepare and enable all clocks required for the phy to operate. + * + * Return: + * See clk_bulk_prepare_enable(). + */ +static int mtk_xfi_tphy_power_on(struct phy *phy) +{ + struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); + + return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); +} + +/** + * mtk_xfi_tphy_power_off() - Power-off the phy. + * + * @phy: Phy instance. + * + * Disable and unprepare all clocks previously enabled. + * + * Return: + * See clk_bulk_prepare_disable(). + */ +static int mtk_xfi_tphy_power_off(struct phy *phy) +{ + struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); + + return 0; +} + +static const struct phy_ops mtk_xfi_tphy_ops = { + .power_on = mtk_xfi_tphy_power_on, + .power_off = mtk_xfi_tphy_power_off, + .set_mode = mtk_xfi_tphy_set_mode, + .reset = mtk_xfi_tphy_reset, + .owner = THIS_MODULE, +}; + +/** + * mtk_xfi_tphy_probe() - Probe phy instance from Device Tree. + * @pdev: Matching platform device. + * + * The probe function gets IO resource, clocks, reset controller and + * whether the DA work-around for 10GBase-R is required from Device Tree and + * allocates memory for holding that information in a struct mtk_xfi_tphy. + * + * Return: + * * %0 - OK + * * %-ENODEV - Missing associated Device Tree node (should never happen). + * * %-ENOMEM - Out of memory. + * * Any error value which devm_platform_ioremap_resource(), + * devm_clk_bulk_get(), devm_reset_control_get_exclusive(), + * devm_phy_create() or devm_of_phy_provider_register() may return. + */ +static int mtk_xfi_tphy_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct phy_provider *phy_provider; + struct mtk_xfi_tphy *xfi_tphy; + struct phy *phy; + int ret; + + if (!np) + return -ENODEV; + + xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL); + if (!xfi_tphy) + return -ENOMEM; + + xfi_tphy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xfi_tphy->base)) + return PTR_ERR(xfi_tphy->base); + + xfi_tphy->dev = &pdev->dev; + xfi_tphy->clocks[0].id = "topxtal"; + xfi_tphy->clocks[1].id = "xfipll"; + ret = devm_clk_bulk_get(&pdev->dev, MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); + if (ret) + return ret; + + xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(xfi_tphy->reset)) + return PTR_ERR(xfi_tphy->reset); + + xfi_tphy->da_war = of_property_read_bool(np, "mediatek,usxgmii-performance-errata"); + + phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, xfi_tphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mtk_xfi_tphy_match[] = { + { .compatible = "mediatek,mt7988-xfi-tphy", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match); + +static struct platform_driver mtk_xfi_tphy_driver = { + .probe = mtk_xfi_tphy_probe, + .driver = { + .name = "mtk-xfi-tphy", + .of_match_table = mtk_xfi_tphy_match, + }, +}; +module_platform_driver(mtk_xfi_tphy_driver); + +MODULE_DESCRIPTION("MediaTek 10GE SerDes XFI T-PHY driver"); +MODULE_AUTHOR("Daniel Golle <[email protected]>"); +MODULE_AUTHOR("Bc-bocun Chen <[email protected]>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index c5c8d70bc853..bf6a07590321 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -20,7 +20,12 @@ #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> -static struct class *phy_class; +static void phy_release(struct device *dev); +static const struct class phy_class = { + .name = "phy", + .dev_release = phy_release, +}; + static struct dentry *phy_debugfs_root; static DEFINE_MUTEX(phy_provider_mutex); static LIST_HEAD(phy_provider_list); @@ -753,7 +758,7 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct phy *phy; struct class_dev_iter iter; - class_dev_iter_init(&iter, phy_class, NULL, NULL); + class_dev_iter_init(&iter, &phy_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { phy = to_phy(dev); if (args->np != phy->dev.of_node) @@ -1016,7 +1021,7 @@ struct phy *phy_create(struct device *dev, struct device_node *node, device_initialize(&phy->dev); mutex_init(&phy->mutex); - phy->dev.class = phy_class; + phy->dev.class = &phy_class; phy->dev.parent = dev; phy->dev.of_node = node ?: dev->of_node; phy->id = id; @@ -1285,14 +1290,13 @@ static void phy_release(struct device *dev) static int __init phy_core_init(void) { - phy_class = class_create("phy"); - if (IS_ERR(phy_class)) { - pr_err("failed to create phy class --> %ld\n", - PTR_ERR(phy_class)); - return PTR_ERR(phy_class); - } + int err; - phy_class->dev_release = phy_release; + err = class_register(&phy_class); + if (err) { + pr_err("failed to register phy class"); + return err; + } phy_debugfs_root = debugfs_create_dir("phy", NULL); @@ -1303,6 +1307,6 @@ device_initcall(phy_core_init); static void __exit phy_core_exit(void) { debugfs_remove_recursive(phy_debugfs_root); - class_destroy(phy_class); + class_unregister(&phy_class); } module_exit(phy_core_exit); diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index 621d0453bf76..da2b32fb5b45 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -24,6 +24,7 @@ #include "phy-qcom-qmp-dp-phy.h" #include "phy-qcom-qmp-qserdes-com-v4.h" +#include "phy-qcom-qmp-qserdes-com-v6.h" /* EDP_PHY registers */ #define DP_PHY_CFG 0x0010 @@ -77,9 +78,20 @@ struct qcom_edp_swing_pre_emph_cfg { const u8 (*pre_emphasis_hbr3_hbr2)[4][4]; }; +struct qcom_edp; + +struct phy_ver_ops { + int (*com_power_on)(const struct qcom_edp *edp); + int (*com_resetsm_cntrl)(const struct qcom_edp *edp); + int (*com_bias_en_clkbuflr)(const struct qcom_edp *edp); + int (*com_configure_pll)(const struct qcom_edp *edp); + int (*com_configure_ssc)(const struct qcom_edp *edp); +}; + struct qcom_edp_phy_cfg { bool is_edp; const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg; + const struct phy_ver_ops *ver_ops; }; struct qcom_edp { @@ -174,18 +186,6 @@ static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = { .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3, }; -static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = { -}; - -static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = { - .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg, -}; - -static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = { - .is_edp = true, - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg, -}; - static int qcom_edp_phy_init(struct phy *phy) { struct qcom_edp *edp = phy_get_drvdata(phy); @@ -204,8 +204,9 @@ static int qcom_edp_phy_init(struct phy *phy) DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, edp->edp + DP_PHY_PD_CTL); - /* Turn on BIAS current for PHY/PLL */ - writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + ret = edp->cfg->ver_ops->com_bias_en_clkbuflr(edp); + if (ret) + return ret; writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL); msleep(20); @@ -313,6 +314,84 @@ static int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opt static int qcom_edp_configure_ssc(const struct qcom_edp *edp) { + return edp->cfg->ver_ops->com_configure_ssc(edp); +} + +static int qcom_edp_configure_pll(const struct qcom_edp *edp) +{ + return edp->cfg->ver_ops->com_configure_pll(edp); +} + +static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 vco_div; + + switch (dp_opts->link_rate) { + case 1620: + vco_div = 0x1; + *pixel_freq = 1620000000UL / 2; + break; + + case 2700: + vco_div = 0x1; + *pixel_freq = 2700000000UL / 2; + break; + + case 5400: + vco_div = 0x2; + *pixel_freq = 5400000000UL / 4; + break; + + case 8100: + vco_div = 0x0; + *pixel_freq = 8100000000UL / 6; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(vco_div, edp->edp + DP_PHY_VCO_DIV); + + return 0; +} + +static int qcom_edp_phy_power_on_v4(const struct qcom_edp *edp) +{ + u32 val; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + writel(0xfc, edp->edp + DP_PHY_MODE); + + return readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS, + val, val & BIT(7), 5, 200); +} + +static int qcom_edp_phy_com_resetsm_cntrl_v4(const struct qcom_edp *edp) +{ + u32 val; + + writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL); + + return readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS, + val, val & BIT(0), 500, 10000); +} + +static int qcom_edp_com_bias_en_clkbuflr_v4(const struct qcom_edp *edp) +{ + /* Turn on BIAS current for PHY/PLL */ + writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + + return 0; +} + +static int qcom_edp_com_configure_ssc_v4(const struct qcom_edp *edp) +{ const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; u32 step1; u32 step2; @@ -345,7 +424,7 @@ static int qcom_edp_configure_ssc(const struct qcom_edp *edp) return 0; } -static int qcom_edp_configure_pll(const struct qcom_edp *edp) +static int qcom_edp_com_configure_pll_v4(const struct qcom_edp *edp) { const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; u32 div_frac_start2_mode0; @@ -431,30 +510,150 @@ static int qcom_edp_configure_pll(const struct qcom_edp *edp) return 0; } -static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq) +static const struct phy_ver_ops qcom_edp_phy_ops_v4 = { + .com_power_on = qcom_edp_phy_power_on_v4, + .com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v4, + .com_bias_en_clkbuflr = qcom_edp_com_bias_en_clkbuflr_v4, + .com_configure_pll = qcom_edp_com_configure_pll_v4, + .com_configure_ssc = qcom_edp_com_configure_ssc_v4, +}; + +static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = { + .ver_ops = &qcom_edp_phy_ops_v4, +}; + +static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = { + .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg, + .ver_ops = &qcom_edp_phy_ops_v4, +}; + +static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = { + .is_edp = true, + .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg, + .ver_ops = &qcom_edp_phy_ops_v4, +}; + +static int qcom_edp_phy_power_on_v6(const struct qcom_edp *edp) +{ + u32 val; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + writel(0xfc, edp->edp + DP_PHY_MODE); + + return readl_poll_timeout(edp->pll + QSERDES_V6_COM_CMN_STATUS, + val, val & BIT(7), 5, 200); +} + +static int qcom_edp_phy_com_resetsm_cntrl_v6(const struct qcom_edp *edp) +{ + u32 val; + + writel(0x20, edp->pll + QSERDES_V6_COM_RESETSM_CNTRL); + + return readl_poll_timeout(edp->pll + QSERDES_V6_COM_C_READY_STATUS, + val, val & BIT(0), 500, 10000); +} + +static int qcom_edp_com_bias_en_clkbuflr_v6(const struct qcom_edp *edp) +{ + /* Turn on BIAS current for PHY/PLL */ + writel(0x1f, edp->pll + QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN); + + return 0; +} + +static int qcom_edp_com_configure_ssc_v6(const struct qcom_edp *edp) { const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; - u32 vco_div; + u32 step1; + u32 step2; switch (dp_opts->link_rate) { case 1620: - vco_div = 0x1; - *pixel_freq = 1620000000UL / 2; + case 2700: + case 8100: + step1 = 0x92; + step2 = 0x01; + break; + + case 5400: + step1 = 0x18; + step2 = 0x02; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(0x01, edp->pll + QSERDES_V6_COM_SSC_EN_CENTER); + writel(0x00, edp->pll + QSERDES_V6_COM_SSC_ADJ_PER1); + writel(0x36, edp->pll + QSERDES_V6_COM_SSC_PER1); + writel(0x01, edp->pll + QSERDES_V6_COM_SSC_PER2); + writel(step1, edp->pll + QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0); + writel(step2, edp->pll + QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0); + + return 0; +} + +static int qcom_edp_com_configure_pll_v6(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 div_frac_start2_mode0; + u32 div_frac_start3_mode0; + u32 dec_start_mode0; + u32 lock_cmp1_mode0; + u32 lock_cmp2_mode0; + u32 code1_mode0; + u32 code2_mode0; + u32 hsclk_sel; + + switch (dp_opts->link_rate) { + case 1620: + hsclk_sel = 0x5; + dec_start_mode0 = 0x34; + div_frac_start2_mode0 = 0xc0; + div_frac_start3_mode0 = 0x0b; + lock_cmp1_mode0 = 0x37; + lock_cmp2_mode0 = 0x04; + code1_mode0 = 0x71; + code2_mode0 = 0x0c; break; case 2700: - vco_div = 0x1; - *pixel_freq = 2700000000UL / 2; + hsclk_sel = 0x3; + dec_start_mode0 = 0x34; + div_frac_start2_mode0 = 0xc0; + div_frac_start3_mode0 = 0x0b; + lock_cmp1_mode0 = 0x07; + lock_cmp2_mode0 = 0x07; + code1_mode0 = 0x71; + code2_mode0 = 0x0c; break; case 5400: - vco_div = 0x2; - *pixel_freq = 5400000000UL / 4; + hsclk_sel = 0x1; + dec_start_mode0 = 0x46; + div_frac_start2_mode0 = 0x00; + div_frac_start3_mode0 = 0x05; + lock_cmp1_mode0 = 0x0f; + lock_cmp2_mode0 = 0x0e; + code1_mode0 = 0x97; + code2_mode0 = 0x10; break; case 8100: - vco_div = 0x0; - *pixel_freq = 8100000000UL / 6; + hsclk_sel = 0x0; + dec_start_mode0 = 0x34; + div_frac_start2_mode0 = 0xc0; + div_frac_start3_mode0 = 0x0b; + lock_cmp1_mode0 = 0x17; + lock_cmp2_mode0 = 0x15; + code1_mode0 = 0x71; + code2_mode0 = 0x0c; break; default: @@ -462,33 +661,69 @@ static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel return -EINVAL; } - writel(vco_div, edp->edp + DP_PHY_VCO_DIV); + writel(0x01, edp->pll + QSERDES_V6_COM_SVS_MODE_CLK_SEL); + writel(0x0b, edp->pll + QSERDES_V6_COM_SYSCLK_EN_SEL); + writel(0x02, edp->pll + QSERDES_V6_COM_SYS_CLK_CTRL); + writel(0x0c, edp->pll + QSERDES_V6_COM_CLK_ENABLE1); + writel(0x06, edp->pll + QSERDES_V6_COM_SYSCLK_BUF_ENABLE); + writel(0x30, edp->pll + QSERDES_V6_COM_CLK_SELECT); + writel(hsclk_sel, edp->pll + QSERDES_V6_COM_HSCLK_SEL_1); + writel(0x07, edp->pll + QSERDES_V6_COM_PLL_IVCO); + writel(0x08, edp->pll + QSERDES_V6_COM_LOCK_CMP_EN); + writel(0x36, edp->pll + QSERDES_V6_COM_PLL_CCTRL_MODE0); + writel(0x16, edp->pll + QSERDES_V6_COM_PLL_RCTRL_MODE0); + writel(0x06, edp->pll + QSERDES_V6_COM_CP_CTRL_MODE0); + writel(dec_start_mode0, edp->pll + QSERDES_V6_COM_DEC_START_MODE0); + writel(0x00, edp->pll + QSERDES_V6_COM_DIV_FRAC_START1_MODE0); + writel(div_frac_start2_mode0, edp->pll + QSERDES_V6_COM_DIV_FRAC_START2_MODE0); + writel(div_frac_start3_mode0, edp->pll + QSERDES_V6_COM_DIV_FRAC_START3_MODE0); + writel(0x12, edp->pll + QSERDES_V6_COM_CMN_CONFIG_1); + writel(0x3f, edp->pll + QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE0); + writel(0x00, edp->pll + QSERDES_V6_COM_INTEGLOOP_GAIN1_MODE0); + writel(0x00, edp->pll + QSERDES_V6_COM_VCO_TUNE_MAP); + writel(lock_cmp1_mode0, edp->pll + QSERDES_V6_COM_LOCK_CMP1_MODE0); + writel(lock_cmp2_mode0, edp->pll + QSERDES_V6_COM_LOCK_CMP2_MODE0); + + writel(0x0a, edp->pll + QSERDES_V6_COM_BG_TIMER); + writel(0x14, edp->pll + QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0); + writel(0x00, edp->pll + QSERDES_V6_COM_VCO_TUNE_CTRL); + writel(0x1f, edp->pll + QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN); + writel(0x0f, edp->pll + QSERDES_V6_COM_CORE_CLK_EN); + writel(0xa0, edp->pll + QSERDES_V6_COM_VCO_TUNE1_MODE0); + writel(0x03, edp->pll + QSERDES_V6_COM_VCO_TUNE2_MODE0); + + writel(code1_mode0, edp->pll + QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0); + writel(code2_mode0, edp->pll + QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0); return 0; } +static const struct phy_ver_ops qcom_edp_phy_ops_v6 = { + .com_power_on = qcom_edp_phy_power_on_v6, + .com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v6, + .com_bias_en_clkbuflr = qcom_edp_com_bias_en_clkbuflr_v6, + .com_configure_pll = qcom_edp_com_configure_pll_v6, + .com_configure_ssc = qcom_edp_com_configure_ssc_v6, +}; + +static struct qcom_edp_phy_cfg x1e80100_phy_cfg = { + .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg, + .ver_ops = &qcom_edp_phy_ops_v6, +}; + static int qcom_edp_phy_power_on(struct phy *phy) { const struct qcom_edp *edp = phy_get_drvdata(phy); u32 bias0_en, drvr0_en, bias1_en, drvr1_en; unsigned long pixel_freq; u8 ldo_config = 0x0; - int timeout; int ret; u32 val; u8 cfg1; - writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | - DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | - DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, - edp->edp + DP_PHY_PD_CTL); - writel(0xfc, edp->edp + DP_PHY_MODE); - - timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS, - val, val & BIT(7), 5, 200); - if (timeout) - return timeout; - + ret = edp->cfg->ver_ops->com_power_on(edp); + if (ret) + return ret; if (edp->cfg->swing_pre_emph_cfg && !edp->is_edp) ldo_config = 0x1; @@ -535,12 +770,9 @@ static int qcom_edp_phy_power_on(struct phy *phy) writel(0x01, edp->edp + DP_PHY_CFG); writel(0x09, edp->edp + DP_PHY_CFG); - writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL); - - timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS, - val, val & BIT(0), 500, 10000); - if (timeout) - return timeout; + ret = edp->cfg->ver_ops->com_resetsm_cntrl(edp); + if (ret) + return ret; writel(0x19, edp->edp + DP_PHY_CFG); writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN); @@ -880,6 +1112,7 @@ static const struct of_device_id qcom_edp_phy_match_table[] = { { .compatible = "qcom,sc8180x-edp-phy", .data = &sc7280_dp_phy_cfg, }, { .compatible = "qcom,sc8280xp-dp-phy", .data = &sc8280xp_dp_phy_cfg, }, { .compatible = "qcom,sc8280xp-edp-phy", .data = &sc8280xp_edp_phy_cfg, }, + { .compatible = "qcom,x1e80100-dp-phy", .data = &x1e80100_phy_cfg, }, { } }; MODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table); diff --git a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c index a43e20abb10d..68cc8e24f383 100644 --- a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c +++ b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c @@ -88,6 +88,12 @@ static const u32 pm8550b_init_tbl[NUM_TUNE_FIELDS] = { [TUNE_USB2_PREEM] = 0x5, }; +static const u32 smb2360_init_tbl[NUM_TUNE_FIELDS] = { + [TUNE_IUSB2] = 0x5, + [TUNE_SQUELCH_U] = 0x3, + [TUNE_USB2_PREEM] = 0x2, +}; + static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = { .init_tbl = pm8550b_init_tbl, .init_tbl_num = ARRAY_SIZE(pm8550b_init_tbl), @@ -95,6 +101,13 @@ static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = { .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), }; +static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = { + .init_tbl = smb2360_init_tbl, + .init_tbl_num = ARRAY_SIZE(smb2360_init_tbl), + .vreg_list = pm8550b_vreg_l, + .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), +}; + static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr) { int num = rptr->cfg->num_vregs; @@ -271,6 +284,10 @@ static const struct of_device_id eusb2_repeater_of_match_table[] = { .compatible = "qcom,pm8550b-eusb2-repeater", .data = &pm8550b_eusb2_cfg, }, + { + .compatible = "qcom,smb2360-eusb2-repeater", + .data = &smb2360_eusb2_cfg, + }, { }, }; MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index c21cdb8dbfe7..7f999e8a433d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -1382,6 +1382,13 @@ static const u8 qmp_dp_v5_voltage_swing_hbr_rbr[4][4] = { { 0x3f, 0xff, 0xff, 0xff } }; +static const u8 qmp_dp_v6_voltage_swing_hbr_rbr[4][4] = { + { 0x27, 0x2f, 0x36, 0x3f }, + { 0x31, 0x3e, 0x3f, 0xff }, + { 0x36, 0x3f, 0xff, 0xff }, + { 0x3f, 0xff, 0xff, 0xff } +}; + static const u8 qmp_dp_v6_pre_emphasis_hbr_rbr[4][4] = { { 0x20, 0x2d, 0x34, 0x3a }, { 0x20, 0x2e, 0x35, 0xff }, @@ -2001,6 +2008,51 @@ static const struct qmp_phy_cfg sm8550_usb3dpphy_cfg = { .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), }; +static const struct qmp_phy_cfg sm8650_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + + .serdes_tbl = sm8550_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8550_usb3_serdes_tbl), + .tx_tbl = sm8550_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8550_usb3_tx_tbl), + .rx_tbl = sm8550_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8550_usb3_rx_tbl), + .pcs_tbl = sm8550_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_tbl), + .pcs_usb_tbl = sm8550_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_usb_tbl), + + .dp_serdes_tbl = qmp_v6_dp_serdes_tbl, + .dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl), + .dp_tx_tbl = qmp_v6_dp_tx_tbl, + .dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v6_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v6_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v6_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v6_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v6_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v6_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2, + + .dp_aux_init = qmp_v4_dp_aux_init, + .configure_dp_tx = qmp_v4_configure_dp_tx, + .configure_dp_phy = qmp_v4_configure_dp_phy, + .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, + + .regs = qmp_v6_usb3phy_regs_layout, + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), +}; + static int qmp_combo_dp_serdes_init(struct qmp_combo *qmp) { const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -2437,8 +2489,6 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) writel(0x20, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); return 0; - - return 0; } /* @@ -3631,7 +3681,7 @@ static const struct of_device_id qmp_combo_of_match_table[] = { }, { .compatible = "qcom,sm8650-qmp-usb3-dp-phy", - .data = &sm8550_usb3dpphy_cfg, + .data = &sm8650_usb3dpphy_cfg, }, { .compatible = "qcom,x1e80100-qmp-usb3-dp-phy", diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 8836bb1ff0cc..6c796723c8f5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -22,6 +22,8 @@ #include <linux/reset.h> #include <linux/slab.h> +#include <dt-bindings/phy/phy-qcom-qmp.h> + #include "phy-qcom-qmp-common.h" #include "phy-qcom-qmp.h" @@ -2246,6 +2248,7 @@ static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_pcs_alt_tbl[] = { }; static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_serdes_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), @@ -2272,7 +2275,6 @@ static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_rc_serdes_alt_tbl[] QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97), QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), @@ -2389,6 +2391,9 @@ struct qmp_phy_cfg { /* QMP PHY pipe clock interface rate */ unsigned long pipe_clock_rate; + + /* QMP PHY AUX clock interface rate */ + unsigned long aux_clock_rate; }; struct qmp_pcie { @@ -2420,6 +2425,7 @@ struct qmp_pcie { int mode; struct clk_fixed_rate pipe_clk_fixed; + struct clk_fixed_rate aux_clk_fixed; }; static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) @@ -3135,6 +3141,9 @@ static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, .phy_status = PHYSTATUS_4_20, + + /* 20MHz PHY AUX Clock */ + .aux_clock_rate = 20000000, }; static const struct qmp_phy_cfg sm8550_qmp_gen3x2_pciephy_cfg = { @@ -3192,6 +3201,9 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = { .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, .phy_status = PHYSTATUS_4_20, .has_nocsr_reset = true, + + /* 20MHz PHY AUX Clock */ + .aux_clock_rate = 20000000, }; static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = { @@ -3222,6 +3234,9 @@ static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = { .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, .phy_status = PHYSTATUS_4_20, .has_nocsr_reset = true, + + /* 20MHz PHY AUX Clock */ + .aux_clock_rate = 20000000, }; static const struct qmp_phy_cfg sa8775p_qmp_gen4x2_pciephy_cfg = { @@ -3291,6 +3306,13 @@ static const struct qmp_phy_cfg sa8775p_qmp_gen4x4_pciephy_cfg = { .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl), }, + .tbls_ep = &(const struct qmp_phy_cfg_tbls) { + .serdes = sa8775p_qmp_gen4x2_pcie_ep_serdes_alt_tbl, + .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_ep_serdes_alt_tbl), + .pcs_misc = sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl), + }, + .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -3664,7 +3686,7 @@ static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np) struct clk_init_data init = { }; int ret; - ret = of_property_read_string(np, "clock-output-names", &init.name); + ret = of_property_read_string_index(np, "clock-output-names", 0, &init.name); if (ret) { dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); return ret; @@ -3683,14 +3705,87 @@ static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np) fixed->hw.init = &init; - ret = devm_clk_hw_register(qmp->dev, &fixed->hw); - if (ret) + return devm_clk_hw_register(qmp->dev, &fixed->hw); +} + +/* + * Register a fixed rate PHY aux clock. + * + * The <s>_phy_aux_clksrc generated by PHY goes to the GCC that gate + * controls it. The <s>_phy_aux_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the <s>_phy_aux_clksrc here. The gcc driver takes care + * of assigning this <s>_phy_aux_clksrc as parent to <s>_phy_aux_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->phy_aux_clksrc--->| GCC |--->phy_aux_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_aux_clk_register(struct qmp_pcie *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed = &qmp->aux_clk_fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string_index(np, "clock-output-names", 1, &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names index 1\n", np); return ret; + } + + init.ops = &clk_fixed_rate_ops; + + fixed->fixed_rate = qmp->cfg->aux_clock_rate; + fixed->hw.init = &init; + + return devm_clk_hw_register(qmp->dev, &fixed->hw); +} + +static struct clk_hw *qmp_pcie_clk_hw_get(struct of_phandle_args *clkspec, void *data) +{ + struct qmp_pcie *qmp = data; + + /* Support legacy bindings */ + if (!clkspec->args_count) + return &qmp->pipe_clk_fixed.hw; - ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + switch (clkspec->args[0]) { + case QMP_PCIE_PIPE_CLK: + return &qmp->pipe_clk_fixed.hw; + case QMP_PCIE_PHY_AUX_CLK: + return &qmp->aux_clk_fixed.hw; + } + + return ERR_PTR(-EINVAL); +} + +static int qmp_pcie_register_clocks(struct qmp_pcie *qmp, struct device_node *np) +{ + int ret; + + ret = phy_pipe_clk_register(qmp, np); if (ret) return ret; + if (qmp->cfg->aux_clock_rate) { + ret = phy_aux_clk_register(qmp, np); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, qmp_pcie_clk_hw_get, qmp); + if (ret) + return ret; + } else { + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &qmp->pipe_clk_fixed.hw); + if (ret) + return ret; + } + /* * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device. @@ -3899,7 +3994,7 @@ static int qmp_pcie_probe(struct platform_device *pdev) if (ret) goto err_node_put; - ret = phy_pipe_clk_register(qmp, np); + ret = qmp_pcie_register_clocks(qmp, np); if (ret) goto err_node_put; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h index 970cc0667809..f19f9892ed7b 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h @@ -30,5 +30,9 @@ #define QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1 0x1f4 #define QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1 0x1fc #define QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME 0x220 +#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4 0x240 +#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S5 0x244 +#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S6 0x248 +#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S7 0x24c #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h index d9a87bd95590..d17a52357965 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h @@ -25,12 +25,15 @@ #define QSERDES_UFS_V6_RX_UCDR_SO_GAIN_RATE4 0xf0 #define QSERDES_UFS_V6_RX_UCDR_PI_CONTROLS 0xf4 #define QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL 0x178 +#define QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4 0x1ac #define QSERDES_UFS_V6_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x1bc #define QSERDES_UFS_V6_RX_INTERFACE_MODE 0x1e0 #define QSERDES_UFS_V6_RX_OFFSET_ADAPTOR_CNTRL3 0x1c4 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0 0x208 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1 0x20c +#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2 0x210 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3 0x214 +#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4 0x218 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6 0x220 #define QSERDES_UFS_V6_RX_MODE_RATE2_B3 0x238 #define QSERDES_UFS_V6_RX_MODE_RATE2_B6 0x244 @@ -38,6 +41,9 @@ #define QSERDES_UFS_V6_RX_MODE_RATE3_B4 0x260 #define QSERDES_UFS_V6_RX_MODE_RATE3_B5 0x264 #define QSERDES_UFS_V6_RX_MODE_RATE3_B8 0x270 +#define QSERDES_UFS_V6_RX_MODE_RATE4_B0 0x274 +#define QSERDES_UFS_V6_RX_MODE_RATE4_B1 0x278 +#define QSERDES_UFS_V6_RX_MODE_RATE4_B2 0x27c #define QSERDES_UFS_V6_RX_MODE_RATE4_B3 0x280 #define QSERDES_UFS_V6_RX_MODE_RATE4_B4 0x284 #define QSERDES_UFS_V6_RX_MODE_RATE4_B6 0x28c diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 590432d581f9..a57e8a4657f4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -722,6 +722,38 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_g4_pcs[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_BIST_FIXED_PAT_CTRL, 0x0a), }; +static const struct qmp_phy_init_tbl sm8475_ufsphy_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8475_ufsphy_g4_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8475_ufsphy_g4_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04), +}; + static const struct qmp_phy_init_tbl sm8550_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9), QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), @@ -830,17 +862,20 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11), QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x44), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04), QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00), QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06), QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18), QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06), QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06), QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18), QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99), @@ -848,17 +883,28 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_serdes[] = { }; static const struct qmp_phy_init_tbl sm8650_ufsphy_tx[] = { - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x01), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), }; static const struct qmp_phy_init_tbl sm8650_ufsphy_rx[] = { QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xc2), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_SO_GAIN_RATE4, 0x04), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CONTROLS, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_OFFSET_ADAPTOR_CNTRL3, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4, 0x02), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x06), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x3e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xce), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xce), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2, 0x18), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4, 0x0f), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6, 0x60), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B3, 0x9e), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B6, 0x60), @@ -866,23 +912,41 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_rx[] = { QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B4, 0x0e), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B5, 0x36), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B8, 0x02), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B2, 0x20), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B3, 0xb9), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B6, 0xff), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B4, 0x4f), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_SO_SATURATION, 0x1f), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CTRL1, 0x94), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_TERM_BW_CTRL0, 0xfa), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_DLL0_FTUNE_CTRL, 0x30), }; static const struct qmp_phy_init_tbl sm8650_ufsphy_pcs[] = { - QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0xc1), - QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S5, 0x12), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S6, 0x15), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S7, 0x19), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_g4_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x13), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04), - QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), - QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x69), - QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_g5_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x05), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x05), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HS_G5_SYNC_LENGTH_CAPABILITY, 0x4d), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME, 0x9e), }; struct qmp_ufs_offsets { @@ -1346,6 +1410,42 @@ static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { .regs = ufsphy_v5_regs_layout, }; +static const struct qmp_phy_cfg sm8475_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets_v6, + .max_supported_gear = UFS_HS_G4, + + .tbls = { + .serdes = sm8475_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(sm8475_ufsphy_serdes), + .tx = sm8550_ufsphy_tx, + .tx_num = ARRAY_SIZE(sm8550_ufsphy_tx), + .rx = sm8550_ufsphy_rx, + .rx_num = ARRAY_SIZE(sm8550_ufsphy_rx), + .pcs = sm8550_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(sm8550_ufsphy_pcs), + }, + .tbls_hs_b = { + .serdes = sm8550_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm8550_ufsphy_hs_b_serdes), + }, + .tbls_hs_overlay[0] = { + .serdes = sm8475_ufsphy_g4_serdes, + .serdes_num = ARRAY_SIZE(sm8475_ufsphy_g4_serdes), + .tx = sm8550_ufsphy_g4_tx, + .tx_num = ARRAY_SIZE(sm8550_ufsphy_g4_tx), + .rx = sm8550_ufsphy_g4_rx, + .rx_num = ARRAY_SIZE(sm8550_ufsphy_g4_rx), + .pcs = sm8475_ufsphy_g4_pcs, + .pcs_num = ARRAY_SIZE(sm8475_ufsphy_g4_pcs), + .max_gear = UFS_HS_G4, + }, + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = ufsphy_v6_regs_layout, +}; + static const struct qmp_phy_cfg sm8550_ufsphy_cfg = { .lanes = 2, @@ -1407,6 +1507,17 @@ static const struct qmp_phy_cfg sm8650_ufsphy_cfg = { .pcs = sm8650_ufsphy_pcs, .pcs_num = ARRAY_SIZE(sm8650_ufsphy_pcs), }, + .tbls_hs_overlay[0] = { + .pcs = sm8650_ufsphy_g4_pcs, + .pcs_num = ARRAY_SIZE(sm8650_ufsphy_g4_pcs), + .max_gear = UFS_HS_G4, + }, + .tbls_hs_overlay[1] = { + .pcs = sm8650_ufsphy_g5_pcs, + .pcs_num = ARRAY_SIZE(sm8650_ufsphy_g5_pcs), + .max_gear = UFS_HS_G5, + }, + .vreg_list = qmp_phy_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), .regs = ufsphy_v6_regs_layout, @@ -1942,6 +2053,9 @@ static const struct of_device_id qmp_ufs_of_match_table[] = { .compatible = "qcom,sm8450-qmp-ufs-phy", .data = &sm8450_ufsphy_cfg, }, { + .compatible = "qcom,sm8475-qmp-ufs-phy", + .data = &sm8475_ufsphy_cfg, + }, { .compatible = "qcom,sm8550-qmp-ufs-phy", .data = &sm8550_ufsphy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index 85253936fac3..c174463c58a3 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -337,6 +337,29 @@ static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG2, 0x08), }; +static const struct qmp_phy_init_tbl qdu1000_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + +static const struct qmp_phy_init_tbl qdu1000_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1, 0x6f), +}; + static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), @@ -1400,6 +1423,27 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .regs = qmp_v2_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg qdu1000_usb3_uniphy_cfg = { + .offsets = &qmp_usb_offsets_v5, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8350_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_tx_tbl), + .rx_tbl = sm8350_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_rx_tbl), + .pcs_tbl = qdu1000_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qdu1000_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = qdu1000_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(qdu1000_usb3_uniphy_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x1000, + + .has_pwrdn_delay = true, +}; + static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { .offsets = &qmp_usb_offsets_v5, @@ -2203,6 +2247,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,msm8996-qmp-usb3-phy", .data = &msm8996_usb3phy_cfg, }, { + .compatible = "qcom,qdu1000-qmp-usb3-uni-phy", + .data = &qdu1000_usb3_uniphy_cfg, + }, { .compatible = "qcom,sa8775p-qmp-usb3-uni-phy", .data = &sa8775p_usb3_uniphy_cfg, }, { diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index b60a4b60451e..08b0f4345760 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -116,3 +116,15 @@ config PHY_ROCKCHIP_USB select GENERIC_PHY help Enable this to support the Rockchip USB 2.0 PHY. + +config PHY_ROCKCHIP_USBDP + tristate "Rockchip USBDP COMBO PHY Driver" + depends on ARCH_ROCKCHIP && OF + depends on TYPEC + select GENERIC_PHY + help + Enable this to support the Rockchip USB3.0/DP combo PHY with + Samsung IP block. This is required for USB3 support on RK3588. + + To compile this driver as a module, choose M here: the module + will be called phy-rockchip-usbdp diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index 3d911304e654..010a824e32ce 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index bf74e429ff46..0a9989e41237 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -248,7 +248,7 @@ static int rockchip_combphy_exit(struct phy *phy) return 0; } -static const struct phy_ops rochchip_combphy_ops = { +static const struct phy_ops rockchip_combphy_ops = { .init = rockchip_combphy_init, .exit = rockchip_combphy_exit, .owner = THIS_MODULE, @@ -364,7 +364,7 @@ static int rockchip_combphy_probe(struct platform_device *pdev) return ret; } - priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops); + priv->phy = devm_phy_create(dev, NULL, &rockchip_combphy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create combphy\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 9857ee45b89e..4e8ffd173096 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -35,11 +35,17 @@ #define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 #define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 #define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 +#define RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1 0x1004 +#define RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1 0x1104 +#define RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1 0x2004 +#define RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1 0x2104 #define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0)) #define RK3588_BIFURCATION_LANE_0_1 BIT(0) #define RK3588_BIFURCATION_LANE_2_3 BIT(1) #define RK3588_LANE_AGGREGATION BIT(2) +#define RK3588_RX_CMN_REFCLK_MODE_EN ((BIT(7) << 16) | BIT(7)) +#define RK3588_RX_CMN_REFCLK_MODE_DIS (BIT(7) << 16) #define RK3588_PCIE1LN_SEL_EN (GENMASK(1, 0) << 16) #define RK3588_PCIE30_PHY_MODE_EN (GENMASK(2, 0) << 16) @@ -60,6 +66,7 @@ struct rockchip_p3phy_priv { int num_clks; int num_lanes; u32 lanes[4]; + u32 rx_cmn_refclk_mode[4]; }; struct rockchip_p3phy_ops { @@ -137,6 +144,19 @@ static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv) u8 mode = RK3588_LANE_AGGREGATION; /* default */ int ret; + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1, + priv->rx_cmn_refclk_mode[0] ? RK3588_RX_CMN_REFCLK_MODE_EN : + RK3588_RX_CMN_REFCLK_MODE_DIS); + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1, + priv->rx_cmn_refclk_mode[1] ? RK3588_RX_CMN_REFCLK_MODE_EN : + RK3588_RX_CMN_REFCLK_MODE_DIS); + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1, + priv->rx_cmn_refclk_mode[2] ? RK3588_RX_CMN_REFCLK_MODE_EN : + RK3588_RX_CMN_REFCLK_MODE_DIS); + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1, + priv->rx_cmn_refclk_mode[3] ? RK3588_RX_CMN_REFCLK_MODE_EN : + RK3588_RX_CMN_REFCLK_MODE_DIS); + /* Deassert PCIe PMA output clamp mode */ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24)); @@ -182,7 +202,7 @@ static const struct rockchip_p3phy_ops rk3588_ops = { .phy_init = rockchip_p3phy_rk3588_init, }; -static int rochchip_p3phy_init(struct phy *phy) +static int rockchip_p3phy_init(struct phy *phy) { struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); int ret; @@ -205,7 +225,7 @@ static int rochchip_p3phy_init(struct phy *phy) return ret; } -static int rochchip_p3phy_exit(struct phy *phy) +static int rockchip_p3phy_exit(struct phy *phy) { struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); @@ -214,9 +234,9 @@ static int rochchip_p3phy_exit(struct phy *phy) return 0; } -static const struct phy_ops rochchip_p3phy_ops = { - .init = rochchip_p3phy_init, - .exit = rochchip_p3phy_exit, +static const struct phy_ops rockchip_p3phy_ops = { + .init = rockchip_p3phy_init, + .exit = rockchip_p3phy_exit, .set_mode = rockchip_p3phy_set_mode, .owner = THIS_MODULE, }; @@ -275,7 +295,24 @@ static int rockchip_p3phy_probe(struct platform_device *pdev) return priv->num_lanes; } - priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops); + ret = of_property_read_variable_u32_array(dev->of_node, + "rockchip,rx-common-refclk-mode", + priv->rx_cmn_refclk_mode, 1, + ARRAY_SIZE(priv->rx_cmn_refclk_mode)); + /* + * if no rockchip,rx-common-refclk-mode, assume enabled for all lanes in + * order to be DT backwards compatible. (Since HW reset val is enabled.) + */ + if (ret == -EINVAL) { + for (int i = 0; i < ARRAY_SIZE(priv->rx_cmn_refclk_mode); i++) + priv->rx_cmn_refclk_mode[i] = 1; + } else if (ret < 0) { + dev_err(dev, "failed to read rockchip,rx-common-refclk-mode property %d\n", + ret); + return ret; + } + + priv->phy = devm_phy_create(dev, NULL, &rockchip_p3phy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create combphy\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c new file mode 100644 index 000000000000..2c51e5c62d3e --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c @@ -0,0 +1,1608 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip USBDP Combo PHY with Samsung IP block driver + * + * Copyright (C) 2021-2024 Rockchip Electronics Co., Ltd + * Copyright (C) 2024 Collabora Ltd + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/mfd/syscon.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/usb/ch9.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> + +/* USBDP PHY Register Definitions */ +#define UDPHY_PCS 0x4000 +#define UDPHY_PMA 0x8000 + +/* VO0 GRF Registers */ +#define DP_SINK_HPD_CFG BIT(11) +#define DP_SINK_HPD_SEL BIT(10) +#define DP_AUX_DIN_SEL BIT(9) +#define DP_AUX_DOUT_SEL BIT(8) +#define DP_LANE_SEL_N(n) GENMASK(2 * (n) + 1, 2 * (n)) +#define DP_LANE_SEL_ALL GENMASK(7, 0) + +/* PMA CMN Registers */ +#define CMN_LANE_MUX_AND_EN_OFFSET 0x0288 /* cmn_reg00A2 */ +#define CMN_DP_LANE_MUX_N(n) BIT((n) + 4) +#define CMN_DP_LANE_EN_N(n) BIT(n) +#define CMN_DP_LANE_MUX_ALL GENMASK(7, 4) +#define CMN_DP_LANE_EN_ALL GENMASK(3, 0) + +#define CMN_DP_LINK_OFFSET 0x28c /* cmn_reg00A3 */ +#define CMN_DP_TX_LINK_BW GENMASK(6, 5) +#define CMN_DP_TX_LANE_SWAP_EN BIT(2) + +#define CMN_SSC_EN_OFFSET 0x2d0 /* cmn_reg00B4 */ +#define CMN_ROPLL_SSC_EN BIT(1) +#define CMN_LCPLL_SSC_EN BIT(0) + +#define CMN_ANA_LCPLL_DONE_OFFSET 0x0350 /* cmn_reg00D4 */ +#define CMN_ANA_LCPLL_LOCK_DONE BIT(7) +#define CMN_ANA_LCPLL_AFC_DONE BIT(6) + +#define CMN_ANA_ROPLL_DONE_OFFSET 0x0354 /* cmn_reg00D5 */ +#define CMN_ANA_ROPLL_LOCK_DONE BIT(1) +#define CMN_ANA_ROPLL_AFC_DONE BIT(0) + +#define CMN_DP_RSTN_OFFSET 0x038c /* cmn_reg00E3 */ +#define CMN_DP_INIT_RSTN BIT(3) +#define CMN_DP_CMN_RSTN BIT(2) +#define CMN_CDR_WTCHDG_EN BIT(1) +#define CMN_CDR_WTCHDG_MSK_CDR_EN BIT(0) + +#define TRSV_ANA_TX_CLK_OFFSET_N(n) (0x854 + (n) * 0x800) /* trsv_reg0215 */ +#define LN_ANA_TX_SER_TXCLK_INV BIT(1) + +#define TRSV_LN0_MON_RX_CDR_DONE_OFFSET 0x0b84 /* trsv_reg02E1 */ +#define TRSV_LN0_MON_RX_CDR_LOCK_DONE BIT(0) + +#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */ +#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0) + +#define BIT_WRITEABLE_SHIFT 16 +#define PHY_AUX_DP_DATA_POL_NORMAL 0 +#define PHY_AUX_DP_DATA_POL_INVERT 1 +#define PHY_LANE_MUX_USB 0 +#define PHY_LANE_MUX_DP 1 + +enum { + DP_BW_RBR, + DP_BW_HBR, + DP_BW_HBR2, + DP_BW_HBR3, +}; + +enum { + UDPHY_MODE_NONE = 0, + UDPHY_MODE_USB = BIT(0), + UDPHY_MODE_DP = BIT(1), + UDPHY_MODE_DP_USB = BIT(1) | BIT(0), +}; + +struct rk_udphy_grf_reg { + unsigned int offset; + unsigned int disable; + unsigned int enable; +}; + +#define _RK_UDPHY_GEN_GRF_REG(offset, mask, disable, enable) \ +{\ + offset, \ + FIELD_PREP_CONST(mask, disable) | (mask << BIT_WRITEABLE_SHIFT), \ + FIELD_PREP_CONST(mask, enable) | (mask << BIT_WRITEABLE_SHIFT), \ +} + +#define RK_UDPHY_GEN_GRF_REG(offset, bitend, bitstart, disable, enable) \ + _RK_UDPHY_GEN_GRF_REG(offset, GENMASK(bitend, bitstart), disable, enable) + +struct rk_udphy_grf_cfg { + /* u2phy-grf */ + struct rk_udphy_grf_reg bvalid_phy_con; + struct rk_udphy_grf_reg bvalid_grf_con; + + /* usb-grf */ + struct rk_udphy_grf_reg usb3otg0_cfg; + struct rk_udphy_grf_reg usb3otg1_cfg; + + /* usbdpphy-grf */ + struct rk_udphy_grf_reg low_pwrn; + struct rk_udphy_grf_reg rx_lfps; +}; + +struct rk_udphy_vogrf_cfg { + /* vo-grf */ + struct rk_udphy_grf_reg hpd_trigger; + u32 dp_lane_reg; +}; + +struct rk_udphy_dp_tx_drv_ctrl { + u32 trsv_reg0204; + u32 trsv_reg0205; + u32 trsv_reg0206; + u32 trsv_reg0207; +}; + +struct rk_udphy_cfg { + unsigned int num_phys; + unsigned int phy_ids[2]; + /* resets to be requested */ + const char * const *rst_list; + int num_rsts; + + struct rk_udphy_grf_cfg grfcfg; + struct rk_udphy_vogrf_cfg vogrfcfg[2]; + const struct rk_udphy_dp_tx_drv_ctrl (*dp_tx_ctrl_cfg[4])[4]; + const struct rk_udphy_dp_tx_drv_ctrl (*dp_tx_ctrl_cfg_typec[4])[4]; +}; + +struct rk_udphy { + struct device *dev; + struct regmap *pma_regmap; + struct regmap *u2phygrf; + struct regmap *udphygrf; + struct regmap *usbgrf; + struct regmap *vogrf; + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + struct mutex mutex; /* mutex to protect access to individual PHYs */ + + /* clocks and rests */ + int num_clks; + struct clk_bulk_data *clks; + struct clk *refclk; + int num_rsts; + struct reset_control_bulk_data *rsts; + + /* PHY status management */ + bool flip; + bool mode_change; + u8 mode; + u8 status; + + /* utilized for USB */ + bool hs; /* flag for high-speed */ + + /* utilized for DP */ + struct gpio_desc *sbu1_dc_gpio; + struct gpio_desc *sbu2_dc_gpio; + u32 lane_mux_sel[4]; + u32 dp_lane_sel[4]; + u32 dp_aux_dout_sel; + u32 dp_aux_din_sel; + bool dp_sink_hpd_sel; + bool dp_sink_hpd_cfg; + u8 bw; + int id; + + bool dp_in_use; + + /* PHY const config */ + const struct rk_udphy_cfg *cfgs; + + /* PHY devices */ + struct phy *phy_dp; + struct phy *phy_u3; +}; + +static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { + { 0x20, 0x10, 0x42, 0xe5 }, + { 0x26, 0x14, 0x42, 0xe5 }, + { 0x29, 0x18, 0x42, 0xe5 }, + { 0x2b, 0x1c, 0x43, 0xe7 }, + }, + + /* voltage swing 1, pre-emphasis 0->2 */ + { + { 0x23, 0x10, 0x42, 0xe7 }, + { 0x2a, 0x17, 0x43, 0xe7 }, + { 0x2b, 0x1a, 0x43, 0xe7 }, + }, + + /* voltage swing 2, pre-emphasis 0->1 */ + { + { 0x27, 0x10, 0x42, 0xe7 }, + { 0x2b, 0x17, 0x43, 0xe7 }, + }, + + /* voltage swing 3, pre-emphasis 0 */ + { + { 0x29, 0x10, 0x43, 0xe7 }, + }, +}; + +static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr_typec[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { + { 0x20, 0x10, 0x42, 0xe5 }, + { 0x26, 0x14, 0x42, 0xe5 }, + { 0x29, 0x18, 0x42, 0xe5 }, + { 0x2b, 0x1c, 0x43, 0xe7 }, + }, + + /* voltage swing 1, pre-emphasis 0->2 */ + { + { 0x23, 0x10, 0x42, 0xe7 }, + { 0x2a, 0x17, 0x43, 0xe7 }, + { 0x2b, 0x1a, 0x43, 0xe7 }, + }, + + /* voltage swing 2, pre-emphasis 0->1 */ + { + { 0x27, 0x10, 0x43, 0x67 }, + { 0x2b, 0x17, 0x43, 0xe7 }, + }, + + /* voltage swing 3, pre-emphasis 0 */ + { + { 0x29, 0x10, 0x43, 0xe7 }, + }, +}; + +static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr2[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { + { 0x21, 0x10, 0x42, 0xe5 }, + { 0x26, 0x14, 0x42, 0xe5 }, + { 0x26, 0x16, 0x43, 0xe5 }, + { 0x2a, 0x19, 0x43, 0xe7 }, + }, + + /* voltage swing 1, pre-emphasis 0->2 */ + { + { 0x24, 0x10, 0x42, 0xe7 }, + { 0x2a, 0x17, 0x43, 0xe7 }, + { 0x2b, 0x1a, 0x43, 0xe7 }, + }, + + /* voltage swing 2, pre-emphasis 0->1 */ + { + { 0x28, 0x10, 0x42, 0xe7 }, + { 0x2b, 0x17, 0x43, 0xe7 }, + }, + + /* voltage swing 3, pre-emphasis 0 */ + { + { 0x28, 0x10, 0x43, 0xe7 }, + }, +}; + +static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr3[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { + { 0x21, 0x10, 0x42, 0xe5 }, + { 0x26, 0x14, 0x42, 0xe5 }, + { 0x26, 0x16, 0x43, 0xe5 }, + { 0x29, 0x18, 0x43, 0xe7 }, + }, + + /* voltage swing 1, pre-emphasis 0->2 */ + { + { 0x24, 0x10, 0x42, 0xe7 }, + { 0x2a, 0x18, 0x43, 0xe7 }, + { 0x2b, 0x1b, 0x43, 0xe7 } + }, + + /* voltage swing 2, pre-emphasis 0->1 */ + { + { 0x27, 0x10, 0x42, 0xe7 }, + { 0x2b, 0x18, 0x43, 0xe7 } + }, + + /* voltage swing 3, pre-emphasis 0 */ + { + { 0x28, 0x10, 0x43, 0xe7 }, + }, +}; + +static const struct reg_sequence rk_udphy_24m_refclk_cfg[] = { + {0x0090, 0x68}, {0x0094, 0x68}, + {0x0128, 0x24}, {0x012c, 0x44}, + {0x0130, 0x3f}, {0x0134, 0x44}, + {0x015c, 0xa9}, {0x0160, 0x71}, + {0x0164, 0x71}, {0x0168, 0xa9}, + {0x0174, 0xa9}, {0x0178, 0x71}, + {0x017c, 0x71}, {0x0180, 0xa9}, + {0x018c, 0x41}, {0x0190, 0x00}, + {0x0194, 0x05}, {0x01ac, 0x2a}, + {0x01b0, 0x17}, {0x01b4, 0x17}, + {0x01b8, 0x2a}, {0x01c8, 0x04}, + {0x01cc, 0x08}, {0x01d0, 0x08}, + {0x01d4, 0x04}, {0x01d8, 0x20}, + {0x01dc, 0x01}, {0x01e0, 0x09}, + {0x01e4, 0x03}, {0x01f0, 0x29}, + {0x01f4, 0x02}, {0x01f8, 0x02}, + {0x01fc, 0x29}, {0x0208, 0x2a}, + {0x020c, 0x17}, {0x0210, 0x17}, + {0x0214, 0x2a}, {0x0224, 0x20}, + {0x03f0, 0x0a}, {0x03f4, 0x07}, + {0x03f8, 0x07}, {0x03fc, 0x0c}, + {0x0404, 0x12}, {0x0408, 0x1a}, + {0x040c, 0x1a}, {0x0410, 0x3f}, + {0x0ce0, 0x68}, {0x0ce8, 0xd0}, + {0x0cf0, 0x87}, {0x0cf8, 0x70}, + {0x0d00, 0x70}, {0x0d08, 0xa9}, + {0x1ce0, 0x68}, {0x1ce8, 0xd0}, + {0x1cf0, 0x87}, {0x1cf8, 0x70}, + {0x1d00, 0x70}, {0x1d08, 0xa9}, + {0x0a3c, 0xd0}, {0x0a44, 0xd0}, + {0x0a48, 0x01}, {0x0a4c, 0x0d}, + {0x0a54, 0xe0}, {0x0a5c, 0xe0}, + {0x0a64, 0xa8}, {0x1a3c, 0xd0}, + {0x1a44, 0xd0}, {0x1a48, 0x01}, + {0x1a4c, 0x0d}, {0x1a54, 0xe0}, + {0x1a5c, 0xe0}, {0x1a64, 0xa8} +}; + +static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = { + {0x0830, 0x07}, {0x085c, 0x80}, + {0x1030, 0x07}, {0x105c, 0x80}, + {0x1830, 0x07}, {0x185c, 0x80}, + {0x2030, 0x07}, {0x205c, 0x80}, + {0x0228, 0x38}, {0x0104, 0x44}, + {0x0248, 0x44}, {0x038c, 0x02}, + {0x0878, 0x04}, {0x1878, 0x04}, + {0x0898, 0x77}, {0x1898, 0x77}, + {0x0054, 0x01}, {0x00e0, 0x38}, + {0x0060, 0x24}, {0x0064, 0x77}, + {0x0070, 0x76}, {0x0234, 0xe8}, + {0x0af4, 0x15}, {0x1af4, 0x15}, + {0x081c, 0xe5}, {0x181c, 0xe5}, + {0x099c, 0x48}, {0x199c, 0x48}, + {0x09a4, 0x07}, {0x09a8, 0x22}, + {0x19a4, 0x07}, {0x19a8, 0x22}, + {0x09b8, 0x3e}, {0x19b8, 0x3e}, + {0x09e4, 0x02}, {0x19e4, 0x02}, + {0x0a34, 0x1e}, {0x1a34, 0x1e}, + {0x0a98, 0x2f}, {0x1a98, 0x2f}, + {0x0c30, 0x0e}, {0x0c48, 0x06}, + {0x1c30, 0x0e}, {0x1c48, 0x06}, + {0x028c, 0x18}, {0x0af0, 0x00}, + {0x1af0, 0x00} +}; + +static const struct reg_sequence rk_udphy_init_sequence[] = { + {0x0104, 0x44}, {0x0234, 0xe8}, + {0x0248, 0x44}, {0x028c, 0x18}, + {0x081c, 0xe5}, {0x0878, 0x00}, + {0x0994, 0x1c}, {0x0af0, 0x00}, + {0x181c, 0xe5}, {0x1878, 0x00}, + {0x1994, 0x1c}, {0x1af0, 0x00}, + {0x0428, 0x60}, {0x0d58, 0x33}, + {0x1d58, 0x33}, {0x0990, 0x74}, + {0x0d64, 0x17}, {0x08c8, 0x13}, + {0x1990, 0x74}, {0x1d64, 0x17}, + {0x18c8, 0x13}, {0x0d90, 0x40}, + {0x0da8, 0x40}, {0x0dc0, 0x40}, + {0x0dd8, 0x40}, {0x1d90, 0x40}, + {0x1da8, 0x40}, {0x1dc0, 0x40}, + {0x1dd8, 0x40}, {0x03c0, 0x30}, + {0x03c4, 0x06}, {0x0e10, 0x00}, + {0x1e10, 0x00}, {0x043c, 0x0f}, + {0x0d2c, 0xff}, {0x1d2c, 0xff}, + {0x0d34, 0x0f}, {0x1d34, 0x0f}, + {0x08fc, 0x2a}, {0x0914, 0x28}, + {0x0a30, 0x03}, {0x0e38, 0x03}, + {0x0ecc, 0x27}, {0x0ed0, 0x22}, + {0x0ed4, 0x26}, {0x18fc, 0x2a}, + {0x1914, 0x28}, {0x1a30, 0x03}, + {0x1e38, 0x03}, {0x1ecc, 0x27}, + {0x1ed0, 0x22}, {0x1ed4, 0x26}, + {0x0048, 0x0f}, {0x0060, 0x3c}, + {0x0064, 0xf7}, {0x006c, 0x20}, + {0x0070, 0x7d}, {0x0074, 0x68}, + {0x0af4, 0x1a}, {0x1af4, 0x1a}, + {0x0440, 0x3f}, {0x10d4, 0x08}, + {0x20d4, 0x08}, {0x00d4, 0x30}, + {0x0024, 0x6e}, +}; + +static inline int rk_udphy_grfreg_write(struct regmap *base, + const struct rk_udphy_grf_reg *reg, bool en) +{ + return regmap_write(base, reg->offset, en ? reg->enable : reg->disable); +} + +static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev) +{ + int i; + + udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks); + if (udphy->num_clks < 1) + return -ENODEV; + + /* used for configure phy reference clock frequency */ + for (i = 0; i < udphy->num_clks; i++) { + if (!strncmp(udphy->clks[i].id, "refclk", 6)) { + udphy->refclk = udphy->clks[i].clk; + break; + } + } + + if (!udphy->refclk) + return dev_err_probe(udphy->dev, -EINVAL, "no refclk found\n"); + + return 0; +} + +static int rk_udphy_reset_assert_all(struct rk_udphy *udphy) +{ + return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts); +} + +static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy) +{ + return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts); +} + +static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name) +{ + struct reset_control_bulk_data *list = udphy->rsts; + int idx; + + for (idx = 0; idx < udphy->num_rsts; idx++) { + if (!strcmp(list[idx].id, name)) + return reset_control_deassert(list[idx].rstc); + } + + return -EINVAL; +} + +static int rk_udphy_reset_init(struct rk_udphy *udphy, struct device *dev) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + int idx; + + udphy->num_rsts = cfg->num_rsts; + udphy->rsts = devm_kcalloc(dev, udphy->num_rsts, + sizeof(*udphy->rsts), GFP_KERNEL); + if (!udphy->rsts) + return -ENOMEM; + + for (idx = 0; idx < cfg->num_rsts; idx++) + udphy->rsts[idx].id = cfg->rst_list[idx]; + + return devm_reset_control_bulk_get_exclusive(dev, cfg->num_rsts, + udphy->rsts); +} + +static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + const struct rk_udphy_grf_reg *preg; + + preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg; + rk_udphy_grfreg_write(udphy->usbgrf, preg, disable); +} + +static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + + rk_udphy_grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_phy_con, enable); + rk_udphy_grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_grf_con, enable); +} + +/* + * In usb/dp combo phy driver, here are 2 ways to mapping lanes. + * + * 1 Type-C Mapping table (DP_Alt_Mode V1.0b remove ABF pin mapping) + * --------------------------------------------------------------------------- + * Type-C Pin B11-B10 A2-A3 A11-A10 B2-B3 + * PHY Pad ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) + * C/E(Normal) dpln3 dpln2 dpln0 dpln1 + * C/E(Flip ) dpln0 dpln1 dpln3 dpln2 + * D/F(Normal) usbrx usbtx dpln0 dpln1 + * D/F(Flip ) dpln0 dpln1 usbrx usbtx + * A(Normal ) dpln3 dpln1 dpln2 dpln0 + * A(Flip ) dpln2 dpln0 dpln3 dpln1 + * B(Normal ) usbrx usbtx dpln1 dpln0 + * B(Flip ) dpln1 dpln0 usbrx usbtx + * --------------------------------------------------------------------------- + * + * 2 Mapping the lanes in dtsi + * if all 4 lane assignment for dp function, define rockchip,dp-lane-mux = <x x x x>; + * sample as follow: + * --------------------------------------------------------------------------- + * B11-B10 A2-A3 A11-A10 B2-B3 + * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) + * <0 1 2 3> dpln0 dpln1 dpln2 dpln3 + * <2 3 0 1> dpln2 dpln3 dpln0 dpln1 + * --------------------------------------------------------------------------- + * if 2 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x x>; + * sample as follow: + * --------------------------------------------------------------------------- + * B11-B10 A2-A3 A11-A10 B2-B3 + * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx) + * <0 1> dpln0 dpln1 usbrx usbtx + * <2 3> usbrx usbtx dpln0 dpln1 + * --------------------------------------------------------------------------- + */ + +static void rk_udphy_dplane_select(struct rk_udphy *udphy) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + u32 value = 0; + + switch (udphy->mode) { + case UDPHY_MODE_DP: + value |= 2 << udphy->dp_lane_sel[2] * 2; + value |= 3 << udphy->dp_lane_sel[3] * 2; + fallthrough; + + case UDPHY_MODE_DP_USB: + value |= 0 << udphy->dp_lane_sel[0] * 2; + value |= 1 << udphy->dp_lane_sel[1] * 2; + break; + + case UDPHY_MODE_USB: + break; + + default: + break; + } + + regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, + ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) | + FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) | + FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value); +} + +static int rk_udphy_dplane_get(struct rk_udphy *udphy) +{ + int dp_lanes; + + switch (udphy->mode) { + case UDPHY_MODE_DP: + dp_lanes = 4; + break; + + case UDPHY_MODE_DP_USB: + dp_lanes = 2; + break; + + case UDPHY_MODE_USB: + default: + dp_lanes = 0; + break; + } + + return dp_lanes; +} + +static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes) +{ + u32 val = 0; + int i; + + for (i = 0; i < dp_lanes; i++) + val |= BIT(udphy->dp_lane_sel[i]); + + regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, CMN_DP_LANE_EN_ALL, + FIELD_PREP(CMN_DP_LANE_EN_ALL, val)); + + if (!dp_lanes) + regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, + CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0)); +} + +static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + + udphy->dp_sink_hpd_sel = true; + udphy->dp_sink_hpd_cfg = hpd; + + if (!udphy->dp_in_use) + return; + + rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd); +} + +static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy) +{ + if (udphy->flip) { + udphy->dp_lane_sel[0] = 0; + udphy->dp_lane_sel[1] = 1; + udphy->dp_lane_sel[2] = 3; + udphy->dp_lane_sel[3] = 2; + udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB; + udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT; + udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT; + gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1); + gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0); + } else { + udphy->dp_lane_sel[0] = 2; + udphy->dp_lane_sel[1] = 3; + udphy->dp_lane_sel[2] = 1; + udphy->dp_lane_sel[3] = 0; + udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; + udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL; + udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL; + gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0); + gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1); + } + + udphy->mode = UDPHY_MODE_DP_USB; +} + +static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw, + enum typec_orientation orien) +{ + struct rk_udphy *udphy = typec_switch_get_drvdata(sw); + + mutex_lock(&udphy->mutex); + + if (orien == TYPEC_ORIENTATION_NONE) { + gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0); + gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0); + /* unattached */ + rk_udphy_usb_bvalid_enable(udphy, false); + goto unlock_ret; + } + + udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false; + rk_udphy_set_typec_default_mapping(udphy); + rk_udphy_usb_bvalid_enable(udphy, true); + +unlock_ret: + mutex_unlock(&udphy->mutex); + return 0; +} + +static void rk_udphy_orien_switch_unregister(void *data) +{ + struct rk_udphy *udphy = data; + + typec_switch_unregister(udphy->sw); +} + +static int rk_udphy_setup_orien_switch(struct rk_udphy *udphy) +{ + struct typec_switch_desc sw_desc = { }; + + sw_desc.drvdata = udphy; + sw_desc.fwnode = dev_fwnode(udphy->dev); + sw_desc.set = rk_udphy_orien_sw_set; + + udphy->sw = typec_switch_register(udphy->dev, &sw_desc); + if (IS_ERR(udphy->sw)) { + dev_err(udphy->dev, "Error register typec orientation switch: %ld\n", + PTR_ERR(udphy->sw)); + return PTR_ERR(udphy->sw); + } + + return devm_add_action_or_reset(udphy->dev, + rk_udphy_orien_switch_unregister, udphy); +} + +static int rk_udphy_refclk_set(struct rk_udphy *udphy) +{ + unsigned long rate; + int ret; + + /* configure phy reference clock */ + rate = clk_get_rate(udphy->refclk); + dev_dbg(udphy->dev, "refclk freq %ld\n", rate); + + switch (rate) { + case 24000000: + ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_24m_refclk_cfg, + ARRAY_SIZE(rk_udphy_24m_refclk_cfg)); + if (ret) + return ret; + break; + + case 26000000: + /* register default is 26MHz */ + ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_26m_refclk_cfg, + ARRAY_SIZE(rk_udphy_26m_refclk_cfg)); + if (ret) + return ret; + break; + + default: + dev_err(udphy->dev, "unsupported refclk freq %ld\n", rate); + return -EINVAL; + } + + return 0; +} + +static int rk_udphy_status_check(struct rk_udphy *udphy) +{ + unsigned int val; + int ret; + + /* LCPLL check */ + if (udphy->mode & UDPHY_MODE_USB) { + ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET, + val, (val & CMN_ANA_LCPLL_AFC_DONE) && + (val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000); + if (ret) { + dev_err(udphy->dev, "cmn ana lcpll lock timeout\n"); + /* + * If earlier software (U-Boot) enabled USB once already + * the PLL may have problems locking on the first try. + * It will be successful on the second try, so for the + * time being a -EPROBE_DEFER will solve the issue. + * + * This requires further investigation to understand the + * root cause, especially considering that the driver is + * asserting all reset lines at probe time. + */ + return -EPROBE_DEFER; + } + + if (!udphy->flip) { + ret = regmap_read_poll_timeout(udphy->pma_regmap, + TRSV_LN0_MON_RX_CDR_DONE_OFFSET, val, + val & TRSV_LN0_MON_RX_CDR_LOCK_DONE, + 200, 100000); + if (ret) + dev_err(udphy->dev, "trsv ln0 mon rx cdr lock timeout\n"); + } else { + ret = regmap_read_poll_timeout(udphy->pma_regmap, + TRSV_LN2_MON_RX_CDR_DONE_OFFSET, val, + val & TRSV_LN2_MON_RX_CDR_LOCK_DONE, + 200, 100000); + if (ret) + dev_err(udphy->dev, "trsv ln2 mon rx cdr lock timeout\n"); + } + } + + return 0; +} + +static int rk_udphy_init(struct rk_udphy *udphy) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + int ret; + + rk_udphy_reset_assert_all(udphy); + usleep_range(10000, 11000); + + /* enable rx lfps for usb */ + if (udphy->mode & UDPHY_MODE_USB) + rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true); + + /* Step 1: power on pma and deassert apb rstn */ + rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true); + + rk_udphy_reset_deassert(udphy, "pma_apb"); + rk_udphy_reset_deassert(udphy, "pcs_apb"); + + /* Step 2: set init sequence and phy refclk */ + ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence, + ARRAY_SIZE(rk_udphy_init_sequence)); + if (ret) { + dev_err(udphy->dev, "init sequence set error %d\n", ret); + goto assert_resets; + } + + ret = rk_udphy_refclk_set(udphy); + if (ret) { + dev_err(udphy->dev, "refclk set error %d\n", ret); + goto assert_resets; + } + + /* Step 3: configure lane mux */ + regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, + CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL, + FIELD_PREP(CMN_DP_LANE_MUX_N(3), udphy->lane_mux_sel[3]) | + FIELD_PREP(CMN_DP_LANE_MUX_N(2), udphy->lane_mux_sel[2]) | + FIELD_PREP(CMN_DP_LANE_MUX_N(1), udphy->lane_mux_sel[1]) | + FIELD_PREP(CMN_DP_LANE_MUX_N(0), udphy->lane_mux_sel[0]) | + FIELD_PREP(CMN_DP_LANE_EN_ALL, 0)); + + /* Step 4: deassert init rstn and wait for 200ns from datasheet */ + if (udphy->mode & UDPHY_MODE_USB) + rk_udphy_reset_deassert(udphy, "init"); + + if (udphy->mode & UDPHY_MODE_DP) { + regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, + CMN_DP_INIT_RSTN, + FIELD_PREP(CMN_DP_INIT_RSTN, 0x1)); + } + + udelay(1); + + /* Step 5: deassert cmn/lane rstn */ + if (udphy->mode & UDPHY_MODE_USB) { + rk_udphy_reset_deassert(udphy, "cmn"); + rk_udphy_reset_deassert(udphy, "lane"); + } + + /* Step 6: wait for lock done of pll */ + ret = rk_udphy_status_check(udphy); + if (ret) + goto assert_resets; + + return 0; + +assert_resets: + rk_udphy_reset_assert_all(udphy); + return ret; +} + +static int rk_udphy_setup(struct rk_udphy *udphy) +{ + int ret; + + ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks); + if (ret) { + dev_err(udphy->dev, "failed to enable clk\n"); + return ret; + } + + ret = rk_udphy_init(udphy); + if (ret) { + dev_err(udphy->dev, "failed to init combophy\n"); + clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks); + return ret; + } + + return 0; +} + +static void rk_udphy_disable(struct rk_udphy *udphy) +{ + clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks); + rk_udphy_reset_assert_all(udphy); +} + +static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy) +{ + int ret, i, num_lanes; + + num_lanes = device_property_count_u32(udphy->dev, "rockchip,dp-lane-mux"); + if (num_lanes < 0) { + dev_dbg(udphy->dev, "no dp-lane-mux, following dp alt mode\n"); + udphy->mode = UDPHY_MODE_USB; + return 0; + } + + if (num_lanes != 2 && num_lanes != 4) + return dev_err_probe(udphy->dev, -EINVAL, + "invalid number of lane mux\n"); + + ret = device_property_read_u32_array(udphy->dev, "rockchip,dp-lane-mux", + udphy->dp_lane_sel, num_lanes); + if (ret) + return dev_err_probe(udphy->dev, ret, "get dp lane mux failed\n"); + + for (i = 0; i < num_lanes; i++) { + int j; + + if (udphy->dp_lane_sel[i] > 3) + return dev_err_probe(udphy->dev, -EINVAL, + "lane mux between 0 and 3, exceeding the range\n"); + + udphy->lane_mux_sel[udphy->dp_lane_sel[i]] = PHY_LANE_MUX_DP; + + for (j = i + 1; j < num_lanes; j++) { + if (udphy->dp_lane_sel[i] == udphy->dp_lane_sel[j]) + return dev_err_probe(udphy->dev, -EINVAL, + "set repeat lane mux value\n"); + } + } + + udphy->mode = UDPHY_MODE_DP; + if (num_lanes == 2) { + udphy->mode |= UDPHY_MODE_USB; + udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP); + } + + return 0; +} + +static int rk_udphy_get_initial_status(struct rk_udphy *udphy) +{ + int ret; + u32 value; + + ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks); + if (ret) { + dev_err(udphy->dev, "failed to enable clk\n"); + return ret; + } + + rk_udphy_reset_deassert_all(udphy); + + regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value); + if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) + udphy->status = UDPHY_MODE_DP; + else + rk_udphy_disable(udphy); + + return 0; +} + +static int rk_udphy_parse_dt(struct rk_udphy *udphy) +{ + struct device *dev = udphy->dev; + struct device_node *np = dev_of_node(dev); + enum usb_device_speed maximum_speed; + int ret; + + udphy->u2phygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,u2phy-grf"); + if (IS_ERR(udphy->u2phygrf)) + return dev_err_probe(dev, PTR_ERR(udphy->u2phygrf), "failed to get u2phy-grf\n"); + + udphy->udphygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbdpphy-grf"); + if (IS_ERR(udphy->udphygrf)) + return dev_err_probe(dev, PTR_ERR(udphy->udphygrf), "failed to get usbdpphy-grf\n"); + + udphy->usbgrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usb-grf"); + if (IS_ERR(udphy->usbgrf)) + return dev_err_probe(dev, PTR_ERR(udphy->usbgrf), "failed to get usb-grf\n"); + + udphy->vogrf = syscon_regmap_lookup_by_phandle(np, "rockchip,vo-grf"); + if (IS_ERR(udphy->vogrf)) + return dev_err_probe(dev, PTR_ERR(udphy->vogrf), "failed to get vo-grf\n"); + + ret = rk_udphy_parse_lane_mux_data(udphy); + if (ret) + return ret; + + udphy->sbu1_dc_gpio = devm_gpiod_get_optional(dev, "sbu1-dc", GPIOD_OUT_LOW); + if (IS_ERR(udphy->sbu1_dc_gpio)) + return PTR_ERR(udphy->sbu1_dc_gpio); + + udphy->sbu2_dc_gpio = devm_gpiod_get_optional(dev, "sbu2-dc", GPIOD_OUT_LOW); + if (IS_ERR(udphy->sbu2_dc_gpio)) + return PTR_ERR(udphy->sbu2_dc_gpio); + + if (device_property_present(dev, "maximum-speed")) { + maximum_speed = usb_get_maximum_speed(dev); + udphy->hs = maximum_speed <= USB_SPEED_HIGH ? true : false; + } + + ret = rk_udphy_clk_init(udphy, dev); + if (ret) + return ret; + + return rk_udphy_reset_init(udphy, dev); +} + +static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode) +{ + int ret; + + if (!(udphy->mode & mode)) { + dev_info(udphy->dev, "mode 0x%02x is not support\n", mode); + return 0; + } + + if (udphy->status == UDPHY_MODE_NONE) { + udphy->mode_change = false; + ret = rk_udphy_setup(udphy); + if (ret) + return ret; + + if (udphy->mode & UDPHY_MODE_USB) + rk_udphy_u3_port_disable(udphy, false); + } else if (udphy->mode_change) { + udphy->mode_change = false; + udphy->status = UDPHY_MODE_NONE; + if (udphy->mode == UDPHY_MODE_DP) + rk_udphy_u3_port_disable(udphy, true); + + rk_udphy_disable(udphy); + ret = rk_udphy_setup(udphy); + if (ret) + return ret; + } + + udphy->status |= mode; + + return 0; +} + +static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode) +{ + if (!(udphy->mode & mode)) { + dev_info(udphy->dev, "mode 0x%02x is not support\n", mode); + return; + } + + if (!udphy->status) + return; + + udphy->status &= ~mode; + + if (udphy->status == UDPHY_MODE_NONE) + rk_udphy_disable(udphy); +} + +static int rk_udphy_dp_phy_init(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + + mutex_lock(&udphy->mutex); + + udphy->dp_in_use = true; + rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg); + + mutex_unlock(&udphy->mutex); + + return 0; +} + +static int rk_udphy_dp_phy_exit(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + + mutex_lock(&udphy->mutex); + udphy->dp_in_use = false; + mutex_unlock(&udphy->mutex); + return 0; +} + +static int rk_udphy_dp_phy_power_on(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + int ret, dp_lanes; + + mutex_lock(&udphy->mutex); + + dp_lanes = rk_udphy_dplane_get(udphy); + phy_set_bus_width(phy, dp_lanes); + + ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP); + if (ret) + goto unlock; + + rk_udphy_dplane_enable(udphy, dp_lanes); + + rk_udphy_dplane_select(udphy); + +unlock: + mutex_unlock(&udphy->mutex); + /* + * If data send by aux channel too fast after phy power on, + * the aux may be not ready which will cause aux error. Adding + * delay to avoid this issue. + */ + usleep_range(10000, 11000); + return ret; +} + +static int rk_udphy_dp_phy_power_off(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + + mutex_lock(&udphy->mutex); + rk_udphy_dplane_enable(udphy, 0); + rk_udphy_power_off(udphy, UDPHY_MODE_DP); + mutex_unlock(&udphy->mutex); + + return 0; +} + +static int rk_udphy_dp_phy_verify_link_rate(unsigned int link_rate) +{ + switch (link_rate) { + case 1620: + case 2700: + case 5400: + case 8100: + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int rk_udphy_dp_phy_verify_config(struct rk_udphy *udphy, + struct phy_configure_opts_dp *dp) +{ + int i, ret; + + /* If changing link rate was required, verify it's supported. */ + ret = rk_udphy_dp_phy_verify_link_rate(dp->link_rate); + if (ret) + return ret; + + /* Verify lane count. */ + switch (dp->lanes) { + case 1: + case 2: + case 4: + /* valid lane count. */ + break; + + default: + return -EINVAL; + } + + /* + * If changing voltages is required, check swing and pre-emphasis + * levels, per-lane. + */ + if (dp->set_voltages) { + /* Lane count verified previously. */ + for (i = 0; i < dp->lanes; i++) { + if (dp->voltage[i] > 3 || dp->pre[i] > 3) + return -EINVAL; + + /* + * Sum of voltage swing and pre-emphasis levels cannot + * exceed 3. + */ + if (dp->voltage[i] + dp->pre[i] > 3) + return -EINVAL; + } + } + + return 0; +} + +static void rk_udphy_dp_set_voltage(struct rk_udphy *udphy, u8 bw, + u32 voltage, u32 pre, u32 lane) +{ + const struct rk_udphy_cfg *cfg = udphy->cfgs; + const struct rk_udphy_dp_tx_drv_ctrl (*dp_ctrl)[4]; + u32 offset = 0x800 * lane; + u32 val; + + if (udphy->mux) + dp_ctrl = cfg->dp_tx_ctrl_cfg_typec[bw]; + else + dp_ctrl = cfg->dp_tx_ctrl_cfg[bw]; + + val = dp_ctrl[voltage][pre].trsv_reg0204; + regmap_write(udphy->pma_regmap, 0x0810 + offset, val); + + val = dp_ctrl[voltage][pre].trsv_reg0205; + regmap_write(udphy->pma_regmap, 0x0814 + offset, val); + + val = dp_ctrl[voltage][pre].trsv_reg0206; + regmap_write(udphy->pma_regmap, 0x0818 + offset, val); + + val = dp_ctrl[voltage][pre].trsv_reg0207; + regmap_write(udphy->pma_regmap, 0x081c + offset, val); +} + +static int rk_udphy_dp_phy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + struct phy_configure_opts_dp *dp = &opts->dp; + u32 i, val, lane; + int ret; + + ret = rk_udphy_dp_phy_verify_config(udphy, dp); + if (ret) + return ret; + + if (dp->set_rate) { + regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, + CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0)); + + switch (dp->link_rate) { + case 1620: + udphy->bw = DP_BW_RBR; + break; + + case 2700: + udphy->bw = DP_BW_HBR; + break; + + case 5400: + udphy->bw = DP_BW_HBR2; + break; + + case 8100: + udphy->bw = DP_BW_HBR3; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(udphy->pma_regmap, CMN_DP_LINK_OFFSET, CMN_DP_TX_LINK_BW, + FIELD_PREP(CMN_DP_TX_LINK_BW, udphy->bw)); + regmap_update_bits(udphy->pma_regmap, CMN_SSC_EN_OFFSET, CMN_ROPLL_SSC_EN, + FIELD_PREP(CMN_ROPLL_SSC_EN, dp->ssc)); + regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, CMN_DP_CMN_RSTN, + FIELD_PREP(CMN_DP_CMN_RSTN, 0x1)); + + ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_ROPLL_DONE_OFFSET, val, + FIELD_GET(CMN_ANA_ROPLL_LOCK_DONE, val) && + FIELD_GET(CMN_ANA_ROPLL_AFC_DONE, val), + 0, 1000); + if (ret) { + dev_err(udphy->dev, "ROPLL is not lock, set_rate failed\n"); + return ret; + } + } + + if (dp->set_voltages) { + for (i = 0; i < dp->lanes; i++) { + lane = udphy->dp_lane_sel[i]; + switch (dp->link_rate) { + case 1620: + case 2700: + regmap_update_bits(udphy->pma_regmap, + TRSV_ANA_TX_CLK_OFFSET_N(lane), + LN_ANA_TX_SER_TXCLK_INV, + FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, + udphy->lane_mux_sel[lane])); + break; + + case 5400: + case 8100: + regmap_update_bits(udphy->pma_regmap, + TRSV_ANA_TX_CLK_OFFSET_N(lane), + LN_ANA_TX_SER_TXCLK_INV, + FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, 0x0)); + break; + } + + rk_udphy_dp_set_voltage(udphy, udphy->bw, dp->voltage[i], + dp->pre[i], lane); + } + } + + return 0; +} + +static const struct phy_ops rk_udphy_dp_phy_ops = { + .init = rk_udphy_dp_phy_init, + .exit = rk_udphy_dp_phy_exit, + .power_on = rk_udphy_dp_phy_power_on, + .power_off = rk_udphy_dp_phy_power_off, + .configure = rk_udphy_dp_phy_configure, + .owner = THIS_MODULE, +}; + +static int rk_udphy_usb3_phy_init(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + int ret = 0; + + mutex_lock(&udphy->mutex); + /* DP only or high-speed, disable U3 port */ + if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) { + rk_udphy_u3_port_disable(udphy, true); + goto unlock; + } + + ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB); + +unlock: + mutex_unlock(&udphy->mutex); + return ret; +} + +static int rk_udphy_usb3_phy_exit(struct phy *phy) +{ + struct rk_udphy *udphy = phy_get_drvdata(phy); + + mutex_lock(&udphy->mutex); + /* DP only or high-speed */ + if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) + goto unlock; + + rk_udphy_power_off(udphy, UDPHY_MODE_USB); + +unlock: + mutex_unlock(&udphy->mutex); + return 0; +} + +static const struct phy_ops rk_udphy_usb3_phy_ops = { + .init = rk_udphy_usb3_phy_init, + .exit = rk_udphy_usb3_phy_exit, + .owner = THIS_MODULE, +}; + +static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct rk_udphy *udphy = typec_mux_get_drvdata(mux); + u8 mode; + + mutex_lock(&udphy->mutex); + + switch (state->mode) { + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; + mode = UDPHY_MODE_DP; + break; + + case TYPEC_DP_STATE_D: + default: + if (udphy->flip) { + udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB; + } else { + udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB; + udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP; + udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP; + } + mode = UDPHY_MODE_DP_USB; + break; + } + + if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) { + struct typec_displayport_data *data = state->data; + + if (!data) { + rk_udphy_dp_hpd_event_trigger(udphy, false); + } else if (data->status & DP_STATUS_IRQ_HPD) { + rk_udphy_dp_hpd_event_trigger(udphy, false); + usleep_range(750, 800); + rk_udphy_dp_hpd_event_trigger(udphy, true); + } else if (data->status & DP_STATUS_HPD_STATE) { + if (udphy->mode != mode) { + udphy->mode = mode; + udphy->mode_change = true; + } + rk_udphy_dp_hpd_event_trigger(udphy, true); + } else { + rk_udphy_dp_hpd_event_trigger(udphy, false); + } + } + + mutex_unlock(&udphy->mutex); + return 0; +} + +static void rk_udphy_typec_mux_unregister(void *data) +{ + struct rk_udphy *udphy = data; + + typec_mux_unregister(udphy->mux); +} + +static int rk_udphy_setup_typec_mux(struct rk_udphy *udphy) +{ + struct typec_mux_desc mux_desc = {}; + + mux_desc.drvdata = udphy; + mux_desc.fwnode = dev_fwnode(udphy->dev); + mux_desc.set = rk_udphy_typec_mux_set; + + udphy->mux = typec_mux_register(udphy->dev, &mux_desc); + if (IS_ERR(udphy->mux)) { + dev_err(udphy->dev, "Error register typec mux: %ld\n", + PTR_ERR(udphy->mux)); + return PTR_ERR(udphy->mux); + } + + return devm_add_action_or_reset(udphy->dev, rk_udphy_typec_mux_unregister, + udphy); +} + +static const struct regmap_config rk_udphy_pma_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .max_register = 0x20dc, +}; + +static struct phy *rk_udphy_phy_xlate(struct device *dev, const struct of_phandle_args *args) +{ + struct rk_udphy *udphy = dev_get_drvdata(dev); + + if (args->args_count == 0) + return ERR_PTR(-EINVAL); + + switch (args->args[0]) { + case PHY_TYPE_USB3: + return udphy->phy_u3; + case PHY_TYPE_DP: + return udphy->phy_dp; + } + + return ERR_PTR(-EINVAL); +} + +static int rk_udphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct resource *res; + struct rk_udphy *udphy; + void __iomem *base; + int id, ret; + + udphy = devm_kzalloc(dev, sizeof(*udphy), GFP_KERNEL); + if (!udphy) + return -ENOMEM; + + udphy->cfgs = device_get_match_data(dev); + if (!udphy->cfgs) + return dev_err_probe(dev, -EINVAL, "missing match data\n"); + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* find the phy-id from the io address */ + udphy->id = -ENODEV; + for (id = 0; id < udphy->cfgs->num_phys; id++) { + if (res->start == udphy->cfgs->phy_ids[id]) { + udphy->id = id; + break; + } + } + + if (udphy->id < 0) + return dev_err_probe(dev, -ENODEV, "no matching device found\n"); + + udphy->pma_regmap = devm_regmap_init_mmio(dev, base + UDPHY_PMA, + &rk_udphy_pma_regmap_cfg); + if (IS_ERR(udphy->pma_regmap)) + return PTR_ERR(udphy->pma_regmap); + + udphy->dev = dev; + ret = rk_udphy_parse_dt(udphy); + if (ret) + return ret; + + ret = rk_udphy_get_initial_status(udphy); + if (ret) + return ret; + + mutex_init(&udphy->mutex); + platform_set_drvdata(pdev, udphy); + + if (device_property_present(dev, "orientation-switch")) { + ret = rk_udphy_setup_orien_switch(udphy); + if (ret) + return ret; + } + + if (device_property_present(dev, "mode-switch")) { + ret = rk_udphy_setup_typec_mux(udphy); + if (ret) + return ret; + } + + udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops); + if (IS_ERR(udphy->phy_u3)) { + ret = PTR_ERR(udphy->phy_u3); + return dev_err_probe(dev, ret, "failed to create USB3 phy\n"); + } + phy_set_drvdata(udphy->phy_u3, udphy); + + udphy->phy_dp = devm_phy_create(dev, dev->of_node, &rk_udphy_dp_phy_ops); + if (IS_ERR(udphy->phy_dp)) { + ret = PTR_ERR(udphy->phy_dp); + return dev_err_probe(dev, ret, "failed to create DP phy\n"); + } + phy_set_bus_width(udphy->phy_dp, rk_udphy_dplane_get(udphy)); + udphy->phy_dp->attrs.max_link_rate = 8100; + phy_set_drvdata(udphy->phy_dp, udphy); + + phy_provider = devm_of_phy_provider_register(dev, rk_udphy_phy_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + return dev_err_probe(dev, ret, "failed to register phy provider\n"); + } + + return 0; +} + +static int __maybe_unused rk_udphy_resume(struct device *dev) +{ + struct rk_udphy *udphy = dev_get_drvdata(dev); + + if (udphy->dp_sink_hpd_sel) + rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg); + + return 0; +} + +static const struct dev_pm_ops rk_udphy_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume) +}; + +static const char * const rk_udphy_rst_list[] = { + "init", "cmn", "lane", "pcs_apb", "pma_apb" +}; + +static const struct rk_udphy_cfg rk3588_udphy_cfgs = { + .num_phys = 2, + .phy_ids = { + 0xfed80000, + 0xfed90000, + }, + .num_rsts = ARRAY_SIZE(rk_udphy_rst_list), + .rst_list = rk_udphy_rst_list, + .grfcfg = { + /* u2phy-grf */ + .bvalid_phy_con = RK_UDPHY_GEN_GRF_REG(0x0008, 1, 0, 0x2, 0x3), + .bvalid_grf_con = RK_UDPHY_GEN_GRF_REG(0x0010, 3, 2, 0x2, 0x3), + + /* usb-grf */ + .usb3otg0_cfg = RK_UDPHY_GEN_GRF_REG(0x001c, 15, 0, 0x1100, 0x0188), + .usb3otg1_cfg = RK_UDPHY_GEN_GRF_REG(0x0034, 15, 0, 0x1100, 0x0188), + + /* usbdpphy-grf */ + .low_pwrn = RK_UDPHY_GEN_GRF_REG(0x0004, 13, 13, 0, 1), + .rx_lfps = RK_UDPHY_GEN_GRF_REG(0x0004, 14, 14, 0, 1), + }, + .vogrfcfg = { + { + .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3), + .dp_lane_reg = 0x0000, + }, + { + .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3), + .dp_lane_reg = 0x0008, + }, + }, + .dp_tx_ctrl_cfg = { + rk3588_dp_tx_drv_ctrl_rbr_hbr, + rk3588_dp_tx_drv_ctrl_rbr_hbr, + rk3588_dp_tx_drv_ctrl_hbr2, + rk3588_dp_tx_drv_ctrl_hbr3, + }, + .dp_tx_ctrl_cfg_typec = { + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_hbr2, + rk3588_dp_tx_drv_ctrl_hbr3, + }, +}; + +static const struct of_device_id rk_udphy_dt_match[] = { + { + .compatible = "rockchip,rk3588-usbdp-phy", + .data = &rk3588_udphy_cfgs + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rk_udphy_dt_match); + +static struct platform_driver rk_udphy_driver = { + .probe = rk_udphy_probe, + .driver = { + .name = "rockchip-usbdp-phy", + .of_match_table = rk_udphy_dt_match, + .pm = &rk_udphy_pm_ops, + }, +}; +module_platform_driver(rk_udphy_driver); + +MODULE_AUTHOR("Frank Wang <[email protected]>"); +MODULE_AUTHOR("Zhang Yubing <[email protected]>"); +MODULE_DESCRIPTION("Rockchip USBDP Combo PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index afb34a153e34..fea1f96d0e43 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-exynos-ufs.o +phy-exynos-ufs-y += phy-gs101-ufs.o phy-exynos-ufs-y += phy-samsung-ufs.o phy-exynos-ufs-y += phy-exynos7-ufs.o phy-exynos-ufs-y += phy-exynosautov9-ufs.o diff --git a/drivers/phy/samsung/phy-exynos7-ufs.c b/drivers/phy/samsung/phy-exynos7-ufs.c index a982e7c128c5..15eec1d9e0e0 100644 --- a/drivers/phy/samsung/phy-exynos7-ufs.c +++ b/drivers/phy/samsung/phy-exynos7-ufs.c @@ -82,4 +82,5 @@ const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = { .clk_list = exynos7_ufs_phy_clks, .num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks), .cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, + .wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq, }; diff --git a/drivers/phy/samsung/phy-exynosautov9-ufs.c b/drivers/phy/samsung/phy-exynosautov9-ufs.c index 49e2bcbef0b4..9c3e030f07ba 100644 --- a/drivers/phy/samsung/phy-exynosautov9-ufs.c +++ b/drivers/phy/samsung/phy-exynosautov9-ufs.c @@ -71,4 +71,5 @@ const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = { .clk_list = exynosautov9_ufs_phy_clks, .num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks), .cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, + .wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq, }; diff --git a/drivers/phy/samsung/phy-fsd-ufs.c b/drivers/phy/samsung/phy-fsd-ufs.c index d36cabd53434..f2361746db0e 100644 --- a/drivers/phy/samsung/phy-fsd-ufs.c +++ b/drivers/phy/samsung/phy-fsd-ufs.c @@ -60,4 +60,5 @@ const struct samsung_ufs_phy_drvdata fsd_ufs_phy = { .clk_list = fsd_ufs_phy_clks, .num_clks = ARRAY_SIZE(fsd_ufs_phy_clks), .cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, + .wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq, }; diff --git a/drivers/phy/samsung/phy-gs101-ufs.c b/drivers/phy/samsung/phy-gs101-ufs.c new file mode 100644 index 000000000000..17b798da5b57 --- /dev/null +++ b/drivers/phy/samsung/phy-gs101-ufs.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver data for Google Tensor gs101 SoC + * + * Copyright (C) 2024 Linaro Ltd + * Author: Peter Griffin <[email protected]> + */ + +#include "phy-samsung-ufs.h" + +#define TENSOR_GS101_PHY_CTRL 0x3ec8 +#define TENSOR_GS101_PHY_CTRL_MASK 0x1 +#define TENSOR_GS101_PHY_CTRL_EN BIT(0) +#define PHY_GS101_LANE_OFFSET 0x200 +#define TRSV_REG338 0x338 +#define LN0_MON_RX_CAL_DONE BIT(3) +#define TRSV_REG339 0x339 +#define LN0_MON_RX_CDR_FLD_CK_MODE_DONE BIT(3) +#define TRSV_REG222 0x222 +#define LN0_OVRD_RX_CDR_EN BIT(4) +#define LN0_RX_CDR_EN BIT(3) + +#define PHY_PMA_TRSV_ADDR(reg, lane) (PHY_APB_ADDR((reg) + \ + ((lane) * PHY_GS101_LANE_OFFSET))) + +#define PHY_TRSV_REG_CFG_GS101(o, v, d) \ + PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_GS101_LANE_OFFSET) + +/* Calibration for phy initialization */ +static const struct samsung_ufs_phy_cfg tensor_gs101_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x43, 0x10, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x3C, 0x14, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x46, 0x48, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x200, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x201, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x202, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x203, 0x0a, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x204, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x205, 0x11, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x207, 0x0c, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2E1, 0xc0, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x22D, 0xb8, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x234, 0x60, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x238, 0x13, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x239, 0x48, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23A, 0x01, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23B, 0x25, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23C, 0x2a, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23D, 0x01, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23E, 0x13, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x23F, 0x13, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x240, 0x4a, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x243, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x244, 0x02, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x25D, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x25E, 0x3f, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x25F, 0xff, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x273, 0x33, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x274, 0x50, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x284, 0x02, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x285, 0x02, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2A2, 0x04, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x25D, 0x01, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2FA, 0x01, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x286, 0x03, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x287, 0x03, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x288, 0x03, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x289, 0x03, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2B3, 0x04, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2B6, 0x0b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2B7, 0x0b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2B8, 0x0b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2B9, 0x0b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2BA, 0x0b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2BB, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2BC, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2BD, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x29E, 0x06, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2E4, 0x1a, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2ED, 0x25, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x269, 0x1a, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x2F4, 0x2f, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x34B, 0x01, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x34C, 0x23, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x34D, 0x23, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x34E, 0x45, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x34F, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x350, 0x31, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x351, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x352, 0x02, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x353, 0x00, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x354, 0x01, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x43, 0x18, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x43, 0x00, PWR_MODE_ANY), + END_UFS_PHY_CFG, +}; + +static const struct samsung_ufs_phy_cfg tensor_gs101_pre_pwr_hs_config[] = { + PHY_TRSV_REG_CFG_GS101(0x369, 0x11, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_GS101(0x246, 0x03, PWR_MODE_ANY), +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg tensor_gs101_post_pwr_hs_config[] = { + PHY_COMN_REG_CFG(0x8, 0x60, PWR_MODE_PWM_ANY), + PHY_TRSV_REG_CFG_GS101(0x222, 0x08, PWR_MODE_PWM_ANY), + PHY_TRSV_REG_CFG_GS101(0x246, 0x01, PWR_MODE_ANY), + END_UFS_PHY_CFG, +}; + +static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = tensor_gs101_pre_init_cfg, + [CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config, + [CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config, +}; + +static const char * const tensor_gs101_ufs_phy_clks[] = { + "ref_clk", +}; + +static int gs101_phy_wait_for_calibration(struct phy *phy, u8 lane) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + const unsigned int timeout_us = 40000; + const unsigned int sleep_us = 40; + u32 val; + u32 off; + int err; + + off = PHY_PMA_TRSV_ADDR(TRSV_REG338, lane); + + err = readl_poll_timeout(ufs_phy->reg_pma + off, + val, (val & LN0_MON_RX_CAL_DONE), + sleep_us, timeout_us); + + if (err) { + dev_err(ufs_phy->dev, + "failed to get phy cal done %d\n", err); + } + + return err; +} + +#define DELAY_IN_US 40 +#define RETRY_CNT 100 +static int gs101_phy_wait_for_cdr_lock(struct phy *phy, u8 lane) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + u32 val; + int i; + + for (i = 0; i < RETRY_CNT; i++) { + udelay(DELAY_IN_US); + val = readl(ufs_phy->reg_pma + + PHY_PMA_TRSV_ADDR(TRSV_REG339, lane)); + + if (val & LN0_MON_RX_CDR_FLD_CK_MODE_DONE) + return 0; + + udelay(DELAY_IN_US); + /* Override and enable clock data recovery */ + writel(LN0_OVRD_RX_CDR_EN, ufs_phy->reg_pma + + PHY_PMA_TRSV_ADDR(TRSV_REG222, lane)); + writel(LN0_OVRD_RX_CDR_EN | LN0_RX_CDR_EN, + ufs_phy->reg_pma + PHY_PMA_TRSV_ADDR(TRSV_REG222, lane)); + } + dev_err(ufs_phy->dev, "failed to get cdr lock\n"); + return -ETIMEDOUT; +} + +const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = { + .cfgs = tensor_gs101_ufs_phy_cfgs, + .isol = { + .offset = TENSOR_GS101_PHY_CTRL, + .mask = TENSOR_GS101_PHY_CTRL_MASK, + .en = TENSOR_GS101_PHY_CTRL_EN, + }, + .clk_list = tensor_gs101_ufs_phy_clks, + .num_clks = ARRAY_SIZE(tensor_gs101_ufs_phy_clks), + .wait_for_cal = gs101_phy_wait_for_calibration, + .wait_for_cdr = gs101_phy_wait_for_cdr_lock, +}; diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c index 183c88e3d1ec..6c5d41552649 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.c +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -13,11 +13,11 @@ #include <linux/of.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/soc/samsung/exynos-pmu.h> #include "phy-samsung-ufs.h" @@ -45,7 +45,7 @@ static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy, } } -static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy) +int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane) { struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); const unsigned int timeout_us = 100000; @@ -97,8 +97,21 @@ static int samsung_ufs_phy_calibrate(struct phy *phy) } } - if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS) - err = samsung_ufs_phy_wait_for_lock_acq(phy); + for_each_phy_lane(ufs_phy, i) { + if (ufs_phy->ufs_phy_state == CFG_PRE_INIT && + ufs_phy->drvdata->wait_for_cal) { + err = ufs_phy->drvdata->wait_for_cal(phy, i); + if (err) + goto out; + } + + if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS && + ufs_phy->drvdata->wait_for_cdr) { + err = ufs_phy->drvdata->wait_for_cdr(phy, i); + if (err) + goto out; + } + } /** * In Samsung ufshci, PHY need to be calibrated at different @@ -255,8 +268,8 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev) goto out; } - phy->reg_pmu = syscon_regmap_lookup_by_phandle( - dev->of_node, "samsung,pmu-syscon"); + phy->reg_pmu = exynos_get_pmu_regmap_by_phandle(dev->of_node, + "samsung,pmu-syscon"); if (IS_ERR(phy->reg_pmu)) { err = PTR_ERR(phy->reg_pmu); dev_err(dev, "failed syscon remap for pmu\n"); @@ -302,6 +315,9 @@ out: static const struct of_device_id samsung_ufs_phy_match[] = { { + .compatible = "google,gs101-ufs-phy", + .data = &tensor_gs101_ufs_phy, + }, { .compatible = "samsung,exynos7-ufs-phy", .data = &exynos7_ufs_phy, }, { diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h index e122960cfee8..9b7deef6e10f 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.h +++ b/drivers/phy/samsung/phy-samsung-ufs.h @@ -112,6 +112,9 @@ struct samsung_ufs_phy_drvdata { const char * const *clk_list; int num_clks; u32 cdr_lock_status_offset; + /* SoC's specific operations */ + int (*wait_for_cal)(struct phy *phy, u8 lane); + int (*wait_for_cdr)(struct phy *phy, u8 lane); }; struct samsung_ufs_phy { @@ -139,8 +142,11 @@ static inline void samsung_ufs_phy_ctrl_isol( phy->isol.mask, isol ? 0 : phy->isol.en); } +int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane); + extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy; extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy; extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy; +extern const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy; #endif /* _PHY_SAMSUNG_UFS_ */ diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index f72c5257d712..dc8319bda43d 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -995,15 +995,13 @@ static int xpsgtr_probe(struct platform_device *pdev) return 0; } -static int xpsgtr_remove(struct platform_device *pdev) +static void xpsgtr_remove(struct platform_device *pdev) { struct xpsgtr_dev *gtr_dev = platform_get_drvdata(pdev); pm_runtime_disable(gtr_dev->dev); pm_runtime_put_noidle(gtr_dev->dev); pm_runtime_set_suspended(gtr_dev->dev); - - return 0; } static const struct of_device_id xpsgtr_of_match[] = { @@ -1015,7 +1013,7 @@ MODULE_DEVICE_TABLE(of, xpsgtr_of_match); static struct platform_driver xpsgtr_driver = { .probe = xpsgtr_probe, - .remove = xpsgtr_remove, + .remove_new = xpsgtr_remove, .driver = { .name = "xilinx-psgtr", .of_match_table = xpsgtr_of_match, diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index ba38649cc142..73ec4460a151 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1505,7 +1505,7 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) * IRQ handler for ME interaction * Note: don't use MSI here as the PCH has bugs. */ - ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); + ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); if (ret < 0) return ret; diff --git a/drivers/remoteproc/mtk_common.h b/drivers/remoteproc/mtk_common.h index 6d7736a031f7..fd5c539ab2ac 100644 --- a/drivers/remoteproc/mtk_common.h +++ b/drivers/remoteproc/mtk_common.h @@ -78,7 +78,6 @@ #define MT8195_L2TCM_OFFSET 0x850d0 #define SCP_FW_VER_LEN 32 -#define SCP_SHARE_BUFFER_SIZE 288 struct scp_run { u32 signaled; @@ -97,6 +96,11 @@ struct scp_ipi_desc { struct mtk_scp; +struct mtk_scp_sizes_data { + size_t max_dram_size; + size_t ipi_share_buffer_size; +}; + struct mtk_scp_of_data { int (*scp_clk_get)(struct mtk_scp *scp); int (*scp_before_load)(struct mtk_scp *scp); @@ -110,6 +114,7 @@ struct mtk_scp_of_data { u32 host_to_scp_int_bit; size_t ipi_buf_offset; + const struct mtk_scp_sizes_data *scp_sizes; }; struct mtk_scp_of_cluster { @@ -141,10 +146,10 @@ struct mtk_scp { struct scp_ipi_desc ipi_desc[SCP_IPI_MAX]; bool ipi_id_ack[SCP_IPI_MAX]; wait_queue_head_t ack_wq; + u8 *share_buf; void *cpu_addr; dma_addr_t dma_addr; - size_t dram_size; struct rproc_subdev *rpmsg_subdev; @@ -162,7 +167,7 @@ struct mtk_scp { struct mtk_share_obj { u32 id; u32 len; - u8 share_buf[SCP_SHARE_BUFFER_SIZE]; + u8 *share_buf; }; void scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len); diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index a35409eda0cf..b8498772dba1 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -20,7 +20,6 @@ #include "mtk_common.h" #include "remoteproc_internal.h" -#define MAX_CODE_SIZE 0x500000 #define SECTION_NAME_IPI_BUFFER ".ipi_buffer" /** @@ -94,14 +93,15 @@ static void scp_ipi_handler(struct mtk_scp *scp) { struct mtk_share_obj __iomem *rcv_obj = scp->recv_buf; struct scp_ipi_desc *ipi_desc = scp->ipi_desc; - u8 tmp_data[SCP_SHARE_BUFFER_SIZE]; scp_ipi_handler_t handler; u32 id = readl(&rcv_obj->id); u32 len = readl(&rcv_obj->len); + const struct mtk_scp_sizes_data *scp_sizes; - if (len > SCP_SHARE_BUFFER_SIZE) { - dev_err(scp->dev, "ipi message too long (len %d, max %d)", len, - SCP_SHARE_BUFFER_SIZE); + scp_sizes = scp->data->scp_sizes; + if (len > scp_sizes->ipi_share_buffer_size) { + dev_err(scp->dev, "ipi message too long (len %d, max %zd)", len, + scp_sizes->ipi_share_buffer_size); return; } if (id >= SCP_IPI_MAX) { @@ -117,8 +117,9 @@ static void scp_ipi_handler(struct mtk_scp *scp) return; } - memcpy_fromio(tmp_data, &rcv_obj->share_buf, len); - handler(tmp_data, len, ipi_desc[id].priv); + memset(scp->share_buf, 0, scp_sizes->ipi_share_buffer_size); + memcpy_fromio(scp->share_buf, &rcv_obj->share_buf, len); + handler(scp->share_buf, len, ipi_desc[id].priv); scp_ipi_unlock(scp, id); scp->ipi_id_ack[id] = true; @@ -132,7 +133,9 @@ static int scp_elf_read_ipi_buf_addr(struct mtk_scp *scp, static int scp_ipi_init(struct mtk_scp *scp, const struct firmware *fw) { int ret; - size_t offset; + size_t buf_sz, offset; + size_t share_buf_offset; + const struct mtk_scp_sizes_data *scp_sizes; /* read the ipi buf addr from FW itself first */ ret = scp_elf_read_ipi_buf_addr(scp, fw, &offset); @@ -144,12 +147,23 @@ static int scp_ipi_init(struct mtk_scp *scp, const struct firmware *fw) } dev_info(scp->dev, "IPI buf addr %#010zx\n", offset); + /* Make sure IPI buffer fits in the L2TCM range assigned to this core */ + buf_sz = sizeof(*scp->recv_buf) + sizeof(*scp->send_buf); + + if (scp->sram_size < buf_sz + offset) { + dev_err(scp->dev, "IPI buffer does not fit in SRAM.\n"); + return -EOVERFLOW; + } + + scp_sizes = scp->data->scp_sizes; scp->recv_buf = (struct mtk_share_obj __iomem *) (scp->sram_base + offset); + share_buf_offset = sizeof(scp->recv_buf->id) + + sizeof(scp->recv_buf->len) + scp_sizes->ipi_share_buffer_size; scp->send_buf = (struct mtk_share_obj __iomem *) - (scp->sram_base + offset + sizeof(*scp->recv_buf)); - memset_io(scp->recv_buf, 0, sizeof(*scp->recv_buf)); - memset_io(scp->send_buf, 0, sizeof(*scp->send_buf)); + (scp->sram_base + offset + share_buf_offset); + memset_io(scp->recv_buf, 0, share_buf_offset); + memset_io(scp->send_buf, 0, share_buf_offset); return 0; } @@ -463,6 +477,86 @@ static int mt8186_scp_before_load(struct mtk_scp *scp) return 0; } +static int mt8188_scp_l2tcm_on(struct mtk_scp *scp) +{ + struct mtk_scp_of_cluster *scp_cluster = scp->cluster; + + mutex_lock(&scp_cluster->cluster_lock); + + if (scp_cluster->l2tcm_refcnt == 0) { + /* clear SPM interrupt, SCP2SPM_IPC_CLR */ + writel(0xff, scp->cluster->reg_base + MT8192_SCP2SPM_IPC_CLR); + + /* Power on L2TCM */ + scp_sram_power_on(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); + scp_sram_power_on(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); + scp_sram_power_on(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); + scp_sram_power_on(scp->cluster->reg_base + MT8192_L1TCM_SRAM_PDN, 0); + } + + scp_cluster->l2tcm_refcnt += 1; + + mutex_unlock(&scp_cluster->cluster_lock); + + return 0; +} + +static int mt8188_scp_before_load(struct mtk_scp *scp) +{ + writel(1, scp->cluster->reg_base + MT8192_CORE0_SW_RSTN_SET); + + mt8188_scp_l2tcm_on(scp); + + scp_sram_power_on(scp->cluster->reg_base + MT8192_CPU0_SRAM_PD, 0); + + /* enable MPU for all memory regions */ + writel(0xff, scp->cluster->reg_base + MT8192_CORE0_MEM_ATT_PREDEF); + + return 0; +} + +static int mt8188_scp_c1_before_load(struct mtk_scp *scp) +{ + u32 sec_ctrl; + struct mtk_scp *scp_c0; + struct mtk_scp_of_cluster *scp_cluster = scp->cluster; + + scp->data->scp_reset_assert(scp); + + mt8188_scp_l2tcm_on(scp); + + scp_sram_power_on(scp->cluster->reg_base + MT8195_CPU1_SRAM_PD, 0); + + /* enable MPU for all memory regions */ + writel(0xff, scp->cluster->reg_base + MT8195_CORE1_MEM_ATT_PREDEF); + + /* + * The L2TCM_OFFSET_RANGE and L2TCM_OFFSET shift the destination address + * on SRAM when SCP core 1 accesses SRAM. + * + * This configuration solves booting the SCP core 0 and core 1 from + * different SRAM address because core 0 and core 1 both boot from + * the head of SRAM by default. this must be configured before boot SCP core 1. + * + * The value of L2TCM_OFFSET_RANGE is from the viewpoint of SCP core 1. + * When SCP core 1 issues address within the range (L2TCM_OFFSET_RANGE), + * the address will be added with a fixed offset (L2TCM_OFFSET) on the bus. + * The shift action is tranparent to software. + */ + writel(0, scp->cluster->reg_base + MT8195_L2TCM_OFFSET_RANGE_0_LOW); + writel(scp->sram_size, scp->cluster->reg_base + MT8195_L2TCM_OFFSET_RANGE_0_HIGH); + + scp_c0 = list_first_entry(&scp_cluster->mtk_scp_list, struct mtk_scp, elem); + writel(scp->sram_phys - scp_c0->sram_phys, scp->cluster->reg_base + MT8195_L2TCM_OFFSET); + + /* enable SRAM offset when fetching instruction and data */ + sec_ctrl = readl(scp->cluster->reg_base + MT8195_SEC_CTRL); + sec_ctrl |= MT8195_CORE_OFFSET_ENABLE_I | MT8195_CORE_OFFSET_ENABLE_D; + writel(sec_ctrl, scp->cluster->reg_base + MT8195_SEC_CTRL); + + return 0; +} + static int mt8192_scp_before_load(struct mtk_scp *scp) { /* clear SPM interrupt, SCP2SPM_IPC_CLR */ @@ -653,14 +747,16 @@ stop: static void *mt8183_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) { int offset; + const struct mtk_scp_sizes_data *scp_sizes; + scp_sizes = scp->data->scp_sizes; if (da < scp->sram_size) { offset = da; if (offset >= 0 && (offset + len) <= scp->sram_size) return (void __force *)scp->sram_base + offset; - } else if (scp->dram_size) { + } else if (scp_sizes->max_dram_size) { offset = da - scp->dma_addr; - if (offset >= 0 && (offset + len) <= scp->dram_size) + if (offset >= 0 && (offset + len) <= scp_sizes->max_dram_size) return scp->cpu_addr + offset; } @@ -670,7 +766,9 @@ static void *mt8183_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) static void *mt8192_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) { int offset; + const struct mtk_scp_sizes_data *scp_sizes; + scp_sizes = scp->data->scp_sizes; if (da >= scp->sram_phys && (da + len) <= scp->sram_phys + scp->sram_size) { offset = da - scp->sram_phys; @@ -686,9 +784,9 @@ static void *mt8192_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) } /* optional memory region */ - if (scp->dram_size && + if (scp_sizes->max_dram_size && da >= scp->dma_addr && - (da + len) <= scp->dma_addr + scp->dram_size) { + (da + len) <= scp->dma_addr + scp_sizes->max_dram_size) { offset = da - scp->dma_addr; return scp->cpu_addr + offset; } @@ -709,6 +807,47 @@ static void mt8183_scp_stop(struct mtk_scp *scp) writel(0, scp->cluster->reg_base + MT8183_WDT_CFG); } +static void mt8188_scp_l2tcm_off(struct mtk_scp *scp) +{ + struct mtk_scp_of_cluster *scp_cluster = scp->cluster; + + mutex_lock(&scp_cluster->cluster_lock); + + if (scp_cluster->l2tcm_refcnt > 0) + scp_cluster->l2tcm_refcnt -= 1; + + if (scp_cluster->l2tcm_refcnt == 0) { + /* Power off L2TCM */ + scp_sram_power_off(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); + scp_sram_power_off(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); + scp_sram_power_off(scp->cluster->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); + scp_sram_power_off(scp->cluster->reg_base + MT8192_L1TCM_SRAM_PDN, 0); + } + + mutex_unlock(&scp_cluster->cluster_lock); +} + +static void mt8188_scp_stop(struct mtk_scp *scp) +{ + mt8188_scp_l2tcm_off(scp); + + scp_sram_power_off(scp->cluster->reg_base + MT8192_CPU0_SRAM_PD, 0); + + /* Disable SCP watchdog */ + writel(0, scp->cluster->reg_base + MT8192_CORE0_WDT_CFG); +} + +static void mt8188_scp_c1_stop(struct mtk_scp *scp) +{ + mt8188_scp_l2tcm_off(scp); + + /* Power off CPU SRAM */ + scp_sram_power_off(scp->cluster->reg_base + MT8195_CPU1_SRAM_PD, 0); + + /* Disable SCP watchdog */ + writel(0, scp->cluster->reg_base + MT8195_CORE1_WDT_CFG); +} + static void mt8192_scp_stop(struct mtk_scp *scp) { /* Disable SRAM clock */ @@ -868,6 +1007,7 @@ EXPORT_SYMBOL_GPL(scp_mapping_dm_addr); static int scp_map_memory_region(struct mtk_scp *scp) { int ret; + const struct mtk_scp_sizes_data *scp_sizes; ret = of_reserved_mem_device_init(scp->dev); @@ -883,8 +1023,8 @@ static int scp_map_memory_region(struct mtk_scp *scp) } /* Reserved SCP code size */ - scp->dram_size = MAX_CODE_SIZE; - scp->cpu_addr = dma_alloc_coherent(scp->dev, scp->dram_size, + scp_sizes = scp->data->scp_sizes; + scp->cpu_addr = dma_alloc_coherent(scp->dev, scp_sizes->max_dram_size, &scp->dma_addr, GFP_KERNEL); if (!scp->cpu_addr) return -ENOMEM; @@ -894,10 +1034,13 @@ static int scp_map_memory_region(struct mtk_scp *scp) static void scp_unmap_memory_region(struct mtk_scp *scp) { - if (scp->dram_size == 0) + const struct mtk_scp_sizes_data *scp_sizes; + + scp_sizes = scp->data->scp_sizes; + if (scp_sizes->max_dram_size == 0) return; - dma_free_coherent(scp->dev, scp->dram_size, scp->cpu_addr, + dma_free_coherent(scp->dev, scp_sizes->max_dram_size, scp->cpu_addr, scp->dma_addr); of_reserved_mem_device_release(scp->dev); } @@ -961,6 +1104,7 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev, struct resource *res; const char *fw_name = "scp.img"; int ret, i; + const struct mtk_scp_sizes_data *scp_sizes; ret = rproc_of_parse_firmware(dev, 0, &fw_name); if (ret < 0 && ret != -EINVAL) @@ -1008,6 +1152,14 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev, goto release_dev_mem; } + scp_sizes = scp->data->scp_sizes; + scp->share_buf = kzalloc(scp_sizes->ipi_share_buffer_size, GFP_KERNEL); + if (!scp->share_buf) { + dev_err(dev, "Failed to allocate IPI share buffer\n"); + ret = -ENOMEM; + goto release_dev_mem; + } + init_waitqueue_head(&scp->run.wq); init_waitqueue_head(&scp->ack_wq); @@ -1027,6 +1179,8 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev, remove_subdev: scp_remove_rpmsg_subdev(scp); scp_ipi_unregister(scp, SCP_IPI_INIT); + kfree(scp->share_buf); + scp->share_buf = NULL; release_dev_mem: scp_unmap_memory_region(scp); for (i = 0; i < SCP_IPI_MAX; i++) @@ -1042,6 +1196,8 @@ static void scp_free(struct mtk_scp *scp) scp_remove_rpmsg_subdev(scp); scp_ipi_unregister(scp, SCP_IPI_INIT); + kfree(scp->share_buf); + scp->share_buf = NULL; scp_unmap_memory_region(scp); for (i = 0; i < SCP_IPI_MAX; i++) mutex_destroy(&scp->ipi_desc[i].lock); @@ -1228,6 +1384,21 @@ static void scp_remove(struct platform_device *pdev) mutex_destroy(&scp_cluster->cluster_lock); } +static const struct mtk_scp_sizes_data default_scp_sizes = { + .max_dram_size = 0x500000, + .ipi_share_buffer_size = 288, +}; + +static const struct mtk_scp_sizes_data mt8188_scp_sizes = { + .max_dram_size = 0x500000, + .ipi_share_buffer_size = 600, +}; + +static const struct mtk_scp_sizes_data mt8188_scp_c1_sizes = { + .max_dram_size = 0xA00000, + .ipi_share_buffer_size = 600, +}; + static const struct mtk_scp_of_data mt8183_of_data = { .scp_clk_get = mt8183_scp_clk_get, .scp_before_load = mt8183_scp_before_load, @@ -1239,6 +1410,7 @@ static const struct mtk_scp_of_data mt8183_of_data = { .host_to_scp_reg = MT8183_HOST_TO_SCP, .host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT, .ipi_buf_offset = 0x7bdb0, + .scp_sizes = &default_scp_sizes, }; static const struct mtk_scp_of_data mt8186_of_data = { @@ -1252,18 +1424,33 @@ static const struct mtk_scp_of_data mt8186_of_data = { .host_to_scp_reg = MT8183_HOST_TO_SCP, .host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT, .ipi_buf_offset = 0x3bdb0, + .scp_sizes = &default_scp_sizes, }; static const struct mtk_scp_of_data mt8188_of_data = { .scp_clk_get = mt8195_scp_clk_get, - .scp_before_load = mt8192_scp_before_load, - .scp_irq_handler = mt8192_scp_irq_handler, + .scp_before_load = mt8188_scp_before_load, + .scp_irq_handler = mt8195_scp_irq_handler, .scp_reset_assert = mt8192_scp_reset_assert, .scp_reset_deassert = mt8192_scp_reset_deassert, - .scp_stop = mt8192_scp_stop, + .scp_stop = mt8188_scp_stop, .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, + .scp_sizes = &mt8188_scp_sizes, +}; + +static const struct mtk_scp_of_data mt8188_of_data_c1 = { + .scp_clk_get = mt8195_scp_clk_get, + .scp_before_load = mt8188_scp_c1_before_load, + .scp_irq_handler = mt8195_scp_c1_irq_handler, + .scp_reset_assert = mt8195_scp_c1_reset_assert, + .scp_reset_deassert = mt8195_scp_c1_reset_deassert, + .scp_stop = mt8188_scp_c1_stop, + .scp_da_to_va = mt8192_scp_da_to_va, + .host_to_scp_reg = MT8192_GIPC_IN_SET, + .host_to_scp_int_bit = MT8195_CORE1_HOST_IPC_INT_BIT, + .scp_sizes = &mt8188_scp_c1_sizes, }; static const struct mtk_scp_of_data mt8192_of_data = { @@ -1276,6 +1463,7 @@ static const struct mtk_scp_of_data mt8192_of_data = { .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, + .scp_sizes = &default_scp_sizes, }; static const struct mtk_scp_of_data mt8195_of_data = { @@ -1288,6 +1476,7 @@ static const struct mtk_scp_of_data mt8195_of_data = { .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, + .scp_sizes = &default_scp_sizes, }; static const struct mtk_scp_of_data mt8195_of_data_c1 = { @@ -1300,6 +1489,13 @@ static const struct mtk_scp_of_data mt8195_of_data_c1 = { .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8195_CORE1_HOST_IPC_INT_BIT, + .scp_sizes = &default_scp_sizes, +}; + +static const struct mtk_scp_of_data *mt8188_of_data_cores[] = { + &mt8188_of_data, + &mt8188_of_data_c1, + NULL }; static const struct mtk_scp_of_data *mt8195_of_data_cores[] = { @@ -1312,6 +1508,7 @@ static const struct of_device_id mtk_scp_of_match[] = { { .compatible = "mediatek,mt8183-scp", .data = &mt8183_of_data }, { .compatible = "mediatek,mt8186-scp", .data = &mt8186_of_data }, { .compatible = "mediatek,mt8188-scp", .data = &mt8188_of_data }, + { .compatible = "mediatek,mt8188-scp-dual", .data = &mt8188_of_data_cores }, { .compatible = "mediatek,mt8192-scp", .data = &mt8192_of_data }, { .compatible = "mediatek,mt8195-scp", .data = &mt8195_of_data }, { .compatible = "mediatek,mt8195-scp-dual", .data = &mt8195_of_data_cores }, diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c index cd0b60106ec2..c068227e251e 100644 --- a/drivers/remoteproc/mtk_scp_ipi.c +++ b/drivers/remoteproc/mtk_scp_ipi.c @@ -162,10 +162,13 @@ int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len, struct mtk_share_obj __iomem *send_obj = scp->send_buf; u32 val; int ret; + const struct mtk_scp_sizes_data *scp_sizes; + + scp_sizes = scp->data->scp_sizes; if (WARN_ON(id <= SCP_IPI_INIT) || WARN_ON(id >= SCP_IPI_MAX) || WARN_ON(id == SCP_IPI_NS_SERVICE) || - WARN_ON(len > sizeof(send_obj->share_buf)) || WARN_ON(!buf)) + WARN_ON(len > scp_sizes->ipi_share_buffer_size) || WARN_ON(!buf)) return -EINVAL; ret = clk_prepare_enable(scp->clk); @@ -184,7 +187,7 @@ int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len, goto unlock_mutex; } - scp_memcpy_aligned(send_obj->share_buf, buf, len); + scp_memcpy_aligned(&send_obj->share_buf, buf, len); writel(len, &send_obj->len); writel(id, &send_obj->id); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index f62a82d71dfa..0cd09e67ac14 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -72,7 +72,7 @@ void rproc_init_debugfs(void); void rproc_exit_debugfs(void); /* from remoteproc_sysfs.c */ -extern struct class rproc_class; +extern const struct class rproc_class; int rproc_init_sysfs(void); void rproc_exit_sysfs(void); diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c index 8c7ea8922638..138e752c5e4e 100644 --- a/drivers/remoteproc/remoteproc_sysfs.c +++ b/drivers/remoteproc/remoteproc_sysfs.c @@ -254,7 +254,7 @@ static const struct attribute_group *rproc_devgroups[] = { NULL }; -struct class rproc_class = { +const struct class rproc_class = { .name = "remoteproc", .dev_groups = rproc_devgroups, }; diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index ad3415a3851b..50e486bcfa10 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -103,12 +103,14 @@ struct k3_r5_soc_data { * @dev: cached device pointer * @mode: Mode to configure the Cluster - Split or LockStep * @cores: list of R5 cores within the cluster + * @core_transition: wait queue to sync core state changes * @soc_data: SoC-specific feature data for a R5FSS */ struct k3_r5_cluster { struct device *dev; enum cluster_mode mode; struct list_head cores; + wait_queue_head_t core_transition; const struct k3_r5_soc_data *soc_data; }; @@ -128,6 +130,7 @@ struct k3_r5_cluster { * @atcm_enable: flag to control ATCM enablement * @btcm_enable: flag to control BTCM enablement * @loczrama: flag to dictate which TCM is at device address 0x0 + * @released_from_reset: flag to signal when core is out of reset */ struct k3_r5_core { struct list_head elem; @@ -144,6 +147,7 @@ struct k3_r5_core { u32 atcm_enable; u32 btcm_enable; u32 loczrama; + bool released_from_reset; }; /** @@ -460,6 +464,8 @@ static int k3_r5_rproc_prepare(struct rproc *rproc) ret); return ret; } + core->released_from_reset = true; + wake_up_interruptible(&cluster->core_transition); /* * Newer IP revisions like on J7200 SoCs support h/w auto-initialization @@ -542,7 +548,7 @@ static int k3_r5_rproc_start(struct rproc *rproc) struct k3_r5_rproc *kproc = rproc->priv; struct k3_r5_cluster *cluster = kproc->cluster; struct device *dev = kproc->dev; - struct k3_r5_core *core; + struct k3_r5_core *core0, *core; u32 boot_addr; int ret; @@ -568,6 +574,16 @@ static int k3_r5_rproc_start(struct rproc *rproc) goto unroll_core_run; } } else { + /* do not allow core 1 to start before core 0 */ + core0 = list_first_entry(&cluster->cores, struct k3_r5_core, + elem); + if (core != core0 && core0->rproc->state == RPROC_OFFLINE) { + dev_err(dev, "%s: can not start core 1 before core 0\n", + __func__); + ret = -EPERM; + goto put_mbox; + } + ret = k3_r5_core_run(core); if (ret) goto put_mbox; @@ -613,7 +629,8 @@ static int k3_r5_rproc_stop(struct rproc *rproc) { struct k3_r5_rproc *kproc = rproc->priv; struct k3_r5_cluster *cluster = kproc->cluster; - struct k3_r5_core *core = kproc->core; + struct device *dev = kproc->dev; + struct k3_r5_core *core1, *core = kproc->core; int ret; /* halt all applicable cores */ @@ -626,6 +643,16 @@ static int k3_r5_rproc_stop(struct rproc *rproc) } } } else { + /* do not allow core 0 to stop before core 1 */ + core1 = list_last_entry(&cluster->cores, struct k3_r5_core, + elem); + if (core != core1 && core1->rproc->state != RPROC_OFFLINE) { + dev_err(dev, "%s: can not stop core 0 before core 1\n", + __func__); + ret = -EPERM; + goto out; + } + ret = k3_r5_core_halt(core); if (ret) goto out; @@ -1140,6 +1167,12 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) return ret; } + /* + * Skip the waiting mechanism for sequential power-on of cores if the + * core has already been booted by another entity. + */ + core->released_from_reset = c_state; + ret = ti_sci_proc_get_status(core->tsp, &boot_vec, &cfg, &ctrl, &stat); if (ret < 0) { @@ -1280,6 +1313,26 @@ init_rmem: cluster->mode == CLUSTER_MODE_SINGLECPU || cluster->mode == CLUSTER_MODE_SINGLECORE) break; + + /* + * R5 cores require to be powered on sequentially, core0 + * should be in higher power state than core1 in a cluster + * So, wait for current core to power up before proceeding + * to next core and put timeout of 2sec for each core. + * + * This waiting mechanism is necessary because + * rproc_auto_boot_callback() for core1 can be called before + * core0 due to thread execution order. + */ + ret = wait_event_interruptible_timeout(cluster->core_transition, + core->released_from_reset, + msecs_to_jiffies(2000)); + if (ret <= 0) { + dev_err(dev, + "Timed out waiting for %s core to power up!\n", + rproc->name); + return ret; + } } return 0; @@ -1709,6 +1762,7 @@ static int k3_r5_probe(struct platform_device *pdev) cluster->dev = dev; cluster->soc_data = data; INIT_LIST_HEAD(&cluster->cores); + init_waitqueue_head(&cluster->core_transition); ret = of_property_read_u32(np, "ti,cluster-mode", &cluster->mode); if (ret < 0 && ret != -EINVAL) { diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 4395edea9a64..84243d1dff9f 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -74,8 +74,8 @@ struct mbox_info { }; /* - * Hardcoded TCM bank values. This will be removed once TCM bindings are - * accepted for system-dt specifications and upstreamed in linux kernel + * Hardcoded TCM bank values. This will stay in driver to maintain backward + * compatibility with device-tree that does not have TCM information. */ static const struct mem_bank_data zynqmp_tcm_banks_split[] = { {0xffe00000UL, 0x0, 0x10000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 64KB each */ @@ -84,12 +84,12 @@ static const struct mem_bank_data zynqmp_tcm_banks_split[] = { {0xffeb0000UL, 0x20000, 0x10000UL, PD_R5_1_BTCM, "btcm1"}, }; -/* In lockstep mode cluster combines each 64KB TCM and makes 128KB TCM */ +/* In lockstep mode cluster uses each 64KB TCM from second core as well */ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { - {0xffe00000UL, 0x0, 0x20000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 128KB each */ - {0xffe20000UL, 0x20000, 0x20000UL, PD_R5_0_BTCM, "btcm0"}, - {0, 0, 0, PD_R5_1_ATCM, ""}, - {0, 0, 0, PD_R5_1_BTCM, ""}, + {0xffe00000UL, 0x0, 0x10000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 64KB each */ + {0xffe20000UL, 0x20000, 0x10000UL, PD_R5_0_BTCM, "btcm0"}, + {0xffe10000UL, 0x10000, 0x10000UL, PD_R5_1_ATCM, "atcm1"}, + {0xffe30000UL, 0x30000, 0x10000UL, PD_R5_1_BTCM, "btcm1"}, }; /** @@ -301,36 +301,6 @@ static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) } /* - * zynqmp_r5_set_mode() - * - * set RPU cluster and TCM operation mode - * - * @r5_core: pointer to zynqmp_r5_core type object - * @fw_reg_val: value expected by firmware to configure RPU cluster mode - * @tcm_mode: value expected by fw to configure TCM mode (lockstep or split) - * - * Return: 0 for success and < 0 for failure - */ -static int zynqmp_r5_set_mode(struct zynqmp_r5_core *r5_core, - enum rpu_oper_mode fw_reg_val, - enum rpu_tcm_comb tcm_mode) -{ - int ret; - - ret = zynqmp_pm_set_rpu_mode(r5_core->pm_domain_id, fw_reg_val); - if (ret < 0) { - dev_err(r5_core->dev, "failed to set RPU mode\n"); - return ret; - } - - ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, tcm_mode); - if (ret < 0) - dev_err(r5_core->dev, "failed to configure TCM\n"); - - return ret; -} - -/* * zynqmp_r5_rproc_start() * @rproc: single R5 core's corresponding rproc instance * @@ -486,6 +456,7 @@ static int add_mem_regions_carveout(struct rproc *rproc) } rproc_add_carveout(rproc, rproc_mem); + rproc_coredump_add_segment(rproc, rmem->base, rmem->size); dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx", it.node->name, rmem->base, rmem->size); @@ -540,14 +511,14 @@ static int tcm_mem_map(struct rproc *rproc, } /* - * add_tcm_carveout_split_mode() + * add_tcm_banks() * @rproc: single R5 core's corresponding rproc instance * - * allocate and add remoteproc carveout for TCM memory in split mode + * allocate and add remoteproc carveout for TCM memory * * return 0 on success, otherwise non-zero value on failure */ -static int add_tcm_carveout_split_mode(struct rproc *rproc) +static int add_tcm_banks(struct rproc *rproc) { struct rproc_mem_entry *rproc_mem; struct zynqmp_r5_core *r5_core; @@ -580,10 +551,10 @@ static int add_tcm_carveout_split_mode(struct rproc *rproc) ZYNQMP_PM_REQUEST_ACK_BLOCKING); if (ret < 0) { dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id); - goto release_tcm_split; + goto release_tcm; } - dev_dbg(dev, "TCM carveout split mode %s addr=%llx, da=0x%x, size=0x%lx", + dev_dbg(dev, "TCM carveout %s addr=%llx, da=0x%x, size=0x%lx", bank_name, bank_addr, da, bank_size); rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, @@ -593,15 +564,16 @@ static int add_tcm_carveout_split_mode(struct rproc *rproc) if (!rproc_mem) { ret = -ENOMEM; zynqmp_pm_release_node(pm_domain_id); - goto release_tcm_split; + goto release_tcm; } rproc_add_carveout(rproc, rproc_mem); + rproc_coredump_add_segment(rproc, da, bank_size); } return 0; -release_tcm_split: +release_tcm: /* If failed, Turn off all TCM banks turned on before */ for (i--; i >= 0; i--) { pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; @@ -611,127 +583,6 @@ release_tcm_split: } /* - * add_tcm_carveout_lockstep_mode() - * @rproc: single R5 core's corresponding rproc instance - * - * allocate and add remoteproc carveout for TCM memory in lockstep mode - * - * return 0 on success, otherwise non-zero value on failure - */ -static int add_tcm_carveout_lockstep_mode(struct rproc *rproc) -{ - struct rproc_mem_entry *rproc_mem; - struct zynqmp_r5_core *r5_core; - int i, num_banks, ret; - phys_addr_t bank_addr; - size_t bank_size = 0; - struct device *dev; - u32 pm_domain_id; - char *bank_name; - u32 da; - - r5_core = rproc->priv; - dev = r5_core->dev; - - /* Go through zynqmp banks for r5 node */ - num_banks = r5_core->tcm_bank_count; - - /* - * In lockstep mode, TCM is contiguous memory block - * However, each TCM block still needs to be enabled individually. - * So, Enable each TCM block individually. - * Although ATCM and BTCM is contiguous memory block, add two separate - * carveouts for both. - */ - for (i = 0; i < num_banks; i++) { - pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; - - /* Turn on each TCM bank individually */ - ret = zynqmp_pm_request_node(pm_domain_id, - ZYNQMP_PM_CAPABILITY_ACCESS, 0, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret < 0) { - dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id); - goto release_tcm_lockstep; - } - - bank_size = r5_core->tcm_banks[i]->size; - if (bank_size == 0) - continue; - - bank_addr = r5_core->tcm_banks[i]->addr; - da = r5_core->tcm_banks[i]->da; - bank_name = r5_core->tcm_banks[i]->bank_name; - - /* Register TCM address range, TCM map and unmap functions */ - rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, - bank_size, da, - tcm_mem_map, tcm_mem_unmap, - bank_name); - if (!rproc_mem) { - ret = -ENOMEM; - zynqmp_pm_release_node(pm_domain_id); - goto release_tcm_lockstep; - } - - /* If registration is success, add carveouts */ - rproc_add_carveout(rproc, rproc_mem); - - dev_dbg(dev, "TCM carveout lockstep mode %s addr=0x%llx, da=0x%x, size=0x%lx", - bank_name, bank_addr, da, bank_size); - } - - return 0; - -release_tcm_lockstep: - /* If failed, Turn off all TCM banks turned on before */ - for (i--; i >= 0; i--) { - pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; - zynqmp_pm_release_node(pm_domain_id); - } - return ret; -} - -/* - * add_tcm_banks() - * @rproc: single R5 core's corresponding rproc instance - * - * allocate and add remoteproc carveouts for TCM memory based on cluster mode - * - * return 0 on success, otherwise non-zero value on failure - */ -static int add_tcm_banks(struct rproc *rproc) -{ - struct zynqmp_r5_cluster *cluster; - struct zynqmp_r5_core *r5_core; - struct device *dev; - - r5_core = rproc->priv; - if (!r5_core) - return -EINVAL; - - dev = r5_core->dev; - - cluster = dev_get_drvdata(dev->parent); - if (!cluster) { - dev_err(dev->parent, "Invalid driver data\n"); - return -EINVAL; - } - - /* - * In lockstep mode TCM banks are one contiguous memory region of 256Kb - * In split mode, each TCM bank is 64Kb and not contiguous. - * We add memory carveouts accordingly. - */ - if (cluster->mode == SPLIT_MODE) - return add_tcm_carveout_split_mode(rproc); - else if (cluster->mode == LOCKSTEP_MODE) - return add_tcm_carveout_lockstep_mode(rproc); - - return -EINVAL; -} - -/* * zynqmp_r5_parse_fw() * @rproc: single R5 core's corresponding rproc instance * @fw: ptr to firmware to be loaded onto r5 core @@ -853,6 +704,8 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) return ERR_PTR(-ENOMEM); } + rproc_coredump_set_elf_info(r5_rproc, ELFCLASS32, EM_ARM); + r5_rproc->auto_boot = false; r5_core = r5_rproc->priv; r5_core->dev = cdev; @@ -878,6 +731,103 @@ free_rproc: return ERR_PTR(ret); } +static int zynqmp_r5_get_tcm_node_from_dt(struct zynqmp_r5_cluster *cluster) +{ + int i, j, tcm_bank_count, ret, tcm_pd_idx, pd_count; + struct of_phandle_args out_args; + struct zynqmp_r5_core *r5_core; + struct platform_device *cpdev; + struct mem_bank_data *tcm; + struct device_node *np; + struct resource *res; + u64 abs_addr, size; + struct device *dev; + + for (i = 0; i < cluster->core_count; i++) { + r5_core = cluster->r5_cores[i]; + dev = r5_core->dev; + np = r5_core->np; + + pd_count = of_count_phandle_with_args(np, "power-domains", + "#power-domain-cells"); + + if (pd_count <= 0) { + dev_err(dev, "invalid power-domains property, %d\n", pd_count); + return -EINVAL; + } + + /* First entry in power-domains list is for r5 core, rest for TCM. */ + tcm_bank_count = pd_count - 1; + + if (tcm_bank_count <= 0) { + dev_err(dev, "invalid TCM count %d\n", tcm_bank_count); + return -EINVAL; + } + + r5_core->tcm_banks = devm_kcalloc(dev, tcm_bank_count, + sizeof(struct mem_bank_data *), + GFP_KERNEL); + if (!r5_core->tcm_banks) + return -ENOMEM; + + r5_core->tcm_bank_count = tcm_bank_count; + for (j = 0, tcm_pd_idx = 1; j < tcm_bank_count; j++, tcm_pd_idx++) { + tcm = devm_kzalloc(dev, sizeof(struct mem_bank_data), + GFP_KERNEL); + if (!tcm) + return -ENOMEM; + + r5_core->tcm_banks[j] = tcm; + + /* Get power-domains id of TCM. */ + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", + tcm_pd_idx, &out_args); + if (ret) { + dev_err(r5_core->dev, + "failed to get tcm %d pm domain, ret %d\n", + tcm_pd_idx, ret); + return ret; + } + tcm->pm_domain_id = out_args.args[0]; + of_node_put(out_args.np); + + /* Get TCM address without translation. */ + ret = of_property_read_reg(np, j, &abs_addr, &size); + if (ret) { + dev_err(dev, "failed to get reg property\n"); + return ret; + } + + /* + * Remote processor can address only 32 bits + * so convert 64-bits into 32-bits. This will discard + * any unwanted upper 32-bits. + */ + tcm->da = (u32)abs_addr; + tcm->size = (u32)size; + + cpdev = to_platform_device(dev); + res = platform_get_resource(cpdev, IORESOURCE_MEM, j); + if (!res) { + dev_err(dev, "failed to get tcm resource\n"); + return -EINVAL; + } + + tcm->addr = (u32)res->start; + tcm->bank_name = (char *)res->name; + res = devm_request_mem_region(dev, tcm->addr, tcm->size, + tcm->bank_name); + if (!res) { + dev_err(dev, "failed to request tcm resource\n"); + return -EINVAL; + } + } + } + + return 0; +} + /** * zynqmp_r5_get_tcm_node() * Ideally this function should parse tcm node and store information @@ -954,11 +904,18 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, { struct device *dev = cluster->dev; struct zynqmp_r5_core *r5_core; - int ret, i; + int ret = -EINVAL, i; - ret = zynqmp_r5_get_tcm_node(cluster); - if (ret < 0) { - dev_err(dev, "can't get tcm node, err %d\n", ret); + r5_core = cluster->r5_cores[0]; + + /* Maintain backward compatibility for zynqmp by using hardcode TCM address. */ + if (of_find_property(r5_core->np, "reg", NULL)) + ret = zynqmp_r5_get_tcm_node_from_dt(cluster); + else if (device_is_compatible(dev, "xlnx,zynqmp-r5fss")) + ret = zynqmp_r5_get_tcm_node(cluster); + + if (ret) { + dev_err(dev, "can't get tcm, err %d\n", ret); return ret; } @@ -973,12 +930,21 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, return ret; } - ret = zynqmp_r5_set_mode(r5_core, fw_reg_val, tcm_mode); - if (ret) { - dev_err(dev, "failed to set r5 cluster mode %d, err %d\n", - cluster->mode, ret); + ret = zynqmp_pm_set_rpu_mode(r5_core->pm_domain_id, fw_reg_val); + if (ret < 0) { + dev_err(r5_core->dev, "failed to set RPU mode\n"); return ret; } + + if (of_find_property(dev_of_node(dev), "xlnx,tcm-mode", NULL) || + device_is_compatible(dev, "xlnx,zynqmp-r5fss")) { + ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, + tcm_mode); + if (ret < 0) { + dev_err(r5_core->dev, "failed to configure TCM\n"); + return ret; + } + } } return 0; @@ -1023,16 +989,27 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) * fail driver probe if either of that is not set in dts. */ if (cluster_mode == LOCKSTEP_MODE) { - tcm_mode = PM_RPU_TCM_COMB; fw_reg_val = PM_RPU_MODE_LOCKSTEP; } else if (cluster_mode == SPLIT_MODE) { - tcm_mode = PM_RPU_TCM_SPLIT; fw_reg_val = PM_RPU_MODE_SPLIT; } else { dev_err(dev, "driver does not support cluster mode %d\n", cluster_mode); return -EINVAL; } + if (of_find_property(dev_node, "xlnx,tcm-mode", NULL)) { + ret = of_property_read_u32(dev_node, "xlnx,tcm-mode", (u32 *)&tcm_mode); + if (ret) + return ret; + } else if (device_is_compatible(dev, "xlnx,zynqmp-r5fss")) { + if (cluster_mode == LOCKSTEP_MODE) + tcm_mode = PM_RPU_TCM_COMB; + else + tcm_mode = PM_RPU_TCM_SPLIT; + } else { + tcm_mode = PM_RPU_TCM_COMB; + } + /* * Number of cores is decided by number of child nodes of * r5f subsystem node in dts. If Split mode is used in dts @@ -1216,6 +1193,8 @@ static int zynqmp_r5_remoteproc_probe(struct platform_device *pdev) /* Match table for OF platform binding */ static const struct of_device_id zynqmp_r5_remoteproc_match[] = { + { .compatible = "xlnx,versal-net-r52fss", }, + { .compatible = "xlnx,versal-r5fss", }, { .compatible = "xlnx,zynqmp-r5fss", }, { /* end of list */ }, }; diff --git a/drivers/rpmsg/qcom_glink_ssr.c b/drivers/rpmsg/qcom_glink_ssr.c index 39ffa384c9b1..e71d3716c55c 100644 --- a/drivers/rpmsg/qcom_glink_ssr.c +++ b/drivers/rpmsg/qcom_glink_ssr.c @@ -154,6 +154,7 @@ static const struct rpmsg_device_id qcom_glink_ssr_match[] = { { "glink_ssr" }, {} }; +MODULE_DEVICE_TABLE(rpmsg, qcom_glink_ssr_match); static struct rpmsg_driver qcom_glink_ssr_driver = { .probe = qcom_glink_ssr_probe, diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 1cb8d7474428..d7a342510902 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -423,7 +423,7 @@ static struct rpmsg_eptdev *rpmsg_chrdev_eptdev_alloc(struct rpmsg_device *rpdev init_waitqueue_head(&eptdev->readq); device_initialize(dev); - dev->class = rpmsg_class; + dev->class = &rpmsg_class; dev->parent = parent; dev->groups = rpmsg_eptdev_groups; dev_set_drvdata(dev, eptdev); diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 4295c01a2861..0fa08266404d 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -20,7 +20,9 @@ #include "rpmsg_internal.h" -struct class *rpmsg_class; +const struct class rpmsg_class = { + .name = "rpmsg", +}; EXPORT_SYMBOL(rpmsg_class); /** @@ -715,16 +717,16 @@ static int __init rpmsg_init(void) { int ret; - rpmsg_class = class_create("rpmsg"); - if (IS_ERR(rpmsg_class)) { - pr_err("failed to create rpmsg class\n"); - return PTR_ERR(rpmsg_class); + ret = class_register(&rpmsg_class); + if (ret) { + pr_err("failed to register rpmsg class\n"); + return ret; } ret = bus_register(&rpmsg_bus); if (ret) { pr_err("failed to register rpmsg bus: %d\n", ret); - class_destroy(rpmsg_class); + class_destroy(&rpmsg_class); } return ret; } @@ -733,7 +735,7 @@ postcore_initcall(rpmsg_init); static void __exit rpmsg_fini(void) { bus_unregister(&rpmsg_bus); - class_destroy(rpmsg_class); + class_destroy(&rpmsg_class); } module_exit(rpmsg_fini); diff --git a/drivers/rpmsg/rpmsg_ctrl.c b/drivers/rpmsg/rpmsg_ctrl.c index c312794ba4b3..28f57945ccd9 100644 --- a/drivers/rpmsg/rpmsg_ctrl.c +++ b/drivers/rpmsg/rpmsg_ctrl.c @@ -150,7 +150,7 @@ static int rpmsg_ctrldev_probe(struct rpmsg_device *rpdev) dev = &ctrldev->dev; device_initialize(dev); dev->parent = &rpdev->dev; - dev->class = rpmsg_class; + dev->class = &rpmsg_class; mutex_init(&ctrldev->ctrl_lock); cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index b950d6f790a3..a3ba768138f1 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -18,7 +18,7 @@ #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev) #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) -extern struct class *rpmsg_class; +extern const struct class rpmsg_class; /** * struct rpmsg_device_ops - indirection table for the rpmsg_device operations diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 7e0ed7032f76..eb5dcbe37230 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -215,7 +215,7 @@ dasd_format(struct dasd_block *block, struct format_data_t *fdata) * enabling the device later. */ if (fdata->start_unit == 0) { - block->gdp->part0->bd_inode->i_blkbits = + block->gdp->part0->bd_mapping->host->i_blkbits = blksize_bits(fdata->blksize); } diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index ad227e6cb10e..35860c61468b 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1007,7 +1007,7 @@ msi_int0: goto msi_int1; } } - nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); + nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX); if (nvec < 1) return FAILED; msi_int1: diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 49c57a9c110b..e044ed09d7e0 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -7509,7 +7509,7 @@ fallback: */ static int hpsa_interrupt_mode(struct ctlr_info *h) { - unsigned int flags = PCI_IRQ_LEGACY; + unsigned int flags = PCI_IRQ_INTX; int ret; /* Some boards advertise MSI but don't really support it */ diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 388c8a10295a..31cf2d31cceb 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -9465,7 +9465,7 @@ static int ipr_probe_ioa(struct pci_dev *pdev, ipr_number_of_msix = IPR_MAX_MSIX_VECTORS; } - irq_flag = PCI_IRQ_LEGACY; + irq_flag = PCI_IRQ_INTX; if (ioa_cfg->ipr_chip->has_msi) irq_flag |= PCI_IRQ_MSI | PCI_IRQ_MSIX; rc = pci_alloc_irq_vectors(pdev, 1, ipr_number_of_msix, irq_flag); diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index def0d905b6d9..88acefbf9aea 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -6305,7 +6305,7 @@ static int megasas_init_fw(struct megasas_instance *instance) } if (!instance->msix_vectors) { - i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); + i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_INTX); if (i < 0) goto fail_init_adapter; } @@ -7844,7 +7844,7 @@ megasas_resume(struct device *dev) if (!instance->msix_vectors) { rval = pci_alloc_irq_vectors(instance->pdev, 1, 1, - PCI_IRQ_LEGACY); + PCI_IRQ_INTX); if (rval < 0) goto fail_reenable_msix; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 105917ea70ff..258647fc6bdd 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -3515,7 +3515,7 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "High IOPs queues : disabled\n"); ioc->reply_queue_count = 1; ioc->iopoll_q_start_index = ioc->reply_queue_count - 0; - r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_LEGACY); + r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_INTX); if (r < 0) { dfailprintk(ioc, ioc_info(ioc, "pci_alloc_irq_vector(legacy) failed (r=%d) !!!\n", diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 0efe2fc8b308..a2a084c8075e 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -4036,7 +4036,7 @@ static int pmcraid_register_interrupt_handler(struct pmcraid_instance *pinstance) { struct pci_dev *pdev = pinstance->pdev; - unsigned int irq_flag = PCI_IRQ_LEGACY, flag; + unsigned int irq_flag = PCI_IRQ_INTX, flag; int num_hrrq, rc, i; irq_handler_t isr; diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c index e2c7d8ef205f..dd69342bbe78 100644 --- a/drivers/scsi/scsicam.c +++ b/drivers/scsi/scsicam.c @@ -32,7 +32,7 @@ */ unsigned char *scsi_bios_ptable(struct block_device *dev) { - struct address_space *mapping = bdev_whole(dev)->bd_inode->i_mapping; + struct address_space *mapping = bdev_whole(dev)->bd_mapping; unsigned char *res = NULL; struct folio *folio; diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c index f88ecdb93a8a..c4fea077265e 100644 --- a/drivers/scsi/vmw_pvscsi.c +++ b/drivers/scsi/vmw_pvscsi.c @@ -1346,7 +1346,7 @@ exit: static int pvscsi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - unsigned int irq_flag = PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY; + unsigned int irq_flag = PCI_IRQ_ALL_TYPES; struct pvscsi_adapter *adapter; struct pvscsi_adapter adapter_temp; struct Scsi_Host *host = NULL; diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c index e45dc8261ab1..4cd26f3a21f5 100644 --- a/drivers/soundwire/amd_init.c +++ b/drivers/soundwire/amd_init.c @@ -17,42 +17,38 @@ #define ACP_PAD_PULLDOWN_CTRL 0x0001448 #define ACP_SW_PAD_KEEPER_EN 0x0001454 -#define AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9a -#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9f -#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7ffa -#define AMD_SDW0_PAD_EN_MASK 1 -#define AMD_SDW1_PAD_EN_MASK 0x10 -#define AMD_SDW_PAD_EN_MASK (AMD_SDW0_PAD_EN_MASK | AMD_SDW1_PAD_EN_MASK) +#define AMD_SDW0_PAD_CTRL_MASK 0x60 +#define AMD_SDW1_PAD_CTRL_MASK 5 +#define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK) +#define AMD_SDW0_PAD_EN 1 +#define AMD_SDW1_PAD_EN 0x10 +#define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN) static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) { - u32 val; - u32 pad_keeper_en_mask, pad_pulldown_ctrl_mask; + u32 pad_keeper_en, pad_pulldown_ctrl_mask; switch (link_mask) { case 1: - pad_keeper_en_mask = AMD_SDW0_PAD_EN_MASK; - pad_pulldown_ctrl_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK; + pad_keeper_en = AMD_SDW0_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK; break; case 2: - pad_keeper_en_mask = AMD_SDW1_PAD_EN_MASK; - pad_pulldown_ctrl_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK; + pad_keeper_en = AMD_SDW1_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK; break; case 3: - pad_keeper_en_mask = AMD_SDW_PAD_EN_MASK; - pad_pulldown_ctrl_mask = AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK; + pad_keeper_en = AMD_SDW_PAD_EN; + pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK; break; default: dev_err(dev, "No SDW Links are enabled\n"); return -ENODEV; } - val = readl(mmio + ACP_SW_PAD_KEEPER_EN); - val |= pad_keeper_en_mask; - writel(val, mmio + ACP_SW_PAD_KEEPER_EN); - val = readl(mmio + ACP_PAD_PULLDOWN_CTRL); - val &= pad_pulldown_ctrl_mask; - writel(val, mmio + ACP_PAD_PULLDOWN_CTRL); + amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en); + amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0); + return 0; } diff --git a/drivers/soundwire/amd_init.h b/drivers/soundwire/amd_init.h index 928b0c707162..5e7b43836a37 100644 --- a/drivers/soundwire/amd_init.h +++ b/drivers/soundwire/amd_init.h @@ -10,4 +10,12 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager); +static inline void amd_updatel(void __iomem *mmio, int offset, u32 mask, u32 val) +{ + u32 tmp; + + tmp = readl(mmio + offset); + tmp = (tmp & ~mask) | val; + writel(tmp, mmio + offset); +} #endif diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 6bcf8e75273c..20d94bcfc9b4 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -89,9 +89,8 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) u32 val; mutex_lock(amd_manager->acp_sdw_lock); - val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); - val |= sdw_manager_reg_mask_array[amd_manager->instance]; - writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); + val = sdw_manager_reg_mask_array[amd_manager->instance]; + amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val, val); mutex_unlock(amd_manager->acp_sdw_lock); writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio + @@ -103,12 +102,12 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager) { - u32 val; + u32 irq_mask; mutex_lock(amd_manager->acp_sdw_lock); - val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); - val &= ~sdw_manager_reg_mask_array[amd_manager->instance]; - writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); + irq_mask = sdw_manager_reg_mask_array[amd_manager->instance]; + amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), + irq_mask, 0); mutex_unlock(amd_manager->acp_sdw_lock); writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index f3fec15c3112..191e6cc6f962 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1312,18 +1312,18 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) if (!(19200000 % mclk_freq)) { mclk_freq = 19200000; base = SDW_SCP_BASE_CLOCK_19200000_HZ; - } else if (!(24000000 % mclk_freq)) { - mclk_freq = 24000000; - base = SDW_SCP_BASE_CLOCK_24000000_HZ; - } else if (!(24576000 % mclk_freq)) { - mclk_freq = 24576000; - base = SDW_SCP_BASE_CLOCK_24576000_HZ; } else if (!(22579200 % mclk_freq)) { mclk_freq = 22579200; base = SDW_SCP_BASE_CLOCK_22579200_HZ; + } else if (!(24576000 % mclk_freq)) { + mclk_freq = 24576000; + base = SDW_SCP_BASE_CLOCK_24576000_HZ; } else if (!(32000000 % mclk_freq)) { mclk_freq = 32000000; base = SDW_SCP_BASE_CLOCK_32000000_HZ; + } else if (!(96000000 % mclk_freq)) { + mclk_freq = 24000000; + base = SDW_SCP_BASE_CLOCK_24000000_HZ; } else { dev_err(&slave->dev, "Unsupported clock base, mclk %d\n", @@ -1474,7 +1474,7 @@ static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status) } do { - clear = status & ~SDW_DP0_INTERRUPTS; + clear = status & ~(SDW_DP0_INTERRUPTS | SDW_DP0_SDCA_CASCADE); if (status & SDW_DP0_INT_TEST_FAIL) { dev_err(&slave->dev, "Test fail for port 0\n"); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index fd65b2360fc1..c32faace618f 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -126,8 +126,8 @@ static int sdw_drv_probe(struct device *dev) if (slave->prop.use_domain_irq) sdw_irq_create_mapping(slave); - /* init the sysfs as we have properties now */ - ret = sdw_slave_sysfs_init(slave); + /* init the dynamic sysfs attributes we need */ + ret = sdw_slave_sysfs_dpn_init(slave); if (ret < 0) dev_warn(dev, "Slave sysfs init failed:%d\n", ret); @@ -221,6 +221,7 @@ int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) drv->driver.probe = sdw_drv_probe; drv->driver.remove = sdw_drv_remove; drv->driver.shutdown = sdw_drv_shutdown; + drv->driver.dev_groups = sdw_attr_groups; return driver_register(&drv->driver); } diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 0efc1c3bee5f..74da99034dab 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1236,7 +1236,7 @@ EXPORT_SYMBOL(sdw_cdns_enable_interrupt); static int cdns_allocate_pdi(struct sdw_cdns *cdns, struct sdw_cdns_pdi **stream, - u32 num, u32 pdi_offset) + u32 num) { struct sdw_cdns_pdi *pdi; int i; @@ -1249,7 +1249,7 @@ static int cdns_allocate_pdi(struct sdw_cdns *cdns, return -ENOMEM; for (i = 0; i < num; i++) { - pdi[i].num = i + pdi_offset; + pdi[i].num = i; } *stream = pdi; @@ -1266,7 +1266,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config) { struct sdw_cdns_streams *stream; - int offset; int ret; cdns->pcm.num_bd = config.pcm_bd; @@ -1277,24 +1276,15 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, stream = &cdns->pcm; /* we allocate PDI0 and PDI1 which are used for Bulk */ - offset = 0; - - ret = cdns_allocate_pdi(cdns, &stream->bd, - stream->num_bd, offset); + ret = cdns_allocate_pdi(cdns, &stream->bd, stream->num_bd); if (ret) return ret; - offset += stream->num_bd; - - ret = cdns_allocate_pdi(cdns, &stream->in, - stream->num_in, offset); + ret = cdns_allocate_pdi(cdns, &stream->in, stream->num_in); if (ret) return ret; - offset += stream->num_in; - - ret = cdns_allocate_pdi(cdns, &stream->out, - stream->num_out, offset); + ret = cdns_allocate_pdi(cdns, &stream->out, stream->num_out); if (ret) return ret; @@ -1329,6 +1319,12 @@ static void cdns_init_clock_ctrl(struct sdw_cdns *cdns) u32 ssp_interval; int divider; + dev_dbg(cdns->dev, "mclk %d max %d row %d col %d\n", + prop->mclk_freq, + prop->max_clk_freq, + prop->default_row, + prop->default_col); + /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; @@ -1802,7 +1798,6 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); * cdns_find_pdi() - Find a free PDI * * @cdns: Cadence instance - * @offset: Starting offset * @num: Number of PDIs * @pdi: PDI instances * @dai_id: DAI id @@ -1811,14 +1806,13 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); * expected to match, return NULL otherwise. */ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, - unsigned int offset, unsigned int num, struct sdw_cdns_pdi *pdi, int dai_id) { int i; - for (i = offset; i < offset + num; i++) + for (i = 0; i < num; i++) if (pdi[i].num == dai_id) return &pdi[i]; @@ -1872,15 +1866,15 @@ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_pdi *pdi = NULL; if (dir == SDW_DATA_DIR_RX) - pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, + pdi = cdns_find_pdi(cdns, stream->num_in, stream->in, dai_id); else - pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, + pdi = cdns_find_pdi(cdns, stream->num_out, stream->out, dai_id); /* check if we found a PDI, else find in bi-directional */ if (!pdi) - pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, + pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd, dai_id); if (pdi) { diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1287a325c435..01e1a0f3ec39 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -345,8 +345,10 @@ static int intel_link_power_up(struct sdw_intel *sdw) u32 spa_mask, cpa_mask; u32 link_control; int ret = 0; + u32 clock_source; u32 syncprd; u32 sync_reg; + bool lcap_mlcs; mutex_lock(sdw->link_res->shim_lock); @@ -358,12 +360,35 @@ static int intel_link_power_up(struct sdw_intel *sdw) * is only dependent on the oscillator clock provided to * the IP, so adjust based on _DSD properties reported in DSDT * tables. The values reported are based on either 24MHz - * (CNL/CML) or 38.4 MHz (ICL/TGL+). + * (CNL/CML) or 38.4 MHz (ICL/TGL+). On MeteorLake additional + * frequencies are available with the MLCS clock source selection. */ - if (prop->mclk_freq % 6000000) - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; - else - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; + lcap_mlcs = intel_readl(shim, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_MLCS_MASK; + + if (prop->mclk_freq % 6000000) { + if (prop->mclk_freq % 2400000) { + if (lcap_mlcs) { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24_576; + clock_source = SDW_SHIM_MLCS_CARDINAL_CLK; + } else { + dev_err(sdw->cdns.dev, "%s: invalid clock configuration, mclk %d lcap_mlcs %d\n", + __func__, prop->mclk_freq, lcap_mlcs); + ret = -EINVAL; + goto out; + } + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; + clock_source = SDW_SHIM_MLCS_XTAL_CLK; + } + } else { + if (lcap_mlcs) { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_96; + clock_source = SDW_SHIM_MLCS_AUDIO_PLL_CLK; + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; + clock_source = SDW_SHIM_MLCS_XTAL_CLK; + } + } if (!*shim_mask) { dev_dbg(sdw->cdns.dev, "powering up all links\n"); @@ -403,6 +428,13 @@ static int intel_link_power_up(struct sdw_intel *sdw) "Failed to set SHIM_SYNC: %d\n", ret); goto out; } + + /* update link clock if needed */ + if (lcap_mlcs) { + link_control = intel_readl(shim, SDW_SHIM_LCTL); + u32p_replace_bits(&link_control, clock_source, SDW_SHIM_LCTL_MLCS_MASK); + intel_writel(shim, SDW_SHIM_LCTL, link_control); + } } *shim_mask |= BIT(link_id); @@ -668,6 +700,24 @@ static int intel_params_stream(struct sdw_intel *sdw, * DAI routines */ +static int intel_free_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int link_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_free_data free_data; + + free_data.substream = substream; + free_data.dai = dai; + free_data.link_id = link_id; + + if (res->ops && res->ops->free_stream && res->dev) + return res->ops->free_stream(res->dev, &free_data); + + return 0; +} + static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -799,6 +849,7 @@ static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dai_runtime *dai_runtime; int ret; @@ -819,6 +870,12 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } + ret = intel_free_stream(sdw, substream, dai, sdw->instance); + if (ret < 0) { + dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); + return ret; + } + dai_runtime->pdi = NULL; return 0; @@ -1062,4 +1119,3 @@ const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = { .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, }; EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL); - diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 511932c55216..b68e74c294e7 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -58,6 +58,13 @@ struct sdw_intel { #endif }; +struct sdw_intel_prop { + u16 doaise; + u16 doais; + u16 dodse; + u16 dods; +}; + enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 8280baa3254b..8b1b6ad420cf 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -10,8 +10,10 @@ #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_intel.h> -#include <sound/pcm_params.h> +#include <sound/hdaudio.h> #include <sound/hda-mlink.h> +#include <sound/hda_register.h> +#include <sound/pcm_params.h> #include "cadence_master.h" #include "bus.h" #include "intel.h" @@ -23,49 +25,86 @@ static void intel_shim_vs_init(struct sdw_intel *sdw) { void __iomem *shim_vs = sdw->link_res->shim_vs; + struct sdw_bus *bus = &sdw->cdns.bus; + struct sdw_intel_prop *intel_prop; + u16 doaise; + u16 doais; + u16 dodse; + u16 dods; u16 act; + intel_prop = bus->vendor_specific_prop; + doaise = intel_prop->doaise; + doais = intel_prop->doais; + dodse = intel_prop->dodse; + dods = intel_prop->dods; + act = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL); - u16p_replace_bits(&act, 0x1, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); + u16p_replace_bits(&act, doaise, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAISE); + u16p_replace_bits(&act, doais, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); + u16p_replace_bits(&act, dodse, SDW_SHIM2_INTEL_VS_ACTMCTL_DODSE); + u16p_replace_bits(&act, dods, SDW_SHIM2_INTEL_VS_ACTMCTL_DODS); act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE; - act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DODS; intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL, act); usleep_range(10, 15); } -static int intel_shim_check_wake(struct sdw_intel *sdw) +static void intel_shim_vs_set_clock_source(struct sdw_intel *sdw, u32 source) { - void __iomem *shim_vs; - u16 wake_sts; + void __iomem *shim_vs = sdw->link_res->shim_vs; + u32 val; - shim_vs = sdw->link_res->shim_vs; - wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); + val = intel_readl(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL); - return wake_sts & SDW_SHIM2_INTEL_VS_WAKEEN_PWS; + u32p_replace_bits(&val, source, SDW_SHIM2_INTEL_VS_LVSCTL_MLCS); + + intel_writel(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL, val); + + dev_dbg(sdw->cdns.dev, "clock source %d LVSCTL %#x\n", source, val); +} + +static int intel_shim_check_wake(struct sdw_intel *sdw) +{ + /* + * We follow the HDaudio example and resume unconditionally + * without checking the WAKESTS bit for that specific link + */ + + return 1; } static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) { - void __iomem *shim_vs = sdw->link_res->shim_vs; + u16 lsdiid = 0; u16 wake_en; u16 wake_sts; + int ret; + + mutex_lock(sdw->link_res->shim_lock); + + ret = hdac_bus_eml_sdw_get_lsdiid_unlocked(sdw->link_res->hbus, sdw->instance, &lsdiid); + if (ret < 0) + goto unlock; - wake_en = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN); + wake_en = snd_hdac_chip_readw(sdw->link_res->hbus, WAKEEN); if (wake_enable) { /* Enable the wakeup */ - wake_en |= SDW_SHIM2_INTEL_VS_WAKEEN_PWE; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + wake_en |= lsdiid; + + snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en); } else { /* Disable the wake up interrupt */ - wake_en &= ~SDW_SHIM2_INTEL_VS_WAKEEN_PWE; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + wake_en &= ~lsdiid; + snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en); /* Clear wake status (W1C) */ - wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); - wake_sts |= SDW_SHIM2_INTEL_VS_WAKEEN_PWS; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS, wake_sts); + wake_sts = snd_hdac_chip_readw(sdw->link_res->hbus, STATESTS); + wake_sts |= lsdiid; + snd_hdac_chip_writew(sdw->link_res->hbus, STATESTS, wake_sts); } +unlock: + mutex_unlock(sdw->link_res->shim_lock); } static int intel_link_power_up(struct sdw_intel *sdw) @@ -74,36 +113,45 @@ static int intel_link_power_up(struct sdw_intel *sdw) struct sdw_master_prop *prop = &bus->prop; u32 *shim_mask = sdw->link_res->shim_mask; unsigned int link_id = sdw->instance; + u32 clock_source; u32 syncprd; int ret; + if (prop->mclk_freq % 6000000) { + if (prop->mclk_freq % 2400000) { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24_576; + clock_source = SDW_SHIM2_MLCS_CARDINAL_CLK; + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; + clock_source = SDW_SHIM2_MLCS_XTAL_CLK; + } + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_96; + clock_source = SDW_SHIM2_MLCS_AUDIO_PLL_CLK; + } + mutex_lock(sdw->link_res->shim_lock); + ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", + __func__, ret); + goto out; + } + + intel_shim_vs_set_clock_source(sdw, clock_source); + if (!*shim_mask) { /* we first need to program the SyncPRD/CPU registers */ dev_dbg(sdw->cdns.dev, "first link up, programming SYNCPRD\n"); - if (prop->mclk_freq % 6000000) - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; - else - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; - ret = hdac_bus_eml_sdw_set_syncprd_unlocked(sdw->link_res->hbus, syncprd); if (ret < 0) { dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_set_syncprd failed: %d\n", __func__, ret); goto out; } - } - - ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); - if (ret < 0) { - dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", - __func__, ret); - goto out; - } - if (!*shim_mask) { /* SYNCPU will change once link is active */ ret = hdac_bus_eml_sdw_wait_syncpu_unlocked(sdw->link_res->hbus); if (ret < 0) { @@ -268,6 +316,11 @@ static int intel_hw_params(struct snd_pcm_substream *substream, goto error; } + /* use same definitions for alh_id as previous generations */ + pdi->intel_alh_id = (sdw->instance * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; + /* the SHIM will be configured in the callback functions */ sdw_cdns_config_stream(cdns, ch, dir, pdi); diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 95125cc2fc59..17cf27e6ea73 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -122,6 +122,7 @@ static void generic_new_peripheral_assigned(struct sdw_bus *bus, static int sdw_master_read_intel_prop(struct sdw_bus *bus) { struct sdw_master_prop *prop = &bus->prop; + struct sdw_intel_prop *intel_prop; struct fwnode_handle *link; char name[32]; u32 quirk_mask; @@ -153,6 +154,36 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH | SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY; + intel_prop = devm_kzalloc(bus->dev, sizeof(*intel_prop), GFP_KERNEL); + if (!intel_prop) + return -ENOMEM; + + /* initialize with hardware defaults, in case the properties are not found */ + intel_prop->doaise = 0x1; + intel_prop->doais = 0x3; + intel_prop->dodse = 0x0; + intel_prop->dods = 0x1; + + fwnode_property_read_u16(link, + "intel-sdw-doaise", + &intel_prop->doaise); + fwnode_property_read_u16(link, + "intel-sdw-doais", + &intel_prop->doais); + fwnode_property_read_u16(link, + "intel-sdw-dodse", + &intel_prop->dodse); + fwnode_property_read_u16(link, + "intel-sdw-dods", + &intel_prop->dods); + bus->vendor_specific_prop = intel_prop; + + dev_dbg(bus->dev, "doaise %#x doais %#x dodse %#x dods %#x\n", + intel_prop->doaise, + intel_prop->doais, + intel_prop->dodse, + intel_prop->dods); + return 0; } @@ -440,7 +471,7 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) * PM calls */ -static int intel_resume_child_device(struct device *dev, void *data) +int intel_resume_child_device(struct device *dev, void *data) { int ret; struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -454,9 +485,9 @@ static int intel_resume_child_device(struct device *dev, void *data) return 0; } - ret = pm_request_resume(dev); + ret = pm_runtime_resume(dev); if (ret < 0) { - dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); + dev_err(dev, "%s: pm_runtime_resume failed: %d\n", __func__, ret); return ret; } @@ -499,9 +530,9 @@ static int __maybe_unused intel_pm_prepare(struct device *dev) * first resume the device for this link. This will also by construction * resume the PCI parent device. */ - ret = pm_request_resume(dev); + ret = pm_runtime_resume(dev); if (ret < 0) { - dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); + dev_err(dev, "%s: pm_runtime_resume failed: %d\n", __func__, ret); return 0; } diff --git a/drivers/soundwire/intel_auxdevice.h b/drivers/soundwire/intel_auxdevice.h index a00ecde95563..acaae0bd6592 100644 --- a/drivers/soundwire/intel_auxdevice.h +++ b/drivers/soundwire/intel_auxdevice.h @@ -6,6 +6,7 @@ int intel_link_startup(struct auxiliary_device *auxdev); int intel_link_process_wakeen_event(struct auxiliary_device *auxdev); +int intel_resume_child_device(struct device *dev, void *data); struct sdw_intel_link_dev { struct auxiliary_device auxdev; diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 534c8795e7e8..a09134b97cd6 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -16,6 +16,7 @@ #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_intel.h> #include "cadence_master.h" +#include "bus.h" #include "intel.h" #include "intel_auxdevice.h" @@ -356,6 +357,19 @@ EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); */ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { + struct sdw_intel_link_res *link; + + /* we first resume links and devices and wait synchronously before the cleanup */ + list_for_each_entry(link, &ctx->link_list, list) { + struct sdw_bus *bus = &link->cdns->bus; + int ret; + + ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device); + if (ret < 0) + dev_err(bus->dev, "%s: intel_resume_child_device failed: %d\n", + __func__, ret); + } + sdw_intel_cleanup(ctx); kfree(ctx->ids); kfree(ctx->ldev); diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 3c4d6debab1f..ce5cf3ecceb5 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -905,6 +905,18 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) return 0; } +static int qcom_swrm_read_prop(struct sdw_bus *bus) +{ + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + + if (ctrl->version >= SWRM_VERSION_2_0_0) { + bus->multi_link = true; + bus->hw_sync_min_links = 3; + } + + return 0; +} + static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) { @@ -1056,6 +1068,7 @@ static const struct sdw_master_port_ops qcom_swrm_port_ops = { }; static const struct sdw_master_ops qcom_swrm_ops = { + .read_prop = qcom_swrm_read_prop, .xfer_msg = qcom_swrm_xfer_msg, .pre_bank_switch = qcom_swrm_pre_bank_switch, }; @@ -1173,6 +1186,15 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, mutex_lock(&ctrl->port_lock); list_for_each_entry(m_rt, &stream->master_list, stream_node) { + /* + * For streams with multiple masters: + * Allocate ports only for devices connected to this master. + * Such devices will have ports allocated by their own master + * and its qcom_swrm_stream_alloc_ports() call. + */ + if (ctrl->bus.id != m_rt->bus->id) + continue; + if (m_rt->direction == SDW_DATA_DIR_RX) { maxport = ctrl->num_dout_ports; port_mask = &ctrl->dout_port_mask; @@ -1636,14 +1658,12 @@ err_init: return ret; } -static int qcom_swrm_remove(struct platform_device *pdev) +static void qcom_swrm_remove(struct platform_device *pdev) { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev); sdw_bus_master_delete(&ctrl->bus); clk_disable_unprepare(ctrl->hclk); - - return 0; } static int __maybe_unused swrm_runtime_resume(struct device *dev) @@ -1769,7 +1789,7 @@ MODULE_DEVICE_TABLE(of, qcom_swrm_of_match); static struct platform_driver qcom_swrm_driver = { .probe = &qcom_swrm_probe, - .remove = &qcom_swrm_remove, + .remove_new = qcom_swrm_remove, .driver = { .name = "qcom-soundwire", .of_match_table = qcom_swrm_of_match, diff --git a/drivers/soundwire/sysfs_local.h b/drivers/soundwire/sysfs_local.h index 7268bc24c538..fa048e112629 100644 --- a/drivers/soundwire/sysfs_local.h +++ b/drivers/soundwire/sysfs_local.h @@ -11,8 +11,10 @@ /* basic attributes to report status of Slave (attachment, dev_num) */ extern const struct attribute_group *sdw_slave_status_attr_groups[]; +/* attributes for all soundwire devices */ +extern const struct attribute_group *sdw_attr_groups[]; + /* additional device-managed properties reported after driver probe */ -int sdw_slave_sysfs_init(struct sdw_slave *slave); int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave); #endif /* __SDW_SYSFS_LOCAL_H */ diff --git a/drivers/soundwire/sysfs_slave.c b/drivers/soundwire/sysfs_slave.c index 3210359cd944..f4259710dd0f 100644 --- a/drivers/soundwire/sysfs_slave.c +++ b/drivers/soundwire/sysfs_slave.c @@ -105,7 +105,10 @@ static struct attribute *slave_attrs[] = { &dev_attr_modalias.attr, NULL, }; -ATTRIBUTE_GROUPS(slave); + +static const struct attribute_group slave_attr_group = { + .attrs = slave_attrs, +}; static struct attribute *slave_dev_attrs[] = { &dev_attr_mipi_revision.attr, @@ -126,10 +129,6 @@ static struct attribute *slave_dev_attrs[] = { NULL, }; -/* - * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory - * for device-level properties - */ static const struct attribute_group sdw_slave_dev_attr_group = { .attrs = slave_dev_attrs, .name = "dev-properties", @@ -181,41 +180,38 @@ static struct attribute *dp0_attrs[] = { NULL, }; -/* - * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory - * for dp0-level properties - */ -static const struct attribute_group dp0_group = { - .attrs = dp0_attrs, - .name = "dp0", -}; - -int sdw_slave_sysfs_init(struct sdw_slave *slave) +static umode_t dp0_attr_visible(struct kobject *kobj, struct attribute *attr, + int n) { - int ret; + struct sdw_slave *slave = dev_to_sdw_dev(kobj_to_dev(kobj)); - ret = devm_device_add_groups(&slave->dev, slave_groups); - if (ret < 0) - return ret; + if (slave->prop.dp0_prop) + return attr->mode; + return 0; +} - ret = devm_device_add_group(&slave->dev, &sdw_slave_dev_attr_group); - if (ret < 0) - return ret; +static bool dp0_group_visible(struct kobject *kobj) +{ + struct sdw_slave *slave = dev_to_sdw_dev(kobj_to_dev(kobj)); - if (slave->prop.dp0_prop) { - ret = devm_device_add_group(&slave->dev, &dp0_group); - if (ret < 0) - return ret; - } + if (slave->prop.dp0_prop) + return true; + return false; +} +DEFINE_SYSFS_GROUP_VISIBLE(dp0); - if (slave->prop.source_ports || slave->prop.sink_ports) { - ret = sdw_slave_sysfs_dpn_init(slave); - if (ret < 0) - return ret; - } +static const struct attribute_group dp0_group = { + .attrs = dp0_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(dp0), + .name = "dp0", +}; - return 0; -} +const struct attribute_group *sdw_attr_groups[] = { + &slave_attr_group, + &sdw_slave_dev_attr_group, + &dp0_group, + NULL, +}; /* * the status is shown in capital letters for UNATTACHED and RESERVED diff --git a/drivers/soundwire/sysfs_slave_dpn.c b/drivers/soundwire/sysfs_slave_dpn.c index c4b6543c09fd..a3fb380ee519 100644 --- a/drivers/soundwire/sysfs_slave_dpn.c +++ b/drivers/soundwire/sysfs_slave_dpn.c @@ -283,6 +283,9 @@ int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave) int ret; int i; + if (!slave->prop.source_ports && !slave->prop.sink_ports) + return 0; + mask = slave->prop.source_ports; for_each_set_bit(i, &mask, 32) { ret = add_all_attributes(&slave->dev, i, 1); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index e2e4f99f9d34..40af74b55933 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -4108,7 +4108,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES); } else { pci_dbg(dev, "Using legacy interrupts\n"); - rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); + rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); } if (rc < 0) { kfree(priv); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ee3156f49533..a08f3f228e6d 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -189,7 +189,8 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver) * make sure irq setup is not touched for xhci in generic hcd code */ if ((driver->flags & HCD_MASK) < HCD_USB3) { - retval = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSI); + retval = pci_alloc_irq_vectors(dev, 1, 1, + PCI_IRQ_INTX | PCI_IRQ_MSI); if (retval < 0) { dev_err(&dev->dev, "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h index 5cf885b09986..5d2c470a49ac 100644 --- a/fs/bcachefs/util.h +++ b/fs/bcachefs/util.h @@ -445,11 +445,6 @@ static inline unsigned fract_exp_two(unsigned x, unsigned fract_bits) void bch2_bio_map(struct bio *bio, void *base, size_t); int bch2_bio_alloc_pages(struct bio *, size_t, gfp_t); -static inline sector_t bdev_sectors(struct block_device *bdev) -{ - return bdev->bd_inode->i_size >> 9; -} - #define closure_bio_submit(bio, cl) \ do { \ closure_get(cl); \ diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 7696beec4c21..7130040d92ab 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -316,7 +316,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); set_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state); device->dev_stats_valid = 1; - set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE); + set_blocksize(bdev_file, BTRFS_BDEV_BLOCKSIZE); device->fs_devices = fs_devices; ret = btrfs_get_dev_zone_info(device, false); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a91a8056758a..1b20b3e390df 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3656,7 +3656,7 @@ struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev, struct btrfs_super_block *super; struct page *page; u64 bytenr, bytenr_orig; - struct address_space *mapping = bdev->bd_inode->i_mapping; + struct address_space *mapping = bdev->bd_mapping; int ret; bytenr_orig = btrfs_sb_offset(copy_num); @@ -3743,7 +3743,7 @@ static int write_dev_supers(struct btrfs_device *device, struct btrfs_super_block *sb, int max_mirrors) { struct btrfs_fs_info *fs_info = device->fs_info; - struct address_space *mapping = device->bdev->bd_inode->i_mapping; + struct address_space *mapping = device->bdev->bd_mapping; SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); int i; int ret; @@ -3861,7 +3861,7 @@ static int wait_dev_supers(struct btrfs_device *device, int max_mirrors) device->commit_total_bytes) break; - folio = filemap_get_folio(device->bdev->bd_inode->i_mapping, + folio = filemap_get_folio(device->bdev->bd_mapping, bytenr >> PAGE_SHIFT); /* If the folio has been removed, then we know it completed. */ if (IS_ERR(folio)) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index b6a701011fb0..c39145e8c4ad 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -482,10 +482,12 @@ btrfs_get_bdev_and_sb(const char *device_path, blk_mode_t flags, void *holder, if (flush) sync_blockdev(bdev); - ret = set_blocksize(bdev, BTRFS_BDEV_BLOCKSIZE); - if (ret) { - fput(*bdev_file); - goto error; + if (holder) { + ret = set_blocksize(*bdev_file, BTRFS_BDEV_BLOCKSIZE); + if (ret) { + fput(*bdev_file); + goto error; + } } invalidate_bdev(bdev); *disk_super = btrfs_read_dev_super(bdev); @@ -498,6 +500,7 @@ btrfs_get_bdev_and_sb(const char *device_path, blk_mode_t flags, void *holder, return 0; error: + *disk_super = NULL; *bdev_file = NULL; return ret; } @@ -1287,7 +1290,7 @@ static struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev return ERR_PTR(-EINVAL); /* pull in the page with our super */ - page = read_cache_page_gfp(bdev->bd_inode->i_mapping, index, GFP_KERNEL); + page = read_cache_page_gfp(bdev->bd_mapping, index, GFP_KERNEL); if (IS_ERR(page)) return ERR_CAST(page); @@ -2714,7 +2717,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); clear_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state); device->dev_stats_valid = 1; - set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE); + set_blocksize(device->bdev_file, BTRFS_BDEV_BLOCKSIZE); if (seeding_dev) { btrfs_clear_sb_rdonly(sb); diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 4cba80b34387..9b43fa493219 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -118,7 +118,7 @@ static int sb_write_pointer(struct block_device *bdev, struct blk_zone *zones, return -ENOENT; } else if (full[0] && full[1]) { /* Compare two super blocks */ - struct address_space *mapping = bdev->bd_inode->i_mapping; + struct address_space *mapping = bdev->bd_mapping; struct page *page[BTRFS_NR_SB_LOG_ZONES]; struct btrfs_super_block *super[BTRFS_NR_SB_LOG_ZONES]; int i; diff --git a/fs/buffer.c b/fs/buffer.c index ed698caa8834..8c19e705b9c3 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -189,8 +189,8 @@ EXPORT_SYMBOL(end_buffer_write_sync); static struct buffer_head * __find_get_block_slow(struct block_device *bdev, sector_t block) { - struct inode *bd_inode = bdev->bd_inode; - struct address_space *bd_mapping = bd_inode->i_mapping; + struct address_space *bd_mapping = bdev->bd_mapping; + const int blkbits = bd_mapping->host->i_blkbits; struct buffer_head *ret = NULL; pgoff_t index; struct buffer_head *bh; @@ -199,7 +199,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block) int all_mapped = 1; static DEFINE_RATELIMIT_STATE(last_warned, HZ, 1); - index = ((loff_t)block << bd_inode->i_blkbits) / PAGE_SIZE; + index = ((loff_t)block << blkbits) / PAGE_SIZE; folio = __filemap_get_folio(bd_mapping, index, FGP_ACCESSED, 0); if (IS_ERR(folio)) goto out; @@ -233,7 +233,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block) (unsigned long long)block, (unsigned long long)bh->b_blocknr, bh->b_state, bh->b_size, bdev, - 1 << bd_inode->i_blkbits); + 1 << blkbits); } out_unlock: spin_unlock(&bd_mapping->i_private_lock); @@ -1041,12 +1041,12 @@ static sector_t folio_init_buffers(struct folio *folio, static bool grow_dev_folio(struct block_device *bdev, sector_t block, pgoff_t index, unsigned size, gfp_t gfp) { - struct inode *inode = bdev->bd_inode; + struct address_space *mapping = bdev->bd_mapping; struct folio *folio; struct buffer_head *bh; sector_t end_block = 0; - folio = __filemap_get_folio(inode->i_mapping, index, + folio = __filemap_get_folio(mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); if (IS_ERR(folio)) return false; @@ -1080,10 +1080,10 @@ static bool grow_dev_folio(struct block_device *bdev, sector_t block, * lock to be atomic wrt __find_get_block(), which does not * run under the folio lock. */ - spin_lock(&inode->i_mapping->i_private_lock); + spin_lock(&mapping->i_private_lock); link_dev_buffers(folio, bh); end_block = folio_init_buffers(folio, bdev, size); - spin_unlock(&inode->i_mapping->i_private_lock); + spin_unlock(&mapping->i_private_lock); unlock: folio_unlock(folio); folio_put(folio); @@ -1486,7 +1486,7 @@ struct buffer_head *__bread_gfp(struct block_device *bdev, sector_t block, { struct buffer_head *bh; - gfp |= mapping_gfp_constraint(bdev->bd_inode->i_mapping, ~__GFP_FS); + gfp |= mapping_gfp_constraint(bdev->bd_mapping, ~__GFP_FS); /* * Prefer looping in the allocator rather than here, at least that @@ -1719,16 +1719,16 @@ EXPORT_SYMBOL(create_empty_buffers); */ void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len) { - struct inode *bd_inode = bdev->bd_inode; - struct address_space *bd_mapping = bd_inode->i_mapping; + struct address_space *bd_mapping = bdev->bd_mapping; + const int blkbits = bd_mapping->host->i_blkbits; struct folio_batch fbatch; - pgoff_t index = ((loff_t)block << bd_inode->i_blkbits) / PAGE_SIZE; + pgoff_t index = ((loff_t)block << blkbits) / PAGE_SIZE; pgoff_t end; int i, count; struct buffer_head *bh; struct buffer_head *head; - end = ((loff_t)(block + len - 1) << bd_inode->i_blkbits) / PAGE_SIZE; + end = ((loff_t)(block + len - 1) << blkbits) / PAGE_SIZE; folio_batch_init(&fbatch); while (filemap_get_folios(bd_mapping, &index, end, &fbatch)) { count = folio_batch_count(&fbatch); diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 9901057a15ba..460690ca0174 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -183,7 +183,7 @@ static int next_buffer; static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset, unsigned int len) { - struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; + struct address_space *mapping = sb->s_bdev->bd_mapping; struct file_ra_state ra = {}; struct page *pages[BLKS_PER_BUF]; unsigned i, blocknr, buffer; diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 52524bd9698b..5fc03c1e2757 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -29,11 +29,9 @@ void erofs_put_metabuf(struct erofs_buf *buf) * Derive the block size from inode->i_blkbits to make compatible with * anonymous inode in fscache mode. */ -void *erofs_bread(struct erofs_buf *buf, erofs_blk_t blkaddr, +void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, enum erofs_kmap_type type) { - struct inode *inode = buf->inode; - erofs_off_t offset = (erofs_off_t)blkaddr << inode->i_blkbits; pgoff_t index = offset >> PAGE_SHIFT; struct page *page = buf->page; struct folio *folio; @@ -43,7 +41,7 @@ void *erofs_bread(struct erofs_buf *buf, erofs_blk_t blkaddr, erofs_put_metabuf(buf); nofs_flag = memalloc_nofs_save(); - folio = read_cache_folio(inode->i_mapping, index, NULL, NULL); + folio = read_cache_folio(buf->mapping, index, NULL, NULL); memalloc_nofs_restore(nofs_flag); if (IS_ERR(folio)) return folio; @@ -68,16 +66,16 @@ void *erofs_bread(struct erofs_buf *buf, erofs_blk_t blkaddr, void erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb) { if (erofs_is_fscache_mode(sb)) - buf->inode = EROFS_SB(sb)->s_fscache->inode; + buf->mapping = EROFS_SB(sb)->s_fscache->inode->i_mapping; else - buf->inode = sb->s_bdev->bd_inode; + buf->mapping = sb->s_bdev->bd_mapping; } void *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb, erofs_blk_t blkaddr, enum erofs_kmap_type type) { erofs_init_metabuf(buf, sb); - return erofs_bread(buf, blkaddr, type); + return erofs_bread(buf, erofs_pos(sb, blkaddr), type); } static int erofs_map_blocks_flatmode(struct inode *inode, diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c index b80abec0531a..2193a6710c8f 100644 --- a/fs/erofs/dir.c +++ b/fs/erofs/dir.c @@ -58,12 +58,12 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx) int err = 0; bool initial = true; - buf.inode = dir; + buf.mapping = dir->i_mapping; while (ctx->pos < dirsize) { struct erofs_dirent *de; unsigned int nameoff, maxsize; - de = erofs_bread(&buf, i, EROFS_KMAP); + de = erofs_bread(&buf, erofs_pos(sb, i), EROFS_KMAP); if (IS_ERR(de)) { erofs_err(sb, "fail to readdir of logical block %u of nid %llu", i, EROFS_I(dir)->nid); diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 21def866a482..00bbb288c08c 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -216,7 +216,7 @@ enum erofs_kmap_type { }; struct erofs_buf { - struct inode *inode; + struct address_space *mapping; struct page *page; void *base; enum erofs_kmap_type kmap_type; @@ -402,7 +402,7 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, erofs_off_t *offset, int *lengthp); void erofs_unmap_metabuf(struct erofs_buf *buf); void erofs_put_metabuf(struct erofs_buf *buf); -void *erofs_bread(struct erofs_buf *buf, erofs_blk_t blkaddr, +void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, enum erofs_kmap_type type); void erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb); void *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb, diff --git a/fs/erofs/namei.c b/fs/erofs/namei.c index f0110a78acb2..c94d0c1608a8 100644 --- a/fs/erofs/namei.c +++ b/fs/erofs/namei.c @@ -99,8 +99,8 @@ static void *erofs_find_target_block(struct erofs_buf *target, struct erofs_buf buf = __EROFS_BUF_INITIALIZER; struct erofs_dirent *de; - buf.inode = dir; - de = erofs_bread(&buf, mid, EROFS_KMAP); + buf.mapping = dir->i_mapping; + de = erofs_bread(&buf, erofs_pos(dir->i_sb, mid), EROFS_KMAP); if (!IS_ERR(de)) { const int nameoff = nameoff_from_disk(de->nameoff, bsz); const int ndirents = nameoff / sizeof(*de); @@ -171,7 +171,7 @@ int erofs_namei(struct inode *dir, const struct qstr *name, erofs_nid_t *nid, qn.name = name->name; qn.end = name->name + name->len; - buf.inode = dir; + buf.mapping = dir->i_mapping; ndirents = 0; de = erofs_find_target_block(&buf, dir, &qn, &ndirents); diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 044c79229a78..348ef1660e50 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -132,11 +132,11 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, int len, i, cnt; *offset = round_up(*offset, 4); - ptr = erofs_bread(buf, erofs_blknr(sb, *offset), EROFS_KMAP); + ptr = erofs_bread(buf, *offset, EROFS_KMAP); if (IS_ERR(ptr)) return ptr; - len = le16_to_cpu(*(__le16 *)&ptr[erofs_blkoff(sb, *offset)]); + len = le16_to_cpu(*(__le16 *)ptr); if (!len) len = U16_MAX + 1; buffer = kmalloc(len, GFP_KERNEL); @@ -148,12 +148,12 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, for (i = 0; i < len; i += cnt) { cnt = min_t(int, sb->s_blocksize - erofs_blkoff(sb, *offset), len - i); - ptr = erofs_bread(buf, erofs_blknr(sb, *offset), EROFS_KMAP); + ptr = erofs_bread(buf, *offset, EROFS_KMAP); if (IS_ERR(ptr)) { kfree(buffer); return ptr; } - memcpy(buffer + i, ptr + erofs_blkoff(sb, *offset), cnt); + memcpy(buffer + i, ptr, cnt); *offset += cnt; } return buffer; diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c index b58316b49a43..a90d7d649739 100644 --- a/fs/erofs/xattr.c +++ b/fs/erofs/xattr.c @@ -81,13 +81,13 @@ static int erofs_init_inode_xattrs(struct inode *inode) it.pos = erofs_iloc(inode) + vi->inode_isize; /* read in shared xattr array (non-atomic, see kmalloc below) */ - it.kaddr = erofs_bread(&it.buf, erofs_blknr(sb, it.pos), EROFS_KMAP); + it.kaddr = erofs_bread(&it.buf, it.pos, EROFS_KMAP); if (IS_ERR(it.kaddr)) { ret = PTR_ERR(it.kaddr); goto out_unlock; } - ih = it.kaddr + erofs_blkoff(sb, it.pos); + ih = it.kaddr; vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter); vi->xattr_shared_count = ih->h_shared_count; vi->xattr_shared_xattrs = kmalloc_array(vi->xattr_shared_count, @@ -102,16 +102,14 @@ static int erofs_init_inode_xattrs(struct inode *inode) it.pos += sizeof(struct erofs_xattr_ibody_header); for (i = 0; i < vi->xattr_shared_count; ++i) { - it.kaddr = erofs_bread(&it.buf, erofs_blknr(sb, it.pos), - EROFS_KMAP); + it.kaddr = erofs_bread(&it.buf, it.pos, EROFS_KMAP); if (IS_ERR(it.kaddr)) { kfree(vi->xattr_shared_xattrs); vi->xattr_shared_xattrs = NULL; ret = PTR_ERR(it.kaddr); goto out_unlock; } - vi->xattr_shared_xattrs[i] = le32_to_cpu(*(__le32 *) - (it.kaddr + erofs_blkoff(sb, it.pos))); + vi->xattr_shared_xattrs[i] = le32_to_cpu(*(__le32 *)it.kaddr); it.pos += sizeof(__le32); } erofs_put_metabuf(&it.buf); @@ -185,12 +183,11 @@ static int erofs_xattr_copy_to_buffer(struct erofs_xattr_iter *it, void *src; for (processed = 0; processed < len; processed += slice) { - it->kaddr = erofs_bread(&it->buf, erofs_blknr(sb, it->pos), - EROFS_KMAP); + it->kaddr = erofs_bread(&it->buf, it->pos, EROFS_KMAP); if (IS_ERR(it->kaddr)) return PTR_ERR(it->kaddr); - src = it->kaddr + erofs_blkoff(sb, it->pos); + src = it->kaddr; slice = min_t(unsigned int, sb->s_blocksize - erofs_blkoff(sb, it->pos), len - processed); memcpy(it->buffer + it->buffer_ofs, src, slice); @@ -208,8 +205,7 @@ static int erofs_listxattr_foreach(struct erofs_xattr_iter *it) int err; /* 1. handle xattr entry */ - entry = *(struct erofs_xattr_entry *) - (it->kaddr + erofs_blkoff(it->sb, it->pos)); + entry = *(struct erofs_xattr_entry *)it->kaddr; it->pos += sizeof(struct erofs_xattr_entry); base_index = entry.e_name_index; @@ -259,8 +255,7 @@ static int erofs_getxattr_foreach(struct erofs_xattr_iter *it) unsigned int slice, processed, value_sz; /* 1. handle xattr entry */ - entry = *(struct erofs_xattr_entry *) - (it->kaddr + erofs_blkoff(sb, it->pos)); + entry = *(struct erofs_xattr_entry *)it->kaddr; it->pos += sizeof(struct erofs_xattr_entry); value_sz = le16_to_cpu(entry.e_value_size); @@ -291,8 +286,7 @@ static int erofs_getxattr_foreach(struct erofs_xattr_iter *it) /* 2. handle xattr name */ for (processed = 0; processed < entry.e_name_len; processed += slice) { - it->kaddr = erofs_bread(&it->buf, erofs_blknr(sb, it->pos), - EROFS_KMAP); + it->kaddr = erofs_bread(&it->buf, it->pos, EROFS_KMAP); if (IS_ERR(it->kaddr)) return PTR_ERR(it->kaddr); @@ -300,7 +294,7 @@ static int erofs_getxattr_foreach(struct erofs_xattr_iter *it) sb->s_blocksize - erofs_blkoff(sb, it->pos), entry.e_name_len - processed); if (memcmp(it->name.name + it->infix_len + processed, - it->kaddr + erofs_blkoff(sb, it->pos), slice)) + it->kaddr, slice)) return -ENOATTR; it->pos += slice; } @@ -336,13 +330,11 @@ static int erofs_xattr_iter_inline(struct erofs_xattr_iter *it, it->pos = erofs_iloc(inode) + vi->inode_isize + xattr_header_sz; while (remaining) { - it->kaddr = erofs_bread(&it->buf, erofs_blknr(it->sb, it->pos), - EROFS_KMAP); + it->kaddr = erofs_bread(&it->buf, it->pos, EROFS_KMAP); if (IS_ERR(it->kaddr)) return PTR_ERR(it->kaddr); - entry_sz = erofs_xattr_entry_size(it->kaddr + - erofs_blkoff(it->sb, it->pos)); + entry_sz = erofs_xattr_entry_size(it->kaddr); /* xattr on-disk corruption: xattr entry beyond xattr_isize */ if (remaining < entry_sz) { DBG_BUGON(1); @@ -375,8 +367,7 @@ static int erofs_xattr_iter_shared(struct erofs_xattr_iter *it, for (i = 0; i < vi->xattr_shared_count; ++i) { it->pos = erofs_pos(sb, sbi->xattr_blkaddr) + vi->xattr_shared_xattrs[i] * sizeof(__le32); - it->kaddr = erofs_bread(&it->buf, erofs_blknr(sb, it->pos), - EROFS_KMAP); + it->kaddr = erofs_bread(&it->buf, it->pos, EROFS_KMAP); if (IS_ERR(it->kaddr)) return PTR_ERR(it->kaddr); @@ -492,7 +483,7 @@ int erofs_xattr_prefixes_init(struct super_block *sb) return -ENOMEM; if (sbi->packed_inode) - buf.inode = sbi->packed_inode; + buf.mapping = sbi->packed_inode->i_mapping; else erofs_init_metabuf(&buf, sb); diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 3216b920d369..283c9c3a611d 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -936,16 +936,16 @@ static int z_erofs_read_fragment(struct super_block *sb, struct page *page, if (!packed_inode) return -EFSCORRUPTED; - buf.inode = packed_inode; + buf.mapping = packed_inode->i_mapping; for (; cur < end; cur += cnt, pos += cnt) { cnt = min_t(unsigned int, end - cur, sb->s_blocksize - erofs_blkoff(sb, pos)); - src = erofs_bread(&buf, erofs_blknr(sb, pos), EROFS_KMAP); + src = erofs_bread(&buf, pos, EROFS_KMAP); if (IS_ERR(src)) { erofs_put_metabuf(&buf); return PTR_ERR(src); } - memcpy_to_page(page, cur, src + erofs_blkoff(sb, pos), cnt); + memcpy_to_page(page, cur, src, cnt); } erofs_put_metabuf(&buf); return 0; diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 3985f8c33f95..ff4514e4626b 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -192,7 +192,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) (PAGE_SHIFT - inode->i_blkbits); if (!ra_has_index(&file->f_ra, index)) page_cache_sync_readahead( - sb->s_bdev->bd_inode->i_mapping, + sb->s_bdev->bd_mapping, &file->f_ra, file, index, 1); file->f_ra.prev_pos = (loff_t)index << PAGE_SHIFT; diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 5d8055161acd..da4a82456383 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -206,7 +206,7 @@ static void ext4_journal_abort_handle(const char *caller, unsigned int line, static void ext4_check_bdev_write_error(struct super_block *sb) { - struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; + struct address_space *mapping = sb->s_bdev->bd_mapping; struct ext4_sb_info *sbi = EXT4_SB(sb); int err; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 893ab80dafba..c682fb927b64 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -244,7 +244,7 @@ static struct buffer_head *__ext4_sb_bread_gfp(struct super_block *sb, struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block, blk_opf_t op_flags) { - gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_inode->i_mapping, + gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_mapping, ~__GFP_FS) | __GFP_MOVABLE; return __ext4_sb_bread_gfp(sb, block, op_flags, gfp); @@ -253,7 +253,7 @@ struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block, struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb, sector_t block) { - gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_inode->i_mapping, + gfp_t gfp = mapping_gfp_constraint(sb->s_bdev->bd_mapping, ~__GFP_FS); return __ext4_sb_bread_gfp(sb, block, 0, gfp); @@ -492,22 +492,6 @@ static void ext4_maybe_update_superblock(struct super_block *sb) schedule_work(&EXT4_SB(sb)->s_sb_upd_work); } -/* - * The del_gendisk() function uninitializes the disk-specific data - * structures, including the bdi structure, without telling anyone - * else. Once this happens, any attempt to call mark_buffer_dirty() - * (for example, by ext4_commit_super), will cause a kernel OOPS. - * This is a kludge to prevent these oops until we can put in a proper - * hook in del_gendisk() to inform the VFS and file system layers. - */ -static int block_device_ejected(struct super_block *sb) -{ - struct inode *bd_inode = sb->s_bdev->bd_inode; - struct backing_dev_info *bdi = inode_to_bdi(bd_inode); - - return bdi->dev == NULL; -} - static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn) { struct super_block *sb = journal->j_private; @@ -5563,7 +5547,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) * used to detect the metadata async write error. */ spin_lock_init(&sbi->s_bdev_wb_lock); - errseq_check_and_advance(&sb->s_bdev->bd_inode->i_mapping->wb_err, + errseq_check_and_advance(&sb->s_bdev->bd_mapping->wb_err, &sbi->s_bdev_wb_err); EXT4_SB(sb)->s_mount_state |= EXT4_ORPHAN_FS; ext4_orphan_cleanup(sb, es); @@ -5866,7 +5850,7 @@ static struct file *ext4_get_journal_blkdev(struct super_block *sb, sb_block = EXT4_MIN_BLOCK_SIZE / blocksize; offset = EXT4_MIN_BLOCK_SIZE % blocksize; - set_blocksize(bdev, blocksize); + set_blocksize(bdev_file, blocksize); bh = __bread(bdev, sb_block, blocksize); if (!bh) { ext4_msg(sb, KERN_ERR, "couldn't read superblock of " @@ -6164,8 +6148,6 @@ static int ext4_commit_super(struct super_block *sb) if (!sbh) return -EINVAL; - if (block_device_ejected(sb)) - return -ENODEV; ext4_update_super(sb); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 9f11fc1e79eb..4ea6c8bfb4e6 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1267,7 +1267,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, mapping = gfs2_glock2aspace(gl); if (mapping) { mapping->a_ops = &gfs2_meta_aops; - mapping->host = s->s_bdev->bd_inode; + mapping->host = s->s_bdev->bd_mapping->host; mapping->flags = 0; mapping_set_gfp_mask(mapping, GFP_NOFS); mapping->i_private_data = NULL; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 227edbaddfbc..05975ec76d35 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -114,7 +114,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) address_space_init_once(mapping); mapping->a_ops = &gfs2_rgrp_aops; - mapping->host = sb->s_bdev->bd_inode; + mapping->host = sb->s_bdev->bd_mapping->host; mapping->flags = 0; mapping_set_gfp_mask(mapping, GFP_NOFS); mapping->i_private_data = NULL; diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index b6c114c11b97..03c4b9214f56 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2009,7 +2009,7 @@ static int __jbd2_journal_erase(journal_t *journal, unsigned int flags) byte_count = (block_stop - block_start + 1) * journal->j_blocksize; - truncate_inode_pages_range(journal->j_dev->bd_inode->i_mapping, + truncate_inode_pages_range(journal->j_dev->bd_mapping, byte_start, byte_stop); if (flags & JBD2_JOURNAL_FLUSH_DISCARD) { diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 8654ab8ad534..6be7dd423fbd 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2784,7 +2784,7 @@ int nilfs_attach_log_writer(struct super_block *sb, struct nilfs_root *root) if (!nilfs->ns_writer) return -ENOMEM; - inode_attach_wb(nilfs->ns_bdev->bd_inode, NULL); + inode_attach_wb(nilfs->ns_bdev->bd_mapping->host, NULL); err = nilfs_segctor_start_thread(nilfs->ns_writer); if (unlikely(err)) diff --git a/fs/pidfs.c b/fs/pidfs.c index a63d5d24aa02..dbb9d854d1c5 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -169,6 +169,24 @@ static int pidfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EOPNOTSUPP; } + +/* + * User space expects pidfs inodes to have no file type in st_mode. + * + * In particular, 'lsof' has this legacy logic: + * + * type = s->st_mode & S_IFMT; + * switch (type) { + * ... + * case 0: + * if (!strcmp(p, "anon_inode")) + * Lf->ntype = Ntype = N_ANON_INODE; + * + * to detect our old anon_inode logic. + * + * Rather than mess with our internal sane inode data, just fix it + * up here in getattr() by masking off the format bits. + */ static int pidfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int query_flags) @@ -176,6 +194,7 @@ static int pidfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct inode *inode = d_inode(path->dentry); generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); + stat->mode &= ~S_IFMT; return 0; } @@ -199,12 +218,13 @@ static const struct super_operations pidfs_sops = { .statfs = simple_statfs, }; +/* + * 'lsof' has knowledge of out historical anon_inode use, and expects + * the pidfs dentry name to start with 'anon_inode'. + */ static char *pidfs_dname(struct dentry *dentry, char *buffer, int buflen) { - struct inode *inode = d_inode(dentry); - struct pid *pid = inode->i_private; - - return dynamic_dname(buffer, buflen, "pidfd:[%llu]", pid->ino); + return dynamic_dname(buffer, buflen, "anon_inode:[pidfd]"); } static const struct dentry_operations pidfs_dentry_operations = { diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index e539ccd39e1e..e477ee0ff35d 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -2626,8 +2626,7 @@ static int journal_init_dev(struct super_block *super, MAJOR(jdev), MINOR(jdev), result); return result; } else if (jdev != super->s_dev) - set_blocksize(file_bdev(journal->j_bdev_file), - super->s_blocksize); + set_blocksize(journal->j_bdev_file, super->s_blocksize); return 0; } @@ -2643,7 +2642,7 @@ static int journal_init_dev(struct super_block *super, return result; } - set_blocksize(file_bdev(journal->j_bdev_file), super->s_blocksize); + set_blocksize(journal->j_bdev_file, super->s_blocksize); reiserfs_info(super, "journal_init_dev: journal device: %pg\n", file_bdev(journal->j_bdev_file)); diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 8a0151e23f3d..aa4dbda7b536 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -2046,7 +2046,7 @@ xfs_setsize_buftarg( btp->bt_meta_sectorsize = sectorsize; btp->bt_meta_sectormask = sectorsize - 1; - if (set_blocksize(btp->bt_bdev, sectorsize)) { + if (set_blocksize(btp->bt_bdev_file, sectorsize)) { xfs_warn(btp->bt_mount, "Cannot set_blocksize to %u on device %pg", sectorsize, btp->bt_bdev); diff --git a/include/dt-bindings/arm/mhuv3-dt.h b/include/dt-bindings/arm/mhuv3-dt.h new file mode 100644 index 000000000000..4575406919dd --- /dev/null +++ b/include/dt-bindings/arm/mhuv3-dt.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * This header provides constants for the defined MHUv3 types. + */ + +#ifndef _DT_BINDINGS_ARM_MHUV3_DT_H +#define _DT_BINDINGS_ARM_MHUV3_DT_H + +#define DBE_EXT 0 +#define FCE_EXT 1 +#define FE_EXT 2 + +#endif /* _DT_BINDINGS_ARM_MHUV3_DT_H */ diff --git a/include/dt-bindings/phy/phy-qcom-qmp.h b/include/dt-bindings/phy/phy-qcom-qmp.h index 4edec4c5b224..6b43ea9e0051 100644 --- a/include/dt-bindings/phy/phy-qcom-qmp.h +++ b/include/dt-bindings/phy/phy-qcom-qmp.h @@ -17,4 +17,8 @@ #define QMP_USB43DP_USB3_PHY 0 #define QMP_USB43DP_DP_PHY 1 +/* QMP PCIE PHYs */ +#define QMP_PCIE_PIPE_CLK 0 +#define QMP_PCIE_PHY_AUX_CLK 1 + #endif /* _DT_BINDINGS_PHY_QMP */ diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 25dbf1097085..d88c0a009483 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -50,7 +50,7 @@ struct block_device { bool bd_write_holder; bool bd_has_submit_bio; dev_t bd_dev; - struct inode *bd_inode; /* will die */ + struct address_space *bd_mapping; /* page cache */ atomic_t bd_openers; spinlock_t bd_size_lock; /* for bd_inode->i_size updates */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index fd5951dd6b7d..c78f500c27b0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -212,11 +212,6 @@ struct gendisk { struct blk_independent_access_ranges *ia_ranges; }; -static inline bool disk_live(struct gendisk *disk) -{ - return !inode_unhashed(disk->part0->bd_inode); -} - /** * disk_openers - returns how many openers are there for a disk * @disk: disk to check @@ -1371,11 +1366,6 @@ static inline unsigned int blksize_bits(unsigned int size) return order_base_2(size >> SECTOR_SHIFT) + SECTOR_SHIFT; } -static inline unsigned int block_size(struct block_device *bdev) -{ - return 1 << bdev->bd_inode->i_blkbits; -} - int kblockd_schedule_work(struct work_struct *work); int kblockd_mod_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay); @@ -1481,7 +1471,7 @@ static inline void bio_end_io_acct(struct bio *bio, unsigned long start_time) } int bdev_read_only(struct block_device *bdev); -int set_blocksize(struct block_device *bdev, int size); +int set_blocksize(struct file *file, int size); int lookup_bdev(const char *pathname, dev_t *dev); @@ -1543,6 +1533,8 @@ void blkdev_put_no_open(struct block_device *bdev); struct block_device *I_BDEV(struct inode *inode); struct block_device *file_bdev(struct file *bdev_file); +bool disk_live(struct gendisk *disk); +unsigned int block_size(struct block_device *bdev); #ifdef CONFIG_BLOCK void invalidate_bdev(struct block_device *bdev); diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index a1c0bdd0cca6..e022e40b099e 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -364,7 +364,7 @@ static inline struct buffer_head *getblk_unmovable(struct block_device *bdev, { gfp_t gfp; - gfp = mapping_gfp_constraint(bdev->bd_inode->i_mapping, ~__GFP_FS); + gfp = mapping_gfp_constraint(bdev->bd_mapping, ~__GFP_FS); gfp |= __GFP_NOFAIL; return bdev_getblk(bdev, block, size, gfp); @@ -375,7 +375,7 @@ static inline struct buffer_head *__getblk(struct block_device *bdev, { gfp_t gfp; - gfp = mapping_gfp_constraint(bdev->bd_inode->i_mapping, ~__GFP_FS); + gfp = mapping_gfp_constraint(bdev->bd_mapping, ~__GFP_FS); gfp |= __GFP_MOVABLE | __GFP_NOFAIL; return bdev_getblk(bdev, block, size, gfp); diff --git a/include/linux/dma/imx-dma.h b/include/linux/dma/imx-dma.h index cfec5f946e23..76a8de9ae151 100644 --- a/include/linux/dma/imx-dma.h +++ b/include/linux/dma/imx-dma.h @@ -41,6 +41,7 @@ enum sdma_peripheral_type { IMX_DMATYPE_SAI, /* SAI */ IMX_DMATYPE_MULTI_SAI, /* MULTI FIFOs For Audio */ IMX_DMATYPE_HDMI, /* HDMI Audio */ + IMX_DMATYPE_I2C, /* I2C */ }; enum imx_dma_prio { diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index 5c1fe6f1fcde..36653e2ee1c9 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -25,7 +25,6 @@ enum irq_domain_bus_token { DOMAIN_BUS_PCI_DEVICE_MSIX, DOMAIN_BUS_DMAR, DOMAIN_BUS_AMDVI, - DOMAIN_BUS_PCI_DEVICE_IMS, DOMAIN_BUS_DEVICE_MSI, DOMAIN_BUS_WIRED_TO_MSI, }; diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index d8787a3c6a9e..ab04c1c27fae 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1694,7 +1694,7 @@ static inline void jbd2_journal_abort_handle(handle_t *handle) static inline void jbd2_init_fs_dev_write_error(journal_t *journal) { - struct address_space *mapping = journal->j_fs_dev->bd_inode->i_mapping; + struct address_space *mapping = journal->j_fs_dev->bd_mapping; /* * Save the original wb_err value of client fs's bdev mapping which @@ -1705,7 +1705,7 @@ static inline void jbd2_init_fs_dev_write_error(journal_t *journal) static inline int jbd2_check_fs_dev_write_error(journal_t *journal) { - struct address_space *mapping = journal->j_fs_dev->bd_inode->i_mapping; + struct address_space *mapping = journal->j_fs_dev->bd_mapping; return errseq_check(&mapping->wb_err, READ_ONCE(journal->j_fs_dev_wb_err)); diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 08b0d1d9d78b..5e51b0de4c4b 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -297,6 +297,9 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie); .wait_type_inner = _wait_type, \ .lock_type = LD_LOCK_WAIT_OVERRIDE, } +#define lock_map_assert_held(l) \ + lockdep_assert(lock_is_held(l) != LOCK_STATE_NOT_HELD) + #else /* !CONFIG_LOCKDEP */ static inline void lockdep_init_task(struct task_struct *task) @@ -388,6 +391,8 @@ extern int lockdep_is_held(const void *); #define DEFINE_WAIT_OVERRIDE_MAP(_name, _wait_type) \ struct lockdep_map __maybe_unused _name = {} +#define lock_map_assert_held(l) do { (void)(l); } while (0) + #endif /* !LOCKDEP */ #ifdef CONFIG_PROVE_LOCKING diff --git a/include/linux/msi.h b/include/linux/msi.h index 765a65581a66..dc27cf3903d5 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -573,8 +573,6 @@ enum { MSI_FLAG_MSIX_CONTIGUOUS = (1 << 19), /* PCI/MSI-X vectors can be dynamically allocated/freed post MSI-X enable */ MSI_FLAG_PCI_MSIX_ALLOC_DYN = (1 << 20), - /* Support for PCI/IMS */ - MSI_FLAG_PCI_IMS = (1 << 21), }; /** diff --git a/include/linux/msi_api.h b/include/linux/msi_api.h index 391087ad99b1..5ae72d1912c4 100644 --- a/include/linux/msi_api.h +++ b/include/linux/msi_api.h @@ -15,7 +15,6 @@ struct device; */ enum msi_domain_ids { MSI_DEFAULT_DOMAIN, - MSI_SECONDARY_DOMAIN, MSI_MAX_DEVICE_IRQDOMAINS, }; diff --git a/include/linux/omap-mailbox.h b/include/linux/omap-mailbox.h index 8aa984ec1f38..3cc5c4ed7f5a 100644 --- a/include/linux/omap-mailbox.h +++ b/include/linux/omap-mailbox.h @@ -10,17 +10,4 @@ typedef uintptr_t mbox_msg_t; #define omap_mbox_message(data) (u32)(mbox_msg_t)(data) -typedef int __bitwise omap_mbox_irq_t; -#define IRQ_TX ((__force omap_mbox_irq_t) 1) -#define IRQ_RX ((__force omap_mbox_irq_t) 2) - -struct mbox_chan; -struct mbox_client; - -struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl, - const char *chan_name); - -void omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq); -void omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq); - #endif /* OMAP_MAILBOX_H */ diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index cc2f70d061c8..acc5f96161fe 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -128,6 +128,8 @@ struct pci_epc_mem { * @group: configfs group representing the PCI EPC device * @lock: mutex to protect pci_epc ops * @function_num_map: bitmap to manage physical function number + * @init_complete: flag to indicate whether the EPC initialization is complete + * or not */ struct pci_epc { struct device dev; @@ -143,6 +145,7 @@ struct pci_epc { /* mutex to protect against concurrent access of EP controller */ struct mutex lock; unsigned long function_num_map; + bool init_complete; }; /** @@ -179,8 +182,6 @@ struct pci_epc_bar_desc { /** * struct pci_epc_features - features supported by a EPC device per function * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up - * @core_init_notifier: indicate cores that can notify about their availability - * for initialization * @msi_capable: indicate if the endpoint function has MSI capability * @msix_capable: indicate if the endpoint function has MSI-X capability * @bar: array specifying the hardware description for each BAR @@ -188,7 +189,6 @@ struct pci_epc_bar_desc { */ struct pci_epc_features { unsigned int linkup_notifier : 1; - unsigned int core_init_notifier : 1; unsigned int msi_capable : 1; unsigned int msix_capable : 1; struct pci_epc_bar_desc bar[PCI_STD_NUM_BARS]; @@ -225,6 +225,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, void pci_epc_linkup(struct pci_epc *epc); void pci_epc_linkdown(struct pci_epc *epc); void pci_epc_init_notify(struct pci_epc *epc); +void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf); void pci_epc_bme_notify(struct pci_epc *epc); void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); diff --git a/include/linux/pci.h b/include/linux/pci.h index 16493426a04f..fb004fd4e889 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -51,7 +51,7 @@ PCI_STATUS_PARITY) /* Number of reset methods used in pci_reset_fn_methods array in pci.c */ -#define PCI_NUM_RESET_METHODS 7 +#define PCI_NUM_RESET_METHODS 8 #define PCI_RESET_PROBE true #define PCI_RESET_DO_RESET false @@ -413,6 +413,8 @@ struct pci_dev { struct resource driver_exclusive_resource; /* driver exclusive resource ranges */ bool match_driver; /* Skip attaching driver */ + struct lock_class_key cfg_access_key; + struct lockdep_map cfg_access_lock; unsigned int transparent:1; /* Subtractive decode bridge */ unsigned int io_window:1; /* Bridge has I/O window */ @@ -1077,8 +1079,6 @@ enum { #define PCI_IRQ_MSIX (1 << 2) /* Allow MSI-X interrupts */ #define PCI_IRQ_AFFINITY (1 << 3) /* Auto-assign affinity */ -#define PCI_IRQ_LEGACY PCI_IRQ_INTX /* Deprecated! Use PCI_IRQ_INTX */ - /* These external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI @@ -1315,7 +1315,6 @@ int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); int __must_check pci_enable_device(struct pci_dev *dev); -int __must_check pci_enable_device_io(struct pci_dev *dev); int __must_check pci_enable_device_mem(struct pci_dev *dev); int __must_check pci_reenable_device(struct pci_dev *); int __must_check pcim_enable_device(struct pci_dev *pdev); @@ -1648,8 +1647,7 @@ int pci_set_vga_state(struct pci_dev *pdev, bool decode, */ #define PCI_IRQ_VIRTUAL (1 << 4) -#define PCI_IRQ_ALL_TYPES \ - (PCI_IRQ_LEGACY | PCI_IRQ_MSI | PCI_IRQ_MSIX) +#define PCI_IRQ_ALL_TYPES (PCI_IRQ_INTX | PCI_IRQ_MSI | PCI_IRQ_MSIX) #include <linux/dmapool.h> @@ -1658,8 +1656,6 @@ struct msix_entry { u16 entry; /* Driver uses to specify entry, OS writes */ }; -struct msi_domain_template; - #ifdef CONFIG_PCI_MSI int pci_msi_vec_count(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); @@ -1692,11 +1688,6 @@ void pci_msix_free_irq(struct pci_dev *pdev, struct msi_map map); void pci_free_irq_vectors(struct pci_dev *dev); int pci_irq_vector(struct pci_dev *dev, unsigned int nr); const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, int vec); -bool pci_create_ims_domain(struct pci_dev *pdev, const struct msi_domain_template *template, - unsigned int hwsize, void *data); -struct msi_map pci_ims_alloc_irq(struct pci_dev *pdev, union msi_instance_cookie *icookie, - const struct irq_affinity_desc *affdesc); -void pci_ims_free_irq(struct pci_dev *pdev, struct msi_map map); #else static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; } @@ -1719,7 +1710,7 @@ pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, unsigned int max_vecs, unsigned int flags, struct irq_affinity *aff_desc) { - if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1 && dev->irq) + if ((flags & PCI_IRQ_INTX) && min_vecs == 1 && dev->irq) return 1; return -ENOSPC; } @@ -1760,25 +1751,6 @@ static inline const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, { return cpu_possible_mask; } - -static inline bool pci_create_ims_domain(struct pci_dev *pdev, - const struct msi_domain_template *template, - unsigned int hwsize, void *data) -{ return false; } - -static inline struct msi_map pci_ims_alloc_irq(struct pci_dev *pdev, - union msi_instance_cookie *icookie, - const struct irq_affinity_desc *affdesc) -{ - struct msi_map map = { .index = -ENOSYS, }; - - return map; -} - -static inline void pci_ims_free_irq(struct pci_dev *pdev, struct msi_map map) -{ -} - #endif /** @@ -1821,17 +1793,21 @@ extern bool pcie_ports_native; #define pcie_ports_native false #endif -#define PCIE_LINK_STATE_L0S BIT(0) -#define PCIE_LINK_STATE_L1 BIT(1) -#define PCIE_LINK_STATE_CLKPM BIT(2) -#define PCIE_LINK_STATE_L1_1 BIT(3) -#define PCIE_LINK_STATE_L1_2 BIT(4) -#define PCIE_LINK_STATE_L1_1_PCIPM BIT(5) -#define PCIE_LINK_STATE_L1_2_PCIPM BIT(6) -#define PCIE_LINK_STATE_ALL (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |\ - PCIE_LINK_STATE_CLKPM | PCIE_LINK_STATE_L1_1 |\ - PCIE_LINK_STATE_L1_2 | PCIE_LINK_STATE_L1_1_PCIPM |\ +#define PCIE_LINK_STATE_L0S (BIT(0) | BIT(1)) /* Upstr/dwnstr L0s */ +#define PCIE_LINK_STATE_L1 BIT(2) /* L1 state */ +#define PCIE_LINK_STATE_L1_1 BIT(3) /* ASPM L1.1 state */ +#define PCIE_LINK_STATE_L1_2 BIT(4) /* ASPM L1.2 state */ +#define PCIE_LINK_STATE_L1_1_PCIPM BIT(5) /* PCI-PM L1.1 state */ +#define PCIE_LINK_STATE_L1_2_PCIPM BIT(6) /* PCI-PM L1.2 state */ +#define PCIE_LINK_STATE_ASPM_ALL (PCIE_LINK_STATE_L0S |\ + PCIE_LINK_STATE_L1 |\ + PCIE_LINK_STATE_L1_1 |\ + PCIE_LINK_STATE_L1_2 |\ + PCIE_LINK_STATE_L1_1_PCIPM |\ PCIE_LINK_STATE_L1_2_PCIPM) +#define PCIE_LINK_STATE_CLKPM BIT(7) +#define PCIE_LINK_STATE_ALL (PCIE_LINK_STATE_ASPM_ALL |\ + PCIE_LINK_STATE_CLKPM) #ifdef CONFIG_PCIEASPM int pci_disable_link_state(struct pci_dev *pdev, int state); @@ -2014,10 +1990,9 @@ static inline int pci_register_driver(struct pci_driver *drv) static inline void pci_unregister_driver(struct pci_driver *drv) { } static inline u8 pci_find_capability(struct pci_dev *dev, int cap) { return 0; } -static inline int pci_find_next_capability(struct pci_dev *dev, u8 post, - int cap) +static inline u8 pci_find_next_capability(struct pci_dev *dev, u8 post, int cap) { return 0; } -static inline int pci_find_ext_capability(struct pci_dev *dev, int cap) +static inline u16 pci_find_ext_capability(struct pci_dev *dev, int cap) { return 0; } static inline u64 pci_get_dsn(struct pci_dev *dev) @@ -2519,7 +2494,12 @@ static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev) static inline bool pci_dev_is_disconnected(const struct pci_dev *dev) { - return dev->error_state == pci_channel_io_perm_failure; + /* + * error_state is set in pci_dev_set_io_state() using xchg/cmpxchg() + * and read w/o common lock. READ_ONCE() ensures compiler cannot cache + * the value (e.g. inside the loop in pci_dev_wait()). + */ + return READ_ONCE(dev->error_state) == pci_channel_io_perm_failure; } void pci_request_acs(void); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 7a099e1282d2..942a587bb97e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2608,6 +2608,8 @@ #define PCI_VENDOR_ID_ALIBABA 0x1ded +#define PCI_VENDOR_ID_CXL 0x1e98 + #define PCI_VENDOR_ID_TEHUTI 0x1fc9 #define PCI_DEVICE_ID_TEHUTI_3009 0x3009 #define PCI_DEVICE_ID_TEHUTI_3010 0x3010 diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h index 7c2b7cc9fe6c..344ff41c22c7 100644 --- a/include/linux/remoteproc/mtk_scp.h +++ b/include/linux/remoteproc/mtk_scp.h @@ -43,6 +43,7 @@ enum scp_ipi_id { SCP_IPI_CROS_HOST_CMD, SCP_IPI_VDEC_LAT, SCP_IPI_VDEC_CORE, + SCP_IPI_IMGSYS_CMD, SCP_IPI_NS_SERVICE = 0xFF, SCP_IPI_MAX = 0x100, }; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 66f814b63a43..13e96d8b7423 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -235,6 +235,7 @@ enum sdw_clk_stop_mode { * @BRA_flow_controlled: Slave implementation results in an OK_NotReady * response * @simple_ch_prep_sm: If channel prepare sequence is required + * @ch_prep_timeout: Port-specific timeout value, in milliseconds * @imp_def_interrupts: If set, each bit corresponds to support for * implementation-defined interrupts * @@ -249,6 +250,7 @@ struct sdw_dp0_prop { u32 *words; bool BRA_flow_controlled; bool simple_ch_prep_sm; + u32 ch_prep_timeout; bool imp_def_interrupts; }; @@ -543,21 +545,6 @@ enum sdw_reg_bank { }; /** - * struct sdw_bus_conf: Bus configuration - * - * @clk_freq: Clock frequency, in Hz - * @num_rows: Number of rows in frame - * @num_cols: Number of columns in frame - * @bank: Next register bank - */ -struct sdw_bus_conf { - unsigned int clk_freq; - unsigned int num_rows; - unsigned int num_cols; - unsigned int bank; -}; - -/** * struct sdw_prepare_ch: Prepare/De-prepare Data Port channel * * @num: Port number @@ -899,6 +886,7 @@ struct sdw_master_ops { * @port_ops: Master port callback ops * @params: Current bus parameters * @prop: Master properties + * @vendor_specific_prop: pointer to non-standard properties * @m_rt_list: List of Master instance of all stream(s) running on Bus. This * is used to compute and program bus bandwidth, clock, frame shape, * transport and port parameters @@ -933,6 +921,7 @@ struct sdw_bus { const struct sdw_master_port_ops *port_ops; struct sdw_bus_params params; struct sdw_master_prop prop; + void *vendor_specific_prop; struct list_head m_rt_list; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 00bb22d96ae5..8e78417156e3 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -22,6 +22,7 @@ /* LCAP */ #define SDW_SHIM_LCAP 0x0 #define SDW_SHIM_LCAP_LCOUNT_MASK GENMASK(2, 0) +#define SDW_SHIM_LCAP_MLCS_MASK BIT(8) /* LCTL */ #define SDW_SHIM_LCTL 0x4 @@ -30,12 +31,18 @@ #define SDW_SHIM_LCTL_SPA_MASK GENMASK(3, 0) #define SDW_SHIM_LCTL_CPA BIT(8) #define SDW_SHIM_LCTL_CPA_MASK GENMASK(11, 8) +#define SDW_SHIM_LCTL_MLCS_MASK GENMASK(29, 27) +#define SDW_SHIM_MLCS_XTAL_CLK 0x0 +#define SDW_SHIM_MLCS_CARDINAL_CLK 0x1 +#define SDW_SHIM_MLCS_AUDIO_PLL_CLK 0x2 /* SYNC */ #define SDW_SHIM_SYNC 0xC -#define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1) -#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1) +#define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1) +#define SDW_SHIM_SYNC_SYNCPRD_VAL_24_576 (24576 / SDW_CADENCE_GSYNC_KHZ - 1) +#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1) +#define SDW_SHIM_SYNC_SYNCPRD_VAL_96 (96000 / SDW_CADENCE_GSYNC_KHZ - 1) #define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) #define SDW_SHIM_SYNC_SYNCCPU BIT(15) #define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16) diff --git a/include/linux/soundwire/sdw_registers.h b/include/linux/soundwire/sdw_registers.h index 138bec908c40..658b10fa5b20 100644 --- a/include/linux/soundwire/sdw_registers.h +++ b/include/linux/soundwire/sdw_registers.h @@ -13,7 +13,7 @@ #define SDW_REG_NO_PAGE 0x00008000 #define SDW_REG_OPTIONAL_PAGE 0x00010000 -#define SDW_REG_MAX 0x80000000 +#define SDW_REG_MAX 0x48000000 #define SDW_DPN_SIZE 0x100 #define SDW_BANK1_OFFSET 0x10 diff --git a/include/linux/swap.h b/include/linux/swap.h index 6b672a67ae5d..bd450023b9a4 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -310,10 +310,8 @@ struct swap_info_struct { unsigned int __percpu *cluster_next_cpu; /*percpu index for next allocation */ struct percpu_cluster __percpu *percpu_cluster; /* per cpu's swap location */ struct rb_root swap_extent_root;/* root of the swap extent rbtree */ - struct file *bdev_file; /* open handle of the bdev */ struct block_device *bdev; /* swap device or bdev of swap file */ struct file *swap_file; /* seldom referenced */ - unsigned int old_block_size; /* seldom referenced */ struct completion comp; /* seldom referenced */ spinlock_t lock; /* * protect map scan related fields like diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index a39193213ff2..94c00996e633 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1144,8 +1144,14 @@ #define PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH 0x0003ffff #define PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX 0x000000ff +#define PCI_DOE_DATA_OBJECT_DISC_REQ_3_VER 0x0000ff00 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID 0x0000ffff #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 +/* Compute Express Link (CXL r3.1, sec 8.1.5) */ +#define PCI_DVSEC_CXL_PORT 3 +#define PCI_DVSEC_CXL_PORT_CTL 0x0c +#define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001 + #endif /* LINUX_PCI_REGS_H */ diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 5bc04bfe2db1..d9abb7ab031d 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -368,11 +368,7 @@ static int swsusp_swap_check(void) if (IS_ERR(hib_resume_bdev_file)) return PTR_ERR(hib_resume_bdev_file); - res = set_blocksize(file_bdev(hib_resume_bdev_file), PAGE_SIZE); - if (res < 0) - fput(hib_resume_bdev_file); - - return res; + return 0; } /** @@ -1574,7 +1570,6 @@ int swsusp_check(bool exclusive) hib_resume_bdev_file = bdev_file_open_by_dev(swsusp_resume_device, BLK_OPEN_READ, holder, NULL); if (!IS_ERR(hib_resume_bdev_file)) { - set_blocksize(file_bdev(hib_resume_bdev_file), PAGE_SIZE); clear_page(swsusp_header); error = hib_submit_io(REQ_OP_READ, swsusp_resume_block, swsusp_header, NULL); diff --git a/mm/swapfile.c b/mm/swapfile.c index f6ca215fb92f..b3e5e384e330 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2469,7 +2469,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) struct inode *inode; struct filename *pathname; int err, found = 0; - unsigned int old_block_size; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -2582,7 +2581,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) } swap_file = p->swap_file; - old_block_size = p->old_block_size; p->swap_file = NULL; p->max = 0; swap_map = p->swap_map; @@ -2605,11 +2603,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) exit_swap_address_space(p->type); inode = mapping->host; - if (p->bdev_file) { - set_blocksize(p->bdev, old_block_size); - fput(p->bdev_file); - p->bdev_file = NULL; - } inode_lock(inode); inode->i_flags &= ~S_SWAPFILE; @@ -2835,21 +2828,8 @@ static struct swap_info_struct *alloc_swap_info(void) static int claim_swapfile(struct swap_info_struct *p, struct inode *inode) { - int error; - if (S_ISBLK(inode->i_mode)) { - p->bdev_file = bdev_file_open_by_dev(inode->i_rdev, - BLK_OPEN_READ | BLK_OPEN_WRITE, p, NULL); - if (IS_ERR(p->bdev_file)) { - error = PTR_ERR(p->bdev_file); - p->bdev_file = NULL; - return error; - } - p->bdev = file_bdev(p->bdev_file); - p->old_block_size = block_size(p->bdev); - error = set_blocksize(p->bdev, PAGE_SIZE); - if (error < 0) - return error; + p->bdev = I_BDEV(inode); /* * Zoned block devices contain zones that have a sequential * write only restriction. Hence zoned block devices are not @@ -3090,7 +3070,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) name = NULL; goto bad_swap; } - swap_file = file_open_name(name, O_RDWR|O_LARGEFILE, 0); + swap_file = file_open_name(name, O_RDWR | O_LARGEFILE | O_EXCL, 0); if (IS_ERR(swap_file)) { error = PTR_ERR(swap_file); swap_file = NULL; @@ -3289,11 +3269,6 @@ bad_swap: p->percpu_cluster = NULL; free_percpu(p->cluster_next_cpu); p->cluster_next_cpu = NULL; - if (p->bdev_file) { - set_blocksize(p->bdev, p->old_block_size); - fput(p->bdev_file); - p->bdev_file = NULL; - } inode = NULL; destroy_swap_extents(p); swap_cgroup_swapoff(p->type); diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index dfeec06301ce..8b7dd73d94c1 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -38,6 +38,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, u8 *end_work = scratch + SCRATCH_SIZE; u8 *priv, *pub; u16 priv_len, pub_len; + int ret; priv_len = get_unaligned_be16(src) + 2; priv = src; @@ -57,8 +58,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, unsigned char bool[3], *w = bool; /* tag 0 is emptyAuth */ w = asn1_encode_boolean(w, w + sizeof(bool), true); - if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) - return PTR_ERR(w); + if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) { + ret = PTR_ERR(w); + goto err; + } work = asn1_encode_tag(work, end_work, 0, bool, w - bool); } @@ -69,8 +72,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, * trigger, so if it does there's something nefarious going on */ if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, - "BUG: scratch buffer is too small")) - return -EINVAL; + "BUG: scratch buffer is too small")) { + ret = -EINVAL; + goto err; + } work = asn1_encode_integer(work, end_work, options->keyhandle); work = asn1_encode_octet_string(work, end_work, pub, pub_len); @@ -79,10 +84,18 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, work1 = payload->blob; work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob), scratch, work - scratch); - if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) - return PTR_ERR(work1); + if (IS_ERR(work1)) { + ret = PTR_ERR(work1); + pr_err("BUG: ASN.1 encoder failed with %d\n", ret); + goto err; + } + kfree(scratch); return work1 - payload->blob; + +err: + kfree(scratch); + return ret; } struct tpm2_key_context { diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index d9816d0701a8..f2dc82a2abc7 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -339,7 +339,7 @@ static int avs_hdac_acquire_irq(struct avs_dev *adev) int ret; /* request one and check that we only got one interrupt */ - ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_INTX); if (ret != 1) { dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret); return ret; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 5c4b8f30a275..e6a38de0a0aa 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -246,12 +246,12 @@ static int hda_sdw_exit(struct snd_sof_dev *sdev) hdev = sdev->pdata->hw_pdata; - hda_sdw_int_enable(sdev, false); - if (hdev->sdw) sdw_intel_exit(hdev->sdw); hdev->sdw = NULL; + hda_sdw_int_enable(sdev, false); + return 0; } |