diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-11 15:34:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-11 15:34:05 -0700 |
commit | 97ff4ca46d3279134cec49752de8c5a62dc68460 (patch) | |
tree | 3b562d5890b2fc358ca55a935395cc7938155406 | |
parent | 4832a4dada1a2baefac76b70e4f3a78e71a7c35c (diff) | |
parent | 2f4281f4dce12440727ab770683cfb83eab62a26 (diff) |
Merge tag 'char-misc-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc driver updates from Greg KH:
"Here is the "large" pull request for char and misc and other assorted
smaller driver subsystems for 5.3-rc1.
It seems that this tree is becoming the funnel point of lots of
smaller driver subsystems, which is fine for me, but that's why it is
getting larger over time and does not just contain stuff under
drivers/char/ and drivers/misc.
Lots of small updates all over the place here from different driver
subsystems:
- habana driver updates
- coresight driver updates
- documentation file movements and updates
- Android binder fixes and updates
- extcon driver updates
- google firmware driver updates
- fsi driver updates
- smaller misc and char driver updates
- soundwire driver updates
- nvmem driver updates
- w1 driver fixes
All of these have been in linux-next for a while with no reported
issues"
* tag 'char-misc-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (188 commits)
coresight: Do not default to CPU0 for missing CPU phandle
dt-bindings: coresight: Change CPU phandle to required property
ocxl: Allow contexts to be attached with a NULL mm
fsi: sbefifo: Don't fail operations when in SBE IPL state
coresight: tmc: Smatch: Fix potential NULL pointer dereference
coresight: etm3x: Smatch: Fix potential NULL pointer dereference
coresight: Potential uninitialized variable in probe()
coresight: etb10: Do not call smp_processor_id from preemptible
coresight: tmc-etf: Do not call smp_processor_id from preemptible
coresight: tmc-etr: alloc_perf_buf: Do not call smp_processor_id from preemptible
coresight: tmc-etr: Do not call smp_processor_id() from preemptible
docs: misc-devices: convert files without extension to ReST
fpga: dfl: fme: align PR buffer size per PR datawidth
fpga: dfl: fme: remove copy_to_user() in ioctl for PR
fpga: dfl-fme-mgr: fix FME_PR_INTFC_ID register address.
intel_th: msu: Start read iterator from a non-empty window
intel_th: msu: Split sgt array and pointer in multiwindow mode
intel_th: msu: Support multipage blocks
intel_th: pci: Add Ice Lake NNPI support
intel_th: msu: Fix single mode with disabled IOMMU
...
164 files changed, 6073 insertions, 2998 deletions
diff --git a/Documentation/ABI/testing/debugfs-driver-habanalabs b/Documentation/ABI/testing/debugfs-driver-habanalabs index 2f5b80be07a3..f0ac14b70ecb 100644 --- a/Documentation/ABI/testing/debugfs-driver-habanalabs +++ b/Documentation/ABI/testing/debugfs-driver-habanalabs @@ -3,7 +3,10 @@ Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com Description: Sets the device address to be used for read or write through - PCI bar. The acceptable value is a string that starts with "0x" + PCI bar, or the device VA of a host mapped memory to be read or + written directly from the host. The latter option is allowed + only when the IOMMU is disabled. + The acceptable value is a string that starts with "0x" What: /sys/kernel/debug/habanalabs/hl<n>/command_buffers Date: Jan 2019 @@ -33,10 +36,12 @@ Contact: oded.gabbay@gmail.com Description: Allows the root user to read or write directly through the device's PCI bar. Writing to this file generates a write transaction while reading from the file generates a read - transcation. This custom interface is needed (instead of using + transaction. This custom interface is needed (instead of using the generic Linux user-space PCI mapping) because the DDR bar is very small compared to the DDR memory and only the driver can - move the bar before and after the transaction + move the bar before and after the transaction. + If the IOMMU is disabled, it also allows the root user to read + or write from the host a device VA of a host mapped memory What: /sys/kernel/debug/habanalabs/hl<n>/device Date: Jan 2019 @@ -46,6 +51,13 @@ Description: Enables the root user to set the device to specific state. Valid values are "disable", "enable", "suspend", "resume". User can read this property to see the valid values +What: /sys/kernel/debug/habanalabs/hl<n>/engines +Date: Jul 2019 +KernelVersion: 5.3 +Contact: oded.gabbay@gmail.com +Description: Displays the status registers values of the device engines and + their derived idle status + What: /sys/kernel/debug/habanalabs/hl<n>/i2c_addr Date: Jan 2019 KernelVersion: 5.1 diff --git a/Documentation/ABI/testing/sysfs-driver-habanalabs b/Documentation/ABI/testing/sysfs-driver-habanalabs index 78b2bcf316a3..f433fc6db3c6 100644 --- a/Documentation/ABI/testing/sysfs-driver-habanalabs +++ b/Documentation/ABI/testing/sysfs-driver-habanalabs @@ -62,18 +62,20 @@ What: /sys/class/habanalabs/hl<n>/ic_clk Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Allows the user to set the maximum clock frequency of the - Interconnect fabric. Writes to this parameter affect the device - only when the power management profile is set to "manual" mode. - The device IC clock might be set to lower value then the +Description: Allows the user to set the maximum clock frequency, in Hz, of + the Interconnect fabric. Writes to this parameter affect the + device only when the power management profile is set to "manual" + mode. The device IC clock might be set to lower value than the maximum. The user should read the ic_clk_curr to see the actual - frequency value of the IC + frequency value of the IC. This property is valid only for the + Goya ASIC family What: /sys/class/habanalabs/hl<n>/ic_clk_curr Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Displays the current clock frequency of the Interconnect fabric +Description: Displays the current clock frequency, in Hz, of the Interconnect + fabric. This property is valid only for the Goya ASIC family What: /sys/class/habanalabs/hl<n>/infineon_ver Date: Jan 2019 @@ -92,18 +94,20 @@ What: /sys/class/habanalabs/hl<n>/mme_clk Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Allows the user to set the maximum clock frequency of the - MME compute engine. Writes to this parameter affect the device - only when the power management profile is set to "manual" mode. - The device MME clock might be set to lower value then the +Description: Allows the user to set the maximum clock frequency, in Hz, of + the MME compute engine. Writes to this parameter affect the + device only when the power management profile is set to "manual" + mode. The device MME clock might be set to lower value than the maximum. The user should read the mme_clk_curr to see the actual - frequency value of the MME + frequency value of the MME. This property is valid only for the + Goya ASIC family What: /sys/class/habanalabs/hl<n>/mme_clk_curr Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Displays the current clock frequency of the MME compute engine +Description: Displays the current clock frequency, in Hz, of the MME compute + engine. This property is valid only for the Goya ASIC family What: /sys/class/habanalabs/hl<n>/pci_addr Date: Jan 2019 @@ -163,18 +167,20 @@ What: /sys/class/habanalabs/hl<n>/tpc_clk Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Allows the user to set the maximum clock frequency of the - TPC compute engines. Writes to this parameter affect the device - only when the power management profile is set to "manual" mode. - The device TPC clock might be set to lower value then the +Description: Allows the user to set the maximum clock frequency, in Hz, of + the TPC compute engines. Writes to this parameter affect the + device only when the power management profile is set to "manual" + mode. The device TPC clock might be set to lower value than the maximum. The user should read the tpc_clk_curr to see the actual - frequency value of the TPC + frequency value of the TPC. This property is valid only for + Goya ASIC family What: /sys/class/habanalabs/hl<n>/tpc_clk_curr Date: Jan 2019 KernelVersion: 5.1 Contact: oded.gabbay@gmail.com -Description: Displays the current clock frequency of the TPC compute engines +Description: Displays the current clock frequency, in Hz, of the TPC compute + engines. This property is valid only for the Goya ASIC family What: /sys/class/habanalabs/hl<n>/uboot_ver Date: Jan 2019 diff --git a/Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt b/Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt index 298291211ea4..f1de3247c1b7 100644 --- a/Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt +++ b/Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt @@ -26,8 +26,8 @@ Required properties: processor core is clocked by the internal CPU clock, so it is enabled with CPU clock by default. -- cpu : the CPU phandle the debug module is affined to. When omitted - the module is considered to belong to CPU0. +- cpu : the CPU phandle the debug module is affined to. Do not assume it + to default to CPU0 if omitted. Optional properties: diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 8a88ddebc1a2..fcc3bacfd8bc 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -59,6 +59,11 @@ its hardware characteristcs. * port or ports: see "Graph bindings for Coresight" below. +* Additional required property for Embedded Trace Macrocell (version 3.x and + version 4.x): + * cpu: the cpu phandle this ETM/PTM is affined to. Do not + assume it to default to CPU0 if omitted. + * Additional required properties for System Trace Macrocells (STM): * reg: along with the physical base address and length of the register set as described above, another entry is required to describe the @@ -87,9 +92,6 @@ its hardware characteristcs. * arm,cp14: must be present if the system accesses ETM/PTM management registers via co-processor 14. - * cpu: the cpu phandle this ETM/PTM is affined to. When omitted the - source is considered to belong to CPU0. - * Optional property for TMC: * arm,buffer-size: size of contiguous buffer space for TMC ETR diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt index 5d7dbabbb784..f378922906f6 100644 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt @@ -133,6 +133,18 @@ RTC bindings based on SCU Message Protocol Required properties: - compatible: should be "fsl,imx8qxp-sc-rtc"; +OCOTP bindings based on SCU Message Protocol +------------------------------------------------------------ +Required properties: +- compatible: Should be "fsl,imx8qxp-scu-ocotp" +- #address-cells: Must be 1. Contains byte index +- #size-cells: Must be 1. Contains byte length + +Optional Child nodes: + +- Data cells of ocotp: + Detailed bindings are described in bindings/nvmem/nvmem.txt + Example (imx8qxp): ------------- aliases { @@ -177,6 +189,16 @@ firmware { ... }; + ocotp: imx8qx-ocotp { + compatible = "fsl,imx8qxp-scu-ocotp"; + #address-cells = <1>; + #size-cells = <1>; + + fec_mac0: mac@2c4 { + reg = <0x2c4 8>; + }; + }; + pd: imx8qx-pd { compatible = "fsl,imx8qxp-scu-pd", "fsl,scu-pd"; #power-domain-cells = <1>; diff --git a/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt b/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt new file mode 100644 index 000000000000..d592c21245f2 --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt @@ -0,0 +1,19 @@ +FAIRCHILD SEMICONDUCTOR FSA9480 MICROUSB SWITCH + +The FSA9480 is a USB port accessory detector and switch. The FSA9480 is fully +controlled using I2C and enables USB data, stereo and mono audio, video, +microphone, and UART data to use a common connector port. + +Required properties: + - compatible : Must be "fcs,fsa9480" + - reg : Specifies i2c slave address. Must be 0x25. + - interrupts : Should contain one entry specifying interrupt signal of + interrupt parent to which interrupt pin of the chip is connected. + + Example: + musb@25 { + compatible = "fcs,fsa9480"; + reg = <0x25>; + interrupt-parent = <&gph2>; + interrupts = <7 0>; + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt index f936b5589b19..59b8dcc118ee 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt @@ -5,6 +5,7 @@ controller in Ingenic JZ4780 Required properties: - compatible: Should be set to one of: + "ingenic,jz4740-nemc" (JZ4740) "ingenic,jz4780-nemc" (JZ4780) - reg: Should specify the NEMC controller registers location and length. - clocks: Clock for the NEMC controller. diff --git a/Documentation/devicetree/bindings/misc/xlnx,sd-fec.txt b/Documentation/devicetree/bindings/misc/xlnx,sd-fec.txt new file mode 100644 index 000000000000..e3289634fa30 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/xlnx,sd-fec.txt @@ -0,0 +1,58 @@ +* Xilinx SDFEC(16nm) IP * + +The Soft Decision Forward Error Correction (SDFEC) Engine is a Hard IP block +which provides high-throughput LDPC and Turbo Code implementations. +The LDPC decode & encode functionality is capable of covering a range of +customer specified Quasi-cyclic (QC) codes. The Turbo decode functionality +principally covers codes used by LTE. The FEC Engine offers significant +power and area savings versus implementations done in the FPGA fabric. + + +Required properties: +- compatible: Must be "xlnx,sd-fec-1.1" +- clock-names : List of input clock names from the following: + - "core_clk", Main processing clock for processing core (required) + - "s_axi_aclk", AXI4-Lite memory-mapped slave interface clock (required) + - "s_axis_din_aclk", DIN AXI4-Stream Slave interface clock (optional) + - "s_axis_din_words-aclk", DIN_WORDS AXI4-Stream Slave interface clock (optional) + - "s_axis_ctrl_aclk", Control input AXI4-Stream Slave interface clock (optional) + - "m_axis_dout_aclk", DOUT AXI4-Stream Master interface clock (optional) + - "m_axis_dout_words_aclk", DOUT_WORDS AXI4-Stream Master interface clock (optional) + - "m_axis_status_aclk", Status output AXI4-Stream Master interface clock (optional) +- clocks : Clock phandles (see clock_bindings.txt for details). +- reg: Should contain Xilinx SDFEC 16nm Hardened IP block registers + location and length. +- xlnx,sdfec-code : Should contain "ldpc" or "turbo" to describe the codes + being used. +- xlnx,sdfec-din-words : A value 0 indicates that the DIN_WORDS interface is + driven with a fixed value and is not present on the device, a value of 1 + configures the DIN_WORDS to be block based, while a value of 2 configures the + DIN_WORDS input to be supplied for each AXI transaction. +- xlnx,sdfec-din-width : Configures the DIN AXI stream where a value of 1 + configures a width of "1x128b", 2 a width of "2x128b" and 4 configures a width + of "4x128b". +- xlnx,sdfec-dout-words : A value 0 indicates that the DOUT_WORDS interface is + driven with a fixed value and is not present on the device, a value of 1 + configures the DOUT_WORDS to be block based, while a value of 2 configures the + DOUT_WORDS input to be supplied for each AXI transaction. +- xlnx,sdfec-dout-width : Configures the DOUT AXI stream where a value of 1 + configures a width of "1x128b", 2 a width of "2x128b" and 4 configures a width + of "4x128b". +Optional properties: +- interrupts: should contain SDFEC interrupt number + +Example +--------------------------------------- + sd_fec_0: sd-fec@a0040000 { + compatible = "xlnx,sd-fec-1.1"; + clock-names = "core_clk","s_axi_aclk","s_axis_ctrl_aclk","s_axis_din_aclk","m_axis_status_aclk","m_axis_dout_aclk"; + clocks = <&misc_clk_2>,<&misc_clk_0>,<&misc_clk_1>,<&misc_clk_1>,<&misc_clk_1>, <&misc_clk_1>; + reg = <0x0 0xa0040000 0x0 0x40000>; + interrupt-parent = <&axi_intc>; + interrupts = <1 0>; + xlnx,sdfec-code = "ldpc"; + xlnx,sdfec-din-words = <0>; + xlnx,sdfec-din-width = <2>; + xlnx,sdfec-dout-words = <0>; + xlnx,sdfec-dout-width = <1>; + }; diff --git a/Documentation/devicetree/bindings/mux/mmio-mux.txt b/Documentation/devicetree/bindings/mux/mmio-mux.txt deleted file mode 100644 index a9bfb4d8b6ac..000000000000 --- a/Documentation/devicetree/bindings/mux/mmio-mux.txt +++ /dev/null @@ -1,60 +0,0 @@ -MMIO register bitfield-based multiplexer controller bindings - -Define register bitfields to be used to control multiplexers. The parent -device tree node must be a syscon node to provide register access. - -Required properties: -- compatible : "mmio-mux" -- #mux-control-cells : <1> -- mux-reg-masks : an array of register offset and pre-shifted bitfield mask - pairs, each describing a single mux control. -* Standard mux-controller bindings as decribed in mux-controller.txt - -Optional properties: -- idle-states : if present, the state the muxes will have when idle. The - special state MUX_IDLE_AS_IS is the default. - -The multiplexer state of each multiplexer is defined as the value of the -bitfield described by the corresponding register offset and bitfield mask pair -in the mux-reg-masks array, accessed through the parent syscon. - -Example: - - syscon { - compatible = "syscon"; - - mux: mux-controller { - compatible = "mmio-mux"; - #mux-control-cells = <1>; - - mux-reg-masks = <0x3 0x30>, /* 0: reg 0x3, bits 5:4 */ - <0x3 0x40>, /* 1: reg 0x3, bit 6 */ - idle-states = <MUX_IDLE_AS_IS>, <0>; - }; - }; - - video-mux { - compatible = "video-mux"; - mux-controls = <&mux 0>; - - ports { - /* inputs 0..3 */ - port@0 { - reg = <0>; - }; - port@1 { - reg = <1>; - }; - port@2 { - reg = <2>; - }; - port@3 { - reg = <3>; - }; - - /* output */ - port@4 { - reg = <4>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/mux/reg-mux.txt b/Documentation/devicetree/bindings/mux/reg-mux.txt new file mode 100644 index 000000000000..4afd7ba73d60 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/reg-mux.txt @@ -0,0 +1,129 @@ +Generic register bitfield-based multiplexer controller bindings + +Define register bitfields to be used to control multiplexers. The parent +device tree node must be a device node to provide register r/w access. + +Required properties: +- compatible : should be one of + "reg-mux" : if parent device of mux controller is not syscon device + "mmio-mux" : if parent device of mux controller is syscon device +- #mux-control-cells : <1> +- mux-reg-masks : an array of register offset and pre-shifted bitfield mask + pairs, each describing a single mux control. +* Standard mux-controller bindings as decribed in mux-controller.txt + +Optional properties: +- idle-states : if present, the state the muxes will have when idle. The + special state MUX_IDLE_AS_IS is the default. + +The multiplexer state of each multiplexer is defined as the value of the +bitfield described by the corresponding register offset and bitfield mask +pair in the mux-reg-masks array. + +Example 1: +The parent device of mux controller is not a syscon device. + +&i2c0 { + fpga@66 { // fpga connected to i2c + compatible = "fsl,lx2160aqds-fpga", "fsl,fpga-qixis-i2c", + "simple-mfd"; + reg = <0x66>; + + mux: mux-controller { + compatible = "reg-mux"; + #mux-control-cells = <1>; + mux-reg-masks = <0x54 0xf8>, /* 0: reg 0x54, bits 7:3 */ + <0x54 0x07>; /* 1: reg 0x54, bits 2:0 */ + }; + }; +}; + +mdio-mux-1 { + compatible = "mdio-mux-multiplexer"; + mux-controls = <&mux 0>; + mdio-parent-bus = <&emdio1>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + mdio@8 { + reg = <0x8>; + #address-cells = <1>; + #size-cells = <0>; + }; + + .. + .. +}; + +mdio-mux-2 { + compatible = "mdio-mux-multiplexer"; + mux-controls = <&mux 1>; + mdio-parent-bus = <&emdio2>; + #address-cells = <1>; + #size-cells = <0>; + + mdio@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + mdio@1 { + reg = <0x1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + .. + .. +}; + +Example 2: +The parent device of mux controller is syscon device. + +syscon { + compatible = "syscon"; + + mux: mux-controller { + compatible = "mmio-mux"; + #mux-control-cells = <1>; + + mux-reg-masks = <0x3 0x30>, /* 0: reg 0x3, bits 5:4 */ + <0x3 0x40>, /* 1: reg 0x3, bit 6 */ + idle-states = <MUX_IDLE_AS_IS>, <0>; + }; +}; + +video-mux { + compatible = "video-mux"; + mux-controls = <&mux 0>; + #address-cells = <1>; + #size-cells = <0>; + + ports { + /* inputs 0..3 */ + port@0 { + reg = <0>; + }; + port@1 { + reg = <1>; + }; + port@2 { + reg = <2>; + }; + port@3 { + reg = <3>; + }; + + /* output */ + port@4 { + reg = <4>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml new file mode 100644 index 000000000000..c9efd6e2c134 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/allwinner,sun4i-a10-sid.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A10 Security ID Device Tree Bindings + +maintainers: + - Chen-Yu Tsai <wens@csie.org> + - Maxime Ripard <maxime.ripard@bootlin.com> + +allOf: + - $ref: "nvmem.yaml#" + +properties: + compatible: + enum: + - allwinner,sun4i-a10-sid + - allwinner,sun7i-a20-sid + - allwinner,sun8i-a83t-sid + - allwinner,sun8i-h3-sid + - allwinner,sun50i-a64-sid + - allwinner,sun50i-h5-sid + - allwinner,sun50i-h6-sid + + reg: + maxItems: 1 + +required: + - compatible + - reg + +# FIXME: We should set it, but it would report all the generic +# properties as additional properties. +# additionalProperties: false + +examples: + - | + sid@1c23800 { + compatible = "allwinner,sun4i-a10-sid"; + reg = <0x01c23800 0x10>; + }; + + - | + sid@1c23800 { + compatible = "allwinner,sun7i-a20-sid"; + reg = <0x01c23800 0x200>; + }; + +... diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt deleted file mode 100644 index cfb18b4ef8f7..000000000000 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt +++ /dev/null @@ -1,29 +0,0 @@ -Allwinner sunxi-sid - -Required properties: -- compatible: Should be one of the following: - "allwinner,sun4i-a10-sid" - "allwinner,sun7i-a20-sid" - "allwinner,sun8i-a83t-sid" - "allwinner,sun8i-h3-sid" - "allwinner,sun50i-a64-sid" - "allwinner,sun50i-h5-sid" - "allwinner,sun50i-h6-sid" - -- reg: Should contain registers location and length - -= Data cells = -Are child nodes of sunxi-sid, bindings of which as described in -bindings/nvmem/nvmem.txt - -Example for sun4i: - sid@1c23800 { - compatible = "allwinner,sun4i-a10-sid"; - reg = <0x01c23800 0x10> - }; - -Example for sun7i: - sid@1c23800 { - compatible = "allwinner,sun7i-a20-sid"; - reg = <0x01c23800 0x200> - }; diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt index 68f7d6fdd140..96ffd06d2ca8 100644 --- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt +++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt @@ -15,6 +15,7 @@ Required properties: "fsl,imx6sll-ocotp" (i.MX6SLL), "fsl,imx7ulp-ocotp" (i.MX7ULP), "fsl,imx8mq-ocotp" (i.MX8MQ), + "fsl,imx8mm-ocotp" (i.MX8MM), followed by "syscon". - #address-cells : Should be 1 - #size-cells : Should be 1 diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index d26308af6036..0dbaa987aa11 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -42,6 +42,7 @@ available subsections can be seen below. target mtdnand miscellaneous + mei/index w1 rapidio s390-drivers diff --git a/Documentation/driver-api/mei/hdcp.rst b/Documentation/driver-api/mei/hdcp.rst new file mode 100644 index 000000000000..e85a065b1cdc --- /dev/null +++ b/Documentation/driver-api/mei/hdcp.rst @@ -0,0 +1,32 @@ +.. SPDX-License-Identifier: GPL-2.0 + +HDCP: +===== + +ME FW as a security engine provides the capability for setting up +HDCP2.2 protocol negotiation between the Intel graphics device and +an HDC2.2 sink. + +ME FW prepares HDCP2.2 negotiation parameters, signs and encrypts them +according the HDCP 2.2 spec. The Intel graphics sends the created blob +to the HDCP2.2 sink. + +Similarly, the HDCP2.2 sink's response is transferred to ME FW +for decryption and verification. + +Once all the steps of HDCP2.2 negotiation are completed, +upon request ME FW will configure the port as authenticated and supply +the HDCP encryption keys to Intel graphics hardware. + + +mei_hdcp driver +--------------- +.. kernel-doc:: drivers/misc/mei/hdcp/mei_hdcp.c + :doc: MEI_HDCP Client Driver + +mei_hdcp api +------------ + +.. kernel-doc:: drivers/misc/mei/hdcp/mei_hdcp.c + :functions: + diff --git a/Documentation/driver-api/mei/iamt.rst b/Documentation/driver-api/mei/iamt.rst new file mode 100644 index 000000000000..6ef3e613684b --- /dev/null +++ b/Documentation/driver-api/mei/iamt.rst @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Intel(R) Active Management Technology (Intel AMT) +================================================= + +Prominent usage of the Intel ME Interface is to communicate with Intel(R) +Active Management Technology (Intel AMT) implemented in firmware running on +the Intel ME. + +Intel AMT provides the ability to manage a host remotely out-of-band (OOB) +even when the operating system running on the host processor has crashed or +is in a sleep state. + +Some examples of Intel AMT usage are: + - Monitoring hardware state and platform components + - Remote power off/on (useful for green computing or overnight IT + maintenance) + - OS updates + - Storage of useful platform information such as software assets + - Built-in hardware KVM + - Selective network isolation of Ethernet and IP protocol flows based + on policies set by a remote management console + - IDE device redirection from remote management console + +Intel AMT (OOB) communication is based on SOAP (deprecated +starting with Release 6.0) over HTTP/S or WS-Management protocol over +HTTP/S that are received from a remote management console application. + +For more information about Intel AMT: +https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm + + +Intel AMT Applications +---------------------- + + 1) Intel Local Management Service (Intel LMS) + + Applications running locally on the platform communicate with Intel AMT Release + 2.0 and later releases in the same way that network applications do via SOAP + over HTTP (deprecated starting with Release 6.0) or with WS-Management over + SOAP over HTTP. This means that some Intel AMT features can be accessed from a + local application using the same network interface as a remote application + communicating with Intel AMT over the network. + + When a local application sends a message addressed to the local Intel AMT host + name, the Intel LMS, which listens for traffic directed to the host name, + intercepts the message and routes it to the Intel MEI. + For more information: + https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm + Under "About Intel AMT" => "Local Access" + + For downloading Intel LMS: + https://github.com/intel/lms + + The Intel LMS opens a connection using the Intel MEI driver to the Intel LMS + firmware feature using a defined GUID and then communicates with the feature + using a protocol called Intel AMT Port Forwarding Protocol (Intel APF protocol). + The protocol is used to maintain multiple sessions with Intel AMT from a + single application. + + See the protocol specification in the Intel AMT Software Development Kit (SDK) + https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm + Under "SDK Resources" => "Intel(R) vPro(TM) Gateway (MPS)" + => "Information for Intel(R) vPro(TM) Gateway Developers" + => "Description of the Intel AMT Port Forwarding (APF) Protocol" + + 2) Intel AMT Remote configuration using a Local Agent + + A Local Agent enables IT personnel to configure Intel AMT out-of-the-box + without requiring installing additional data to enable setup. The remote + configuration process may involve an ISV-developed remote configuration + agent that runs on the host. + For more information: + https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm + Under "Setup and Configuration of Intel AMT" => + "SDK Tools Supporting Setup and Configuration" => + "Using the Local Agent Sample" + +Intel AMT OS Health Watchdog +---------------------------- + +The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog. +Whenever the OS hangs or crashes, Intel AMT will send an event +to any subscriber to this event. This mechanism means that +IT knows when a platform crashes even when there is a hard failure on the host. + +The Intel AMT Watchdog is composed of two parts: + 1) Firmware feature - receives the heartbeats + and sends an event when the heartbeats stop. + 2) Intel MEI iAMT watchdog driver - connects to the watchdog feature, + configures the watchdog and sends the heartbeats. + +The Intel iAMT watchdog MEI driver uses the kernel watchdog API to configure +the Intel AMT Watchdog and to send heartbeats to it. The default timeout of the +watchdog is 120 seconds. + +If the Intel AMT is not enabled in the firmware then the watchdog client won't enumerate +on the me client bus and watchdog devices won't be exposed. + +--- +linux-mei@linux.intel.com diff --git a/Documentation/driver-api/mei/index.rst b/Documentation/driver-api/mei/index.rst new file mode 100644 index 000000000000..3a22b522ee78 --- /dev/null +++ b/Documentation/driver-api/mei/index.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: <isonum.txt> + +=================================================== +Intel(R) Management Engine Interface (Intel(R) MEI) +=================================================== + +**Copyright** |copy| 2019 Intel Corporation + + +.. only:: html + + .. class:: toc-title + + Table of Contents + +.. toctree:: + :maxdepth: 3 + + mei + mei-client-bus + iamt diff --git a/Documentation/driver-api/mei/mei-client-bus.rst b/Documentation/driver-api/mei/mei-client-bus.rst new file mode 100644 index 000000000000..f242b3f8d6aa --- /dev/null +++ b/Documentation/driver-api/mei/mei-client-bus.rst @@ -0,0 +1,168 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================== +Intel(R) Management Engine (ME) Client bus API +============================================== + + +Rationale +========= + +The MEI character device is useful for dedicated applications to send and receive +data to the many FW appliance found in Intel's ME from the user space. +However, for some of the ME functionalities it makes sense to leverage existing software +stack and expose them through existing kernel subsystems. + +In order to plug seamlessly into the kernel device driver model we add kernel virtual +bus abstraction on top of the MEI driver. This allows implementing Linux kernel drivers +for the various MEI features as a stand alone entities found in their respective subsystem. +Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to +the existing code. + + +MEI CL bus API +============== + +A driver implementation for an MEI Client is very similar to any other existing bus +based device drivers. The driver registers itself as an MEI CL bus driver through +the ``struct mei_cl_driver`` structure defined in :file:`include/linux/mei_cl_bus.c` + +.. code-block:: C + + struct mei_cl_driver { + struct device_driver driver; + const char *name; + + const struct mei_cl_device_id *id_table; + + int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id); + int (*remove)(struct mei_cl_device *dev); + }; + + + +The mei_cl_device_id structure defined in :file:`include/linux/mod_devicetable.h` allows a +driver to bind itself against a device name. + +.. code-block:: C + + struct mei_cl_device_id { + char name[MEI_CL_NAME_SIZE]; + uuid_le uuid; + __u8 version; + kernel_ulong_t driver_info; + }; + +To actually register a driver on the ME Client bus one must call the :c:func:`mei_cl_add_driver` +API. This is typically called at module initialization time. + +Once the driver is registered and bound to the device, a driver will typically +try to do some I/O on this bus and this should be done through the :c:func:`mei_cl_send` +and :c:func:`mei_cl_recv` functions. More detailed information is in :ref:`api` section. + +In order for a driver to be notified about pending traffic or event, the driver +should register a callback via :c:func:`mei_cl_devev_register_rx_cb` and +:c:func:`mei_cldev_register_notify_cb` function respectively. + +.. _api: + +API: +---- +.. kernel-doc:: drivers/misc/mei/bus.c + :export: drivers/misc/mei/bus.c + + + +Example +======= + +As a theoretical example let's pretend the ME comes with a "contact" NFC IP. +The driver init and exit routines for this device would look like: + +.. code-block:: C + + #define CONTACT_DRIVER_NAME "contact" + + static struct mei_cl_device_id contact_mei_cl_tbl[] = { + { CONTACT_DRIVER_NAME, }, + + /* required last entry */ + { } + }; + MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl); + + static struct mei_cl_driver contact_driver = { + .id_table = contact_mei_tbl, + .name = CONTACT_DRIVER_NAME, + + .probe = contact_probe, + .remove = contact_remove, + }; + + static int contact_init(void) + { + int r; + + r = mei_cl_driver_register(&contact_driver); + if (r) { + pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; + } + + static void __exit contact_exit(void) + { + mei_cl_driver_unregister(&contact_driver); + } + + module_init(contact_init); + module_exit(contact_exit); + +And the driver's simplified probe routine would look like that: + +.. code-block:: C + + int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id) + { + [...] + mei_cldev_enable(dev); + + mei_cldev_register_rx_cb(dev, contact_rx_cb); + + return 0; + } + +In the probe routine the driver first enable the MEI device and then registers +an rx handler which is as close as it can get to registering a threaded IRQ handler. +The handler implementation will typically call :c:func:`mei_cldev_recv` and then +process received data. + +.. code-block:: C + + #define MAX_PAYLOAD 128 + #define HDR_SIZE 4 + static void conntact_rx_cb(struct mei_cl_device *cldev) + { + struct contact *c = mei_cldev_get_drvdata(cldev); + unsigned char payload[MAX_PAYLOAD]; + ssize_t payload_sz; + + payload_sz = mei_cldev_recv(cldev, payload, MAX_PAYLOAD) + if (reply_size < HDR_SIZE) { + return; + } + + c->process_rx(payload); + + } + +MEI Client Bus Drivers +====================== + +.. toctree:: + :maxdepth: 2 + + hdcp + nfc diff --git a/Documentation/driver-api/mei/mei.rst b/Documentation/driver-api/mei/mei.rst new file mode 100644 index 000000000000..c800d8e5f422 --- /dev/null +++ b/Documentation/driver-api/mei/mei.rst @@ -0,0 +1,176 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Introduction +============ + +The Intel Management Engine (Intel ME) is an isolated and protected computing +resource (Co-processor) residing inside certain Intel chipsets. The Intel ME +provides support for computer/IT management and security features. +The actual feature set depends on the Intel chipset SKU. + +The Intel Management Engine Interface (Intel MEI, previously known as HECI) +is the interface between the Host and Intel ME. This interface is exposed +to the host as a PCI device, actually multiple PCI devices might be exposed. +The Intel MEI Driver is in charge of the communication channel between +a host application and the Intel ME features. + +Each Intel ME feature, or Intel ME Client is addressed by a unique GUID and +each client has its own protocol. The protocol is message-based with a +header and payload up to maximal number of bytes advertised by the client, +upon connection. + +Intel MEI Driver +================ + +The driver exposes a character device with device nodes /dev/meiX. + +An application maintains communication with an Intel ME feature while +/dev/meiX is open. The binding to a specific feature is performed by calling +:c:macro:`MEI_CONNECT_CLIENT_IOCTL`, which passes the desired GUID. +The number of instances of an Intel ME feature that can be opened +at the same time depends on the Intel ME feature, but most of the +features allow only a single instance. + +The driver is transparent to data that are passed between firmware feature +and host application. + +Because some of the Intel ME features can change the system +configuration, the driver by default allows only a privileged +user to access it. + +The session is terminated calling :c:func:`close(int fd)`. + +A code snippet for an application communicating with Intel AMTHI client: + +.. code-block:: C + + struct mei_connect_client_data data; + fd = open(MEI_DEVICE); + + data.d.in_client_uuid = AMTHI_GUID; + + ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &data); + + printf("Ver=%d, MaxLen=%ld\n", + data.d.in_client_uuid.protocol_version, + data.d.in_client_uuid.max_msg_length); + + [...] + + write(fd, amthi_req_data, amthi_req_data_len); + + [...] + + read(fd, &amthi_res_data, amthi_res_data_len); + + [...] + close(fd); + + +User space API + +IOCTLs: +======= + +The Intel MEI Driver supports the following IOCTL commands: + +IOCTL_MEI_CONNECT_CLIENT +------------------------- +Connect to firmware Feature/Client. + +.. code-block:: none + + Usage: + + struct mei_connect_client_data client_data; + + ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &client_data); + + Inputs: + + struct mei_connect_client_data - contain the following + Input field: + + in_client_uuid - GUID of the FW Feature that needs + to connect to. + Outputs: + out_client_properties - Client Properties: MTU and Protocol Version. + + Error returns: + + ENOTTY No such client (i.e. wrong GUID) or connection is not allowed. + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EBUSY Connection Already Open + +:Note: + max_msg_length (MTU) in client properties describes the maximum + data that can be sent or received. (e.g. if MTU=2K, can send + requests up to bytes 2k and received responses up to 2k bytes). + + +IOCTL_MEI_NOTIFY_SET +--------------------- +Enable or disable event notifications. + + +.. code-block:: none + + Usage: + + uint32_t enable; + + ioctl(fd, IOCTL_MEI_NOTIFY_SET, &enable); + + + uint32_t enable = 1; + or + uint32_t enable[disable] = 0; + + Error returns: + + + EINVAL Wrong IOCTL Number + ENODEV Device is not initialized or the client not connected + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EOPNOTSUPP if the device doesn't support the feature + +:Note: + The client must be connected in order to enable notification events + + +IOCTL_MEI_NOTIFY_GET +-------------------- +Retrieve event + +.. code-block:: none + + Usage: + uint32_t event; + ioctl(fd, IOCTL_MEI_NOTIFY_GET, &event); + + Outputs: + 1 - if an event is pending + 0 - if there is no even pending + + Error returns: + EINVAL Wrong IOCTL Number + ENODEV Device is not initialized or the client not connected + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EOPNOTSUPP if the device doesn't support the feature + +:Note: + The client must be connected and event notification has to be enabled + in order to receive an event + + + +Supported Chipsets +================== +82X38/X48 Express and newer + +linux-mei@linux.intel.com diff --git a/Documentation/driver-api/mei/nfc.rst b/Documentation/driver-api/mei/nfc.rst new file mode 100644 index 000000000000..b5b6fc96f85e --- /dev/null +++ b/Documentation/driver-api/mei/nfc.rst @@ -0,0 +1,28 @@ +.. SPDX-License-Identifier: GPL-2.0 + +MEI NFC +------- + +Some Intel 8 and 9 Serieses chipsets supports NFC devices connected behind +the Intel Management Engine controller. +MEI client bus exposes the NFC chips as NFC phy devices and enables +binding with Microread and NXP PN544 NFC device driver from the Linux NFC +subsystem. + +.. kernel-render:: DOT + :alt: MEI NFC digraph + :caption: **MEI NFC** Stack + + digraph NFC { + cl_nfc -> me_cl_nfc; + "drivers/nfc/mei_phy" -> cl_nfc [lhead=bus]; + "drivers/nfc/microread/mei" -> cl_nfc; + "drivers/nfc/microread/mei" -> "drivers/nfc/mei_phy"; + "drivers/nfc/pn544/mei" -> cl_nfc; + "drivers/nfc/pn544/mei" -> "drivers/nfc/mei_phy"; + "net/nfc" -> "drivers/nfc/microread/mei"; + "net/nfc" -> "drivers/nfc/pn544/mei"; + "neard" -> "net/nfc"; + cl_nfc [label="mei/bus(nfc)"]; + me_cl_nfc [label="me fw (nfc)"]; + } diff --git a/Documentation/driver-api/soundwire/locking.rst b/Documentation/driver-api/soundwire/locking.rst index 253f73555255..3a7ffb3d87f3 100644 --- a/Documentation/driver-api/soundwire/locking.rst +++ b/Documentation/driver-api/soundwire/locking.rst @@ -44,7 +44,9 @@ Message transfer. b. Transfer message (Read/Write) to Slave1 or broadcast message on Bus in case of bank switch. - c. Release Message lock :: + c. Release Message lock + + :: +----------+ +---------+ | | | | diff --git a/Documentation/misc-devices/eeprom b/Documentation/misc-devices/eeprom.rst index ba692011f221..008249675ccc 100644 --- a/Documentation/misc-devices/eeprom +++ b/Documentation/misc-devices/eeprom.rst @@ -1,11 +1,17 @@ +==================== Kernel driver eeprom ==================== Supported chips: + * Any EEPROM chip in the designated address range + Prefix: 'eeprom' + Addresses scanned: I2C 0x50 - 0x57 + Datasheets: Publicly available from: + Atmel (www.atmel.com), Catalyst (www.catsemi.com), Fairchild (www.fairchildsemi.com), @@ -16,7 +22,9 @@ Supported chips: Xicor (www.xicor.com), and others. - Chip Size (bits) Address + ========= ============= ============================================ + Chip Size (bits) Address + ========= ============= ============================================ 24C01 1K 0x50 (shadows at 0x51 - 0x57) 24C01A 1K 0x50 - 0x57 (Typical device on DIMMs) 24C02 2K 0x50 - 0x57 @@ -24,7 +32,7 @@ Supported chips: (additional data at 0x51, 0x53, 0x55, 0x57) 24C08 8K 0x50, 0x54 (additional data at 0x51, 0x52, 0x53, 0x55, 0x56, 0x57) - 24C16 16K 0x50 (additional data at 0x51 - 0x57) + 24C16 16K 0x50 (additional data at 0x51 - 0x57) Sony 2K 0x57 Atmel 34C02B 2K 0x50 - 0x57, SW write protect at 0x30-37 @@ -33,14 +41,15 @@ Supported chips: Fairchild 34W02 2K 0x50 - 0x57, SW write protect at 0x30-37 Microchip 24AA52 2K 0x50 - 0x57, SW write protect at 0x30-37 ST M34C02 2K 0x50 - 0x57, SW write protect at 0x30-37 + ========= ============= ============================================ Authors: - Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, - Jean Delvare <jdelvare@suse.de>, - Greg Kroah-Hartman <greg@kroah.com>, - IBM Corp. + - Frodo Looijaard <frodol@dds.nl>, + - Philip Edelbrock <phil@netroedge.com>, + - Jean Delvare <jdelvare@suse.de>, + - Greg Kroah-Hartman <greg@kroah.com>, + - IBM Corp. Description ----------- @@ -74,23 +83,25 @@ this address will write protect the memory array permanently, and the device will no longer respond at the 0x30-37 address. The eeprom driver does not support this register. -Lacking functionality: +Lacking functionality +--------------------- * Full support for larger devices (24C04, 24C08, 24C16). These are not -typically found on a PC. These devices will appear as separate devices at -multiple addresses. + typically found on a PC. These devices will appear as separate devices at + multiple addresses. * Support for really large devices (24C32, 24C64, 24C128, 24C256, 24C512). -These devices require two-byte address fields and are not supported. + These devices require two-byte address fields and are not supported. * Enable Writing. Again, no technical reason why not, but making it easy -to change the contents of the EEPROMs (on DIMMs anyway) also makes it easy -to disable the DIMMs (potentially preventing the computer from booting) -until the values are restored somehow. + to change the contents of the EEPROMs (on DIMMs anyway) also makes it easy + to disable the DIMMs (potentially preventing the computer from booting) + until the values are restored somehow. -Use: +Use +--- After inserting the module (and any other required SMBus/i2c modules), you -should have some EEPROM directories in /sys/bus/i2c/devices/* of names such +should have some EEPROM directories in ``/sys/bus/i2c/devices/*`` of names such as "0-0050". Inside each of these is a series of files, the eeprom file contains the binary data from EEPROM. diff --git a/Documentation/misc-devices/ics932s401 b/Documentation/misc-devices/ics932s401.rst index bdac67ff6e3f..613ee54a9c21 100644 --- a/Documentation/misc-devices/ics932s401 +++ b/Documentation/misc-devices/ics932s401.rst @@ -1,10 +1,15 @@ +======================== Kernel driver ics932s401 -====================== +======================== Supported chips: + * IDT ICS932S401 + Prefix: 'ics932s401' + Addresses scanned: I2C 0x69 + Datasheet: Publicly available at the IDT website Author: Darrick J. Wong diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst index dfd1f45a3127..a57f92dfe49a 100644 --- a/Documentation/misc-devices/index.rst +++ b/Documentation/misc-devices/index.rst @@ -14,4 +14,9 @@ fit into other categories. .. toctree:: :maxdepth: 2 + eeprom ibmvmc + ics932s401 + isl29003 + lis3lv02d + max6875 diff --git a/Documentation/misc-devices/isl29003 b/Documentation/misc-devices/isl29003.rst index 80b952fd32ff..0cc38aed6c00 100644 --- a/Documentation/misc-devices/isl29003 +++ b/Documentation/misc-devices/isl29003.rst @@ -1,10 +1,15 @@ +====================== Kernel driver isl29003 -===================== +====================== Supported chips: + * Intersil ISL29003 + Prefix: 'isl29003' + Addresses scanned: none + Datasheet: http://www.intersil.com/data/fn/fn7464.pdf @@ -37,25 +42,33 @@ Sysfs entries ------------- range: + == =========================== 0: 0 lux to 1000 lux (default) 1: 0 lux to 4000 lux 2: 0 lux to 16,000 lux 3: 0 lux to 64,000 lux + == =========================== resolution: + == ===================== 0: 2^16 cycles (default) 1: 2^12 cycles 2: 2^8 cycles 3: 2^4 cycles + == ===================== mode: + == ================================================= 0: diode1's current (unsigned 16bit) (default) 1: diode1's current (unsigned 16bit) 2: difference between diodes (l1 - l2, signed 15bit) + == ================================================= power_state: + == ================================================= 0: device is disabled (default) 1: device is enabled + == ================================================= lux (read only): returns the value from the last sensor reading diff --git a/Documentation/misc-devices/lis3lv02d b/Documentation/misc-devices/lis3lv02d.rst index f89960a0ff95..959bd2b822cf 100644 --- a/Documentation/misc-devices/lis3lv02d +++ b/Documentation/misc-devices/lis3lv02d.rst @@ -1,3 +1,4 @@ +======================= Kernel driver lis3lv02d ======================= @@ -8,8 +9,8 @@ Supported chips: LIS331DLH (16 bits) Authors: - Yan Burman <burman.yan@gmail.com> - Eric Piel <eric.piel@tremplin-utc.net> + - Yan Burman <burman.yan@gmail.com> + - Eric Piel <eric.piel@tremplin-utc.net> Description @@ -25,11 +26,15 @@ neverball). The accelerometer data is readable via to mg values (1/1000th of earth gravity). Sysfs attributes under /sys/devices/platform/lis3lv02d/: -position - 3D position that the accelerometer reports. Format: "(x,y,z)" -rate - read reports the sampling rate of the accelerometer device in HZ. + +position + - 3D position that the accelerometer reports. Format: "(x,y,z)" +rate + - read reports the sampling rate of the accelerometer device in HZ. write changes sampling rate of the accelerometer device. Only values which are supported by HW are accepted. -selftest - performs selftest for the chip as specified by chip manufacturer. +selftest + - performs selftest for the chip as specified by chip manufacturer. This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick. Joystick device can be @@ -69,11 +74,12 @@ Axes orientation For better compatibility between the various laptops. The values reported by the accelerometer are converted into a "standard" organisation of the axes (aka "can play neverball out of the box"): + * When the laptop is horizontal the position reported is about 0 for X and Y - and a positive value for Z + and a positive value for Z * If the left side is elevated, X increases (becomes positive) * If the front side (where the touchpad is) is elevated, Y decreases - (becomes negative) + (becomes negative) * If the laptop is put upside-down, Z becomes negative If your laptop model is not recognized (cf "dmesg"), you can send an diff --git a/Documentation/misc-devices/max6875 b/Documentation/misc-devices/max6875.rst index 2f2bd0b17b5d..ad419ac22a5b 100644 --- a/Documentation/misc-devices/max6875 +++ b/Documentation/misc-devices/max6875.rst @@ -1,12 +1,16 @@ +===================== Kernel driver max6875 ===================== Supported chips: + * Maxim MAX6874, MAX6875 + Prefix: 'max6875' + Addresses scanned: None (see below) - Datasheet: - http://pdfserv.maxim-ic.com/en/ds/MAX6874-MAX6875.pdf + + Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6874-MAX6875.pdf Author: Ben Gardner <bgardner@wabtec.com> @@ -24,9 +28,13 @@ registers. The Maxim MAX6874 is a similar, mostly compatible device, with more inputs and outputs: - vin gpi vout + +=========== === === ==== +- vin gpi vout +=========== === === ==== MAX6874 6 4 8 MAX6875 4 3 5 +=========== === === ==== See the datasheet for more information. @@ -41,13 +49,16 @@ General Remarks --------------- Valid addresses for the MAX6875 are 0x50 and 0x52. + Valid addresses for the MAX6874 are 0x50, 0x52, 0x54 and 0x56. + The driver does not probe any address, so you explicitly instantiate the devices. -Example: -$ modprobe max6875 -$ echo max6875 0x50 > /sys/bus/i2c/devices/i2c-0/new_device +Example:: + + $ modprobe max6875 + $ echo max6875 0x50 > /sys/bus/i2c/devices/i2c-0/new_device The MAX6874/MAX6875 ignores address bit 0, so this driver attaches to multiple addresses. For example, for address 0x50, it also reserves 0x51. @@ -58,52 +69,67 @@ Programming the chip using i2c-dev ---------------------------------- Use the i2c-dev interface to access and program the chips. + Reads and writes are performed differently depending on the address range. The configuration registers are at addresses 0x00 - 0x45. + Use i2c_smbus_write_byte_data() to write a register and i2c_smbus_read_byte_data() to read a register. + The command is the register number. Examples: -To write a 1 to register 0x45: + +To write a 1 to register 0x45:: + i2c_smbus_write_byte_data(fd, 0x45, 1); -To read register 0x45: +To read register 0x45:: + value = i2c_smbus_read_byte_data(fd, 0x45); The configuration EEPROM is at addresses 0x8000 - 0x8045. + The user EEPROM is at addresses 0x8100 - 0x82ff. Use i2c_smbus_write_word_data() to write a byte to EEPROM. The command is the upper byte of the address: 0x80, 0x81, or 0x82. -The data word is the lower part of the address or'd with data << 8. +The data word is the lower part of the address or'd with data << 8:: + cmd = address >> 8; val = (address & 0xff) | (data << 8); Example: -To write 0x5a to address 0x8003: + +To write 0x5a to address 0x8003:: + i2c_smbus_write_word_data(fd, 0x80, 0x5a03); Reading data from the EEPROM is a little more complicated. + Use i2c_smbus_write_byte_data() to set the read address and then i2c_smbus_read_byte() or i2c_smbus_read_i2c_block_data() to read the data. Example: -To read data starting at offset 0x8100, first set the address: + +To read data starting at offset 0x8100, first set the address:: + i2c_smbus_write_byte_data(fd, 0x81, 0x00); -And then read the data +And then read the data:: + value = i2c_smbus_read_byte(fd); - or +or:: count = i2c_smbus_read_i2c_block_data(fd, 0x84, 16, buffer); The block read should read 16 bytes. + 0x84 is the block read command. See the datasheet for more details. diff --git a/Documentation/misc-devices/mei/mei-client-bus.txt b/Documentation/misc-devices/mei/mei-client-bus.txt deleted file mode 100644 index 743be4ec8989..000000000000 --- a/Documentation/misc-devices/mei/mei-client-bus.txt +++ /dev/null @@ -1,141 +0,0 @@ -Intel(R) Management Engine (ME) Client bus API -============================================== - - -Rationale -========= - -MEI misc character device is useful for dedicated applications to send and receive -data to the many FW appliance found in Intel's ME from the user space. -However for some of the ME functionalities it make sense to leverage existing software -stack and expose them through existing kernel subsystems. - -In order to plug seamlessly into the kernel device driver model we add kernel virtual -bus abstraction on top of the MEI driver. This allows implementing linux kernel drivers -for the various MEI features as a stand alone entities found in their respective subsystem. -Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to -the existing code. - - -MEI CL bus API -============== - -A driver implementation for an MEI Client is very similar to existing bus -based device drivers. The driver registers itself as an MEI CL bus driver through -the mei_cl_driver structure: - -struct mei_cl_driver { - struct device_driver driver; - const char *name; - - const struct mei_cl_device_id *id_table; - - int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id); - int (*remove)(struct mei_cl_device *dev); -}; - -struct mei_cl_id { - char name[MEI_NAME_SIZE]; - kernel_ulong_t driver_info; -}; - -The mei_cl_id structure allows the driver to bind itself against a device name. - -To actually register a driver on the ME Client bus one must call the mei_cl_add_driver() -API. This is typically called at module init time. - -Once registered on the ME Client bus, a driver will typically try to do some I/O on -this bus and this should be done through the mei_cl_send() and mei_cl_recv() -routines. The latter is synchronous (blocks and sleeps until data shows up). -In order for drivers to be notified of pending events waiting for them (e.g. -an Rx event) they can register an event handler through the -mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event -will trigger an event handler call and the driver implementation is supposed -to call mei_recv() from the event handler in order to fetch the pending -received buffers. - - -Example -======= - -As a theoretical example let's pretend the ME comes with a "contact" NFC IP. -The driver init and exit routines for this device would look like: - -#define CONTACT_DRIVER_NAME "contact" - -static struct mei_cl_device_id contact_mei_cl_tbl[] = { - { CONTACT_DRIVER_NAME, }, - - /* required last entry */ - { } -}; -MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl); - -static struct mei_cl_driver contact_driver = { - .id_table = contact_mei_tbl, - .name = CONTACT_DRIVER_NAME, - - .probe = contact_probe, - .remove = contact_remove, -}; - -static int contact_init(void) -{ - int r; - - r = mei_cl_driver_register(&contact_driver); - if (r) { - pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit contact_exit(void) -{ - mei_cl_driver_unregister(&contact_driver); -} - -module_init(contact_init); -module_exit(contact_exit); - -And the driver's simplified probe routine would look like that: - -int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id) -{ - struct contact_driver *contact; - - [...] - mei_cl_enable_device(dev); - - mei_cl_register_event_cb(dev, contact_event_cb, contact); - - return 0; -} - -In the probe routine the driver first enable the MEI device and then registers -an ME bus event handler which is as close as it can get to registering a -threaded IRQ handler. -The handler implementation will typically call some I/O routine depending on -the pending events: - -#define MAX_NFC_PAYLOAD 128 - -static void contact_event_cb(struct mei_cl_device *dev, u32 events, - void *context) -{ - struct contact_driver *contact = context; - - if (events & BIT(MEI_EVENT_RX)) { - u8 payload[MAX_NFC_PAYLOAD]; - int payload_size; - - payload_size = mei_recv(dev, payload, MAX_NFC_PAYLOAD); - if (payload_size <= 0) - return; - - /* Hook to the NFC subsystem */ - nfc_hci_recv_frame(contact->hdev, payload, payload_size); - } -} diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt deleted file mode 100644 index 2b80a0cd621f..000000000000 --- a/Documentation/misc-devices/mei/mei.txt +++ /dev/null @@ -1,266 +0,0 @@ -Intel(R) Management Engine Interface (Intel(R) MEI) -=================================================== - -Introduction -============ - -The Intel Management Engine (Intel ME) is an isolated and protected computing -resource (Co-processor) residing inside certain Intel chipsets. The Intel ME -provides support for computer/IT management features. The feature set -depends on the Intel chipset SKU. - -The Intel Management Engine Interface (Intel MEI, previously known as HECI) -is the interface between the Host and Intel ME. This interface is exposed -to the host as a PCI device. The Intel MEI Driver is in charge of the -communication channel between a host application and the Intel ME feature. - -Each Intel ME feature (Intel ME Client) is addressed by a GUID/UUID and -each client has its own protocol. The protocol is message-based with a -header and payload up to 512 bytes. - -Prominent usage of the Intel ME Interface is to communicate with Intel(R) -Active Management Technology (Intel AMT) implemented in firmware running on -the Intel ME. - -Intel AMT provides the ability to manage a host remotely out-of-band (OOB) -even when the operating system running on the host processor has crashed or -is in a sleep state. - -Some examples of Intel AMT usage are: - - Monitoring hardware state and platform components - - Remote power off/on (useful for green computing or overnight IT - maintenance) - - OS updates - - Storage of useful platform information such as software assets - - Built-in hardware KVM - - Selective network isolation of Ethernet and IP protocol flows based - on policies set by a remote management console - - IDE device redirection from remote management console - -Intel AMT (OOB) communication is based on SOAP (deprecated -starting with Release 6.0) over HTTP/S or WS-Management protocol over -HTTP/S that are received from a remote management console application. - -For more information about Intel AMT: -http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide - - -Intel MEI Driver -================ - -The driver exposes a misc device called /dev/mei. - -An application maintains communication with an Intel ME feature while -/dev/mei is open. The binding to a specific feature is performed by calling -MEI_CONNECT_CLIENT_IOCTL, which passes the desired UUID. -The number of instances of an Intel ME feature that can be opened -at the same time depends on the Intel ME feature, but most of the -features allow only a single instance. - -The Intel AMT Host Interface (Intel AMTHI) feature supports multiple -simultaneous user connected applications. The Intel MEI driver -handles this internally by maintaining request queues for the applications. - -The driver is transparent to data that are passed between firmware feature -and host application. - -Because some of the Intel ME features can change the system -configuration, the driver by default allows only a privileged -user to access it. - -A code snippet for an application communicating with Intel AMTHI client: - - struct mei_connect_client_data data; - fd = open(MEI_DEVICE); - - data.d.in_client_uuid = AMTHI_UUID; - - ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &data); - - printf("Ver=%d, MaxLen=%ld\n", - data.d.in_client_uuid.protocol_version, - data.d.in_client_uuid.max_msg_length); - - [...] - - write(fd, amthi_req_data, amthi_req_data_len); - - [...] - - read(fd, &amthi_res_data, amthi_res_data_len); - - [...] - close(fd); - - -IOCTL -===== - -The Intel MEI Driver supports the following IOCTL commands: - IOCTL_MEI_CONNECT_CLIENT Connect to firmware Feature (client). - - usage: - struct mei_connect_client_data clientData; - ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &clientData); - - inputs: - mei_connect_client_data struct contain the following - input field: - - in_client_uuid - UUID of the FW Feature that needs - to connect to. - outputs: - out_client_properties - Client Properties: MTU and Protocol Version. - - error returns: - EINVAL Wrong IOCTL Number - ENODEV Device or Connection is not initialized or ready. - (e.g. Wrong UUID) - ENOMEM Unable to allocate memory to client internal data. - EFAULT Fatal Error (e.g. Unable to access user input data) - EBUSY Connection Already Open - - Notes: - max_msg_length (MTU) in client properties describes the maximum - data that can be sent or received. (e.g. if MTU=2K, can send - requests up to bytes 2k and received responses up to 2k bytes). - - IOCTL_MEI_NOTIFY_SET: enable or disable event notifications - - Usage: - uint32_t enable; - ioctl(fd, IOCTL_MEI_NOTIFY_SET, &enable); - - Inputs: - uint32_t enable = 1; - or - uint32_t enable[disable] = 0; - - Error returns: - EINVAL Wrong IOCTL Number - ENODEV Device is not initialized or the client not connected - ENOMEM Unable to allocate memory to client internal data. - EFAULT Fatal Error (e.g. Unable to access user input data) - EOPNOTSUPP if the device doesn't support the feature - - Notes: - The client must be connected in order to enable notification events - - - IOCTL_MEI_NOTIFY_GET : retrieve event - - Usage: - uint32_t event; - ioctl(fd, IOCTL_MEI_NOTIFY_GET, &event); - - Outputs: - 1 - if an event is pending - 0 - if there is no even pending - - Error returns: - EINVAL Wrong IOCTL Number - ENODEV Device is not initialized or the client not connected - ENOMEM Unable to allocate memory to client internal data. - EFAULT Fatal Error (e.g. Unable to access user input data) - EOPNOTSUPP if the device doesn't support the feature - - Notes: - The client must be connected and event notification has to be enabled - in order to receive an event - - -Intel ME Applications -===================== - - 1) Intel Local Management Service (Intel LMS) - - Applications running locally on the platform communicate with Intel AMT Release - 2.0 and later releases in the same way that network applications do via SOAP - over HTTP (deprecated starting with Release 6.0) or with WS-Management over - SOAP over HTTP. This means that some Intel AMT features can be accessed from a - local application using the same network interface as a remote application - communicating with Intel AMT over the network. - - When a local application sends a message addressed to the local Intel AMT host - name, the Intel LMS, which listens for traffic directed to the host name, - intercepts the message and routes it to the Intel MEI. - For more information: - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide - Under "About Intel AMT" => "Local Access" - - For downloading Intel LMS: - http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers/ - - The Intel LMS opens a connection using the Intel MEI driver to the Intel LMS - firmware feature using a defined UUID and then communicates with the feature - using a protocol called Intel AMT Port Forwarding Protocol (Intel APF protocol). - The protocol is used to maintain multiple sessions with Intel AMT from a - single application. - - See the protocol specification in the Intel AMT Software Development Kit (SDK) - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide - Under "SDK Resources" => "Intel(R) vPro(TM) Gateway (MPS)" - => "Information for Intel(R) vPro(TM) Gateway Developers" - => "Description of the Intel AMT Port Forwarding (APF) Protocol" - - 2) Intel AMT Remote configuration using a Local Agent - - A Local Agent enables IT personnel to configure Intel AMT out-of-the-box - without requiring installing additional data to enable setup. The remote - configuration process may involve an ISV-developed remote configuration - agent that runs on the host. - For more information: - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide - Under "Setup and Configuration of Intel AMT" => - "SDK Tools Supporting Setup and Configuration" => - "Using the Local Agent Sample" - - An open source Intel AMT configuration utility, implementing a local agent - that accesses the Intel MEI driver, can be found here: - http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers/ - - -Intel AMT OS Health Watchdog -============================ - -The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog. -Whenever the OS hangs or crashes, Intel AMT will send an event -to any subscriber to this event. This mechanism means that -IT knows when a platform crashes even when there is a hard failure on the host. - -The Intel AMT Watchdog is composed of two parts: - 1) Firmware feature - receives the heartbeats - and sends an event when the heartbeats stop. - 2) Intel MEI iAMT watchdog driver - connects to the watchdog feature, - configures the watchdog and sends the heartbeats. - -The Intel iAMT watchdog MEI driver uses the kernel watchdog API to configure -the Intel AMT Watchdog and to send heartbeats to it. The default timeout of the -watchdog is 120 seconds. - -If the Intel AMT is not enabled in the firmware then the watchdog client won't enumerate -on the me client bus and watchdog devices won't be exposed. - - -Supported Chipsets -================== - -7 Series Chipset Family -6 Series Chipset Family -5 Series Chipset Family -4 Series Chipset Family -Mobile 4 Series Chipset Family -ICH9 -82946GZ/GL -82G35 Express -82Q963/Q965 -82P965/G965 -Mobile PM965/GM965 -Mobile GME965/GLE960 -82Q35 Express -82G33/G31/P35/P31 Express -82Q33 Express -82X38/X48 Express - ---- -linux-mei@linux.intel.com diff --git a/MAINTAINERS b/MAINTAINERS index 93cfaad87002..9b4a3e7296ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6537,6 +6537,19 @@ F: fs/crypto/ F: include/linux/fscrypt*.h F: Documentation/filesystems/fscrypt.rst +FSI SUBSYSTEM +M: Jeremy Kerr <jk@ozlabs.org> +M: Joel Stanley <joel@jms.id.au> +R: Alistar Popple <alistair@popple.id.au> +R: Eddie James <eajames@linux.ibm.com> +L: linux-fsi@lists.ozlabs.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi.git +Q: http://patchwork.ozlabs.org/project/linux-fsi/list/ +S: Supported +F: drivers/fsi/ +F: include/linux/fsi*.h +F: include/trace/events/fsi*.h + FSI-ATTACHED I2C DRIVER M: Eddie James <eajames@linux.ibm.com> L: linux-i2c@vger.kernel.org @@ -8103,7 +8116,7 @@ F: include/uapi/linux/mei.h F: include/linux/mei_cl_bus.h F: drivers/misc/mei/* F: drivers/watchdog/mei_wdt.c -F: Documentation/misc-devices/mei/* +F: Documentation/driver-api/mei/* F: samples/mei/* INTEL MENLOW THERMAL DRIVER @@ -8936,7 +8949,7 @@ F: include/linux/leds.h LEGACY EEPROM DRIVER M: Jean Delvare <jdelvare@suse.com> S: Maintained -F: Documentation/misc-devices/eeprom +F: Documentation/misc-devices/eeprom.rst F: drivers/misc/eeprom/eeprom.c LEGO MINDSTORMS EV3 @@ -9222,7 +9235,7 @@ F: Documentation/memory-barriers.txt LIS3LV02D ACCELEROMETER DRIVER M: Eric Piel <eric.piel@tremplin-utc.net> S: Maintained -F: Documentation/misc-devices/lis3lv02d +F: Documentation/misc-devices/lis3lv02d.rst F: drivers/misc/lis3lv02d/ F: drivers/platform/x86/hp_accel.c diff --git a/arch/powerpc/mm/book3s64/radix_tlb.c b/arch/powerpc/mm/book3s64/radix_tlb.c index bb9835681315..ce8a77fae6a7 100644 --- a/arch/powerpc/mm/book3s64/radix_tlb.c +++ b/arch/powerpc/mm/book3s64/radix_tlb.c @@ -666,6 +666,11 @@ EXPORT_SYMBOL(radix__flush_tlb_page); #define radix__flush_all_mm radix__local_flush_all_mm #endif /* CONFIG_SMP */ +/* + * If kernel TLBIs ever become local rather than global, then + * drivers/misc/ocxl/link.c:ocxl_link_add_pe will need some work, as it + * assumes kernel TLBIs are global. + */ void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end) { _tlbie_pid(0, RIC_FLUSH_ALL); diff --git a/drivers/acpi/acpi_amba.c b/drivers/acpi/acpi_amba.c index 8159f0a669b8..49b781a9cd97 100644 --- a/drivers/acpi/acpi_amba.c +++ b/drivers/acpi/acpi_amba.c @@ -21,6 +21,15 @@ static const struct acpi_device_id amba_id_list[] = { {"ARMH0061", 0}, /* PL061 GPIO Device */ + {"ARMHC500", 0}, /* ARM CoreSight ETM4x */ + {"ARMHC501", 0}, /* ARM CoreSight ETR */ + {"ARMHC502", 0}, /* ARM CoreSight STM */ + {"ARMHC503", 0}, /* ARM CoreSight Debug */ + {"ARMHC979", 0}, /* ARM CoreSight TPIU */ + {"ARMHC97C", 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */ + {"ARMHC98D", 0}, /* ARM CoreSight Dynamic Replicator */ + {"ARMHC9CA", 0}, /* ARM CoreSight CATU */ + {"ARMHC9FF", 0}, /* ARM CoreSight Dynamic Funnel */ {"", 0}, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index bc26b5511f0a..38a59a630cd4 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2059,10 +2059,9 @@ static size_t binder_get_object(struct binder_proc *proc, read_size = min_t(size_t, sizeof(*object), buffer->data_size - offset); if (offset > buffer->data_size || read_size < sizeof(*hdr) || - !IS_ALIGNED(offset, sizeof(u32))) + binder_alloc_copy_from_buffer(&proc->alloc, object, buffer, + offset, read_size)) return 0; - binder_alloc_copy_from_buffer(&proc->alloc, object, buffer, - offset, read_size); /* Ok, now see if we read a complete object. */ hdr = &object->hdr; @@ -2131,8 +2130,10 @@ static struct binder_buffer_object *binder_validate_ptr( return NULL; buffer_offset = start_offset + sizeof(binder_size_t) * index; - binder_alloc_copy_from_buffer(&proc->alloc, &object_offset, - b, buffer_offset, sizeof(object_offset)); + if (binder_alloc_copy_from_buffer(&proc->alloc, &object_offset, + b, buffer_offset, + sizeof(object_offset))) + return NULL; object_size = binder_get_object(proc, b, object_offset, object); if (!object_size || object->hdr.type != BINDER_TYPE_PTR) return NULL; @@ -2212,10 +2213,12 @@ static bool binder_validate_fixup(struct binder_proc *proc, return false; last_min_offset = last_bbo->parent_offset + sizeof(uintptr_t); buffer_offset = objects_start_offset + - sizeof(binder_size_t) * last_bbo->parent, - binder_alloc_copy_from_buffer(&proc->alloc, &last_obj_offset, - b, buffer_offset, - sizeof(last_obj_offset)); + sizeof(binder_size_t) * last_bbo->parent; + if (binder_alloc_copy_from_buffer(&proc->alloc, + &last_obj_offset, + b, buffer_offset, + sizeof(last_obj_offset))) + return false; } return (fixup_offset >= last_min_offset); } @@ -2301,15 +2304,15 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { struct binder_object_header *hdr; - size_t object_size; + size_t object_size = 0; struct binder_object object; binder_size_t object_offset; - binder_alloc_copy_from_buffer(&proc->alloc, &object_offset, - buffer, buffer_offset, - sizeof(object_offset)); - object_size = binder_get_object(proc, buffer, - object_offset, &object); + if (!binder_alloc_copy_from_buffer(&proc->alloc, &object_offset, + buffer, buffer_offset, + sizeof(object_offset))) + object_size = binder_get_object(proc, buffer, + object_offset, &object); if (object_size == 0) { pr_err("transaction release %d bad object at offset %lld, size %zd\n", debug_id, (u64)object_offset, buffer->data_size); @@ -2432,15 +2435,16 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, for (fd_index = 0; fd_index < fda->num_fds; fd_index++) { u32 fd; + int err; binder_size_t offset = fda_offset + fd_index * sizeof(fd); - binder_alloc_copy_from_buffer(&proc->alloc, - &fd, - buffer, - offset, - sizeof(fd)); - binder_deferred_fd_close(fd); + err = binder_alloc_copy_from_buffer( + &proc->alloc, &fd, buffer, + offset, sizeof(fd)); + WARN_ON(err); + if (!err) + binder_deferred_fd_close(fd); } } break; default: @@ -2683,11 +2687,12 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, int ret; binder_size_t offset = fda_offset + fdi * sizeof(fd); - binder_alloc_copy_from_buffer(&target_proc->alloc, - &fd, t->buffer, - offset, sizeof(fd)); - ret = binder_translate_fd(fd, offset, t, thread, - in_reply_to); + ret = binder_alloc_copy_from_buffer(&target_proc->alloc, + &fd, t->buffer, + offset, sizeof(fd)); + if (!ret) + ret = binder_translate_fd(fd, offset, t, thread, + in_reply_to); if (ret < 0) return ret; } @@ -2740,8 +2745,12 @@ static int binder_fixup_parent(struct binder_transaction *t, } buffer_offset = bp->parent_offset + (uintptr_t)parent->buffer - (uintptr_t)b->user_data; - binder_alloc_copy_to_buffer(&target_proc->alloc, b, buffer_offset, - &bp->buffer, sizeof(bp->buffer)); + if (binder_alloc_copy_to_buffer(&target_proc->alloc, b, buffer_offset, + &bp->buffer, sizeof(bp->buffer))) { + binder_user_error("%d:%d got transaction with invalid parent offset\n", + proc->pid, thread->pid); + return -EINVAL; + } return 0; } @@ -3160,15 +3169,20 @@ static void binder_transaction(struct binder_proc *proc, goto err_binder_alloc_buf_failed; } if (secctx) { + int err; size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + ALIGN(tr->offsets_size, sizeof(void *)) + ALIGN(extra_buffers_size, sizeof(void *)) - ALIGN(secctx_sz, sizeof(u64)); t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset; - binder_alloc_copy_to_buffer(&target_proc->alloc, - t->buffer, buf_offset, - secctx, secctx_sz); + err = binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, buf_offset, + secctx, secctx_sz); + if (err) { + t->security_ctx = 0; + WARN_ON(1); + } security_release_secctx(secctx, secctx_sz); secctx = NULL; } @@ -3234,11 +3248,16 @@ static void binder_transaction(struct binder_proc *proc, struct binder_object object; binder_size_t object_offset; - binder_alloc_copy_from_buffer(&target_proc->alloc, - &object_offset, - t->buffer, - buffer_offset, - sizeof(object_offset)); + if (binder_alloc_copy_from_buffer(&target_proc->alloc, + &object_offset, + t->buffer, + buffer_offset, + sizeof(object_offset))) { + return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; + goto err_bad_offset; + } object_size = binder_get_object(target_proc, t->buffer, object_offset, &object); if (object_size == 0 || object_offset < off_min) { @@ -3262,15 +3281,17 @@ static void binder_transaction(struct binder_proc *proc, fp = to_flat_binder_object(hdr); ret = binder_translate_binder(fp, t, thread); - if (ret < 0) { + + if (ret < 0 || + binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, + object_offset, + fp, sizeof(*fp))) { return_error = BR_FAILED_REPLY; return_error_param = ret; return_error_line = __LINE__; goto err_translate_failed; } - binder_alloc_copy_to_buffer(&target_proc->alloc, - t->buffer, object_offset, - fp, sizeof(*fp)); } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { @@ -3278,15 +3299,16 @@ static void binder_transaction(struct binder_proc *proc, fp = to_flat_binder_object(hdr); ret = binder_translate_handle(fp, t, thread); - if (ret < 0) { + if (ret < 0 || + binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, + object_offset, + fp, sizeof(*fp))) { return_error = BR_FAILED_REPLY; return_error_param = ret; return_error_line = __LINE__; goto err_translate_failed; } - binder_alloc_copy_to_buffer(&target_proc->alloc, - t->buffer, object_offset, - fp, sizeof(*fp)); } break; case BINDER_TYPE_FD: { @@ -3296,16 +3318,17 @@ static void binder_transaction(struct binder_proc *proc, int ret = binder_translate_fd(fp->fd, fd_offset, t, thread, in_reply_to); - if (ret < 0) { + fp->pad_binder = 0; + if (ret < 0 || + binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, + object_offset, + fp, sizeof(*fp))) { return_error = BR_FAILED_REPLY; return_error_param = ret; return_error_line = __LINE__; goto err_translate_failed; } - fp->pad_binder = 0; - binder_alloc_copy_to_buffer(&target_proc->alloc, - t->buffer, object_offset, - fp, sizeof(*fp)); } break; case BINDER_TYPE_FDA: { struct binder_object ptr_object; @@ -3393,15 +3416,16 @@ static void binder_transaction(struct binder_proc *proc, num_valid, last_fixup_obj_off, last_fixup_min_off); - if (ret < 0) { + if (ret < 0 || + binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, + object_offset, + bp, sizeof(*bp))) { return_error = BR_FAILED_REPLY; return_error_param = ret; return_error_line = __LINE__; goto err_translate_failed; } - binder_alloc_copy_to_buffer(&target_proc->alloc, - t->buffer, object_offset, - bp, sizeof(*bp)); last_fixup_obj_off = object_offset; last_fixup_min_off = 0; } break; @@ -4140,20 +4164,27 @@ static int binder_apply_fd_fixups(struct binder_proc *proc, trace_binder_transaction_fd_recv(t, fd, fixup->offset); fd_install(fd, fixup->file); fixup->file = NULL; - binder_alloc_copy_to_buffer(&proc->alloc, t->buffer, - fixup->offset, &fd, - sizeof(u32)); + if (binder_alloc_copy_to_buffer(&proc->alloc, t->buffer, + fixup->offset, &fd, + sizeof(u32))) { + ret = -EINVAL; + break; + } } list_for_each_entry_safe(fixup, tmp, &t->fd_fixups, fixup_entry) { if (fixup->file) { fput(fixup->file); } else if (ret) { u32 fd; - - binder_alloc_copy_from_buffer(&proc->alloc, &fd, - t->buffer, fixup->offset, - sizeof(fd)); - binder_deferred_fd_close(fd); + int err; + + err = binder_alloc_copy_from_buffer(&proc->alloc, &fd, + t->buffer, + fixup->offset, + sizeof(fd)); + WARN_ON(err); + if (!err) + binder_deferred_fd_close(fd); } list_del(&fixup->fixup_entry); kfree(fixup); @@ -4268,6 +4299,8 @@ retry: case BINDER_WORK_TRANSACTION_COMPLETE: { binder_inner_proc_unlock(proc); cmd = BR_TRANSACTION_COMPLETE; + kfree(w); + binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); @@ -4276,8 +4309,6 @@ retry: binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE, "%d:%d BR_TRANSACTION_COMPLETE\n", proc->pid, thread->pid); - kfree(w); - binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); } break; case BINDER_WORK_NODE: { struct binder_node *node = container_of(w, struct binder_node, work); diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index ce5603c2291c..6d79a1b0d446 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -1119,15 +1119,16 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc, return 0; } -static void binder_alloc_do_buffer_copy(struct binder_alloc *alloc, - bool to_buffer, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - void *ptr, - size_t bytes) +static int binder_alloc_do_buffer_copy(struct binder_alloc *alloc, + bool to_buffer, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + void *ptr, + size_t bytes) { /* All copies must be 32-bit aligned and 32-bit size */ - BUG_ON(!check_buffer(alloc, buffer, buffer_offset, bytes)); + if (!check_buffer(alloc, buffer, buffer_offset, bytes)) + return -EINVAL; while (bytes) { unsigned long size; @@ -1155,25 +1156,26 @@ static void binder_alloc_do_buffer_copy(struct binder_alloc *alloc, ptr = ptr + size; buffer_offset += size; } + return 0; } -void binder_alloc_copy_to_buffer(struct binder_alloc *alloc, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - void *src, - size_t bytes) +int binder_alloc_copy_to_buffer(struct binder_alloc *alloc, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + void *src, + size_t bytes) { - binder_alloc_do_buffer_copy(alloc, true, buffer, buffer_offset, - src, bytes); + return binder_alloc_do_buffer_copy(alloc, true, buffer, buffer_offset, + src, bytes); } -void binder_alloc_copy_from_buffer(struct binder_alloc *alloc, - void *dest, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - size_t bytes) +int binder_alloc_copy_from_buffer(struct binder_alloc *alloc, + void *dest, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + size_t bytes) { - binder_alloc_do_buffer_copy(alloc, false, buffer, buffer_offset, - dest, bytes); + return binder_alloc_do_buffer_copy(alloc, false, buffer, buffer_offset, + dest, bytes); } diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 71bfa95f8e09..db9c1b984695 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -159,17 +159,17 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc, const void __user *from, size_t bytes); -void binder_alloc_copy_to_buffer(struct binder_alloc *alloc, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - void *src, - size_t bytes); - -void binder_alloc_copy_from_buffer(struct binder_alloc *alloc, - void *dest, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - size_t bytes); +int binder_alloc_copy_to_buffer(struct binder_alloc *alloc, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + void *src, + size_t bytes); + +int binder_alloc_copy_from_buffer(struct binder_alloc *alloc, + void *dest, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + size_t bytes); #endif /* _LINUX_BINDER_ALLOC_H */ diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 167e7370d43a..e5e5333f302d 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -134,7 +134,7 @@ static int bsr_mmap(struct file *filp, struct vm_area_struct *vma) return 0; } -static int bsr_open(struct inode * inode, struct file * filp) +static int bsr_open(struct inode *inode, struct file *filp) { struct cdev *cdev = inode->i_cdev; struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev); @@ -309,7 +309,8 @@ static int __init bsr_init(void) goto out_err_2; } - if ((ret = bsr_create_devs(np)) < 0) { + ret = bsr_create_devs(np); + if (ret < 0) { np = NULL; goto out_err_3; } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 53cfe574d8d4..f6a147427029 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -226,6 +226,7 @@ int misc_register(struct miscdevice *misc) mutex_unlock(&misc_mtx); return err; } +EXPORT_SYMBOL(misc_register); /** * misc_deregister - unregister a miscellaneous device @@ -249,8 +250,6 @@ void misc_deregister(struct miscdevice *misc) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); } - -EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); static char *misc_devnode(struct device *dev, umode_t *mode) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 4fa2931dcb7b..00b113f4b958 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -833,7 +833,7 @@ static int quad8_action_get(struct counter_device *counter, return 0; } -const struct counter_ops quad8_ops = { +static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 6f5af4196b8d..fa1804460e8c 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -37,6 +37,18 @@ config EXTCON_AXP288 Say Y here to enable support for USB peripheral detection and USB MUX switching by X-Power AXP288 PMIC. +config EXTCON_FSA9480 + tristate "FSA9480 EXTCON Support" + depends on INPUT && I2C + select IRQ_DOMAIN + select REGMAP_I2C + help + If you say yes here you get support for the Fairchild Semiconductor + FSA9480 microUSB switch and accessory detector chip. The FSA9480 is a USB + port accessory detector and switch. The FSA9480 is fully controlled using + I2C and enables USB data, stereo and mono audio, video, microphone + and UART data to use a common connector port. + config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index d3941a735df3..52096fd8a216 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,6 +8,7 @@ extcon-core-objs += extcon.o devres.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o +obj-$(CONFIG_EXTCON_FSA9480) += extcon-fsa9480.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index bb6434726c7a..7e9f4c9ee87d 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -326,10 +326,12 @@ static void arizona_start_mic(struct arizona_extcon_info *info) arizona_extcon_pulse_micbias(info); - regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, - ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, - &change); - if (!change) { + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, + &change); + if (ret < 0) { + dev_err(arizona->dev, "Failed to enable micd: %d\n", ret); + } else if (!change) { regulator_disable(info->micvdd); pm_runtime_put_autosuspend(info->dev); } @@ -341,12 +343,14 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) const char *widget = arizona_extcon_get_micbias(info); struct snd_soc_dapm_context *dapm = arizona->dapm; struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); - bool change; + bool change = false; int ret; - regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, - ARIZONA_MICD_ENA, 0, - &change); + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + if (ret < 0) + dev_err(arizona->dev, "Failed to disable micd: %d\n", ret); ret = snd_soc_component_disable_pin(component, widget); if (ret != 0) @@ -1718,12 +1722,15 @@ static int arizona_extcon_remove(struct platform_device *pdev) struct arizona *arizona = info->arizona; int jack_irq_rise, jack_irq_fall; bool change; + int ret; - regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, - ARIZONA_MICD_ENA, 0, - &change); - - if (change) { + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n", + ret); + } else if (change) { regulator_disable(info->micvdd); pm_runtime_put(info->dev); } diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c new file mode 100644 index 000000000000..350fb34abfa0 --- /dev/null +++ b/drivers/extcon/extcon-fsa9480.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * extcon-fsa9480.c - Fairchild Semiconductor FSA9480 extcon driver + * + * Copyright (c) 2019 Tomasz Figa <tomasz.figa@gmail.com> + * + * Loosely based on old fsa9480 misc-device driver. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/kobject.h> +#include <linux/extcon-provider.h> +#include <linux/irqdomain.h> +#include <linux/regmap.h> + +/* FSA9480 I2C registers */ +#define FSA9480_REG_DEVID 0x01 +#define FSA9480_REG_CTRL 0x02 +#define FSA9480_REG_INT1 0x03 +#define FSA9480_REG_INT2 0x04 +#define FSA9480_REG_INT1_MASK 0x05 +#define FSA9480_REG_INT2_MASK 0x06 +#define FSA9480_REG_ADC 0x07 +#define FSA9480_REG_TIMING1 0x08 +#define FSA9480_REG_TIMING2 0x09 +#define FSA9480_REG_DEV_T1 0x0a +#define FSA9480_REG_DEV_T2 0x0b +#define FSA9480_REG_BTN1 0x0c +#define FSA9480_REG_BTN2 0x0d +#define FSA9480_REG_CK 0x0e +#define FSA9480_REG_CK_INT1 0x0f +#define FSA9480_REG_CK_INT2 0x10 +#define FSA9480_REG_CK_INTMASK1 0x11 +#define FSA9480_REG_CK_INTMASK2 0x12 +#define FSA9480_REG_MANSW1 0x13 +#define FSA9480_REG_MANSW2 0x14 +#define FSA9480_REG_END 0x15 + +/* Control */ +#define CON_SWITCH_OPEN (1 << 4) +#define CON_RAW_DATA (1 << 3) +#define CON_MANUAL_SW (1 << 2) +#define CON_WAIT (1 << 1) +#define CON_INT_MASK (1 << 0) +#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \ + CON_MANUAL_SW | CON_WAIT) + +/* Device Type 1 */ +#define DEV_USB_OTG 7 +#define DEV_DEDICATED_CHG 6 +#define DEV_USB_CHG 5 +#define DEV_CAR_KIT 4 +#define DEV_UART 3 +#define DEV_USB 2 +#define DEV_AUDIO_2 1 +#define DEV_AUDIO_1 0 + +#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB) +#define DEV_T1_UART_MASK (DEV_UART) +#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG) + +/* Device Type 2 */ +#define DEV_AV 14 +#define DEV_TTY 13 +#define DEV_PPD 12 +#define DEV_JIG_UART_OFF 11 +#define DEV_JIG_UART_ON 10 +#define DEV_JIG_USB_OFF 9 +#define DEV_JIG_USB_ON 8 + +#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON) +#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON) +#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \ + DEV_JIG_UART_OFF | DEV_JIG_UART_ON) + +/* + * Manual Switch + * D- [7:5] / D+ [4:2] + * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO + */ +#define SW_VAUDIO ((4 << 5) | (4 << 2)) +#define SW_UART ((3 << 5) | (3 << 2)) +#define SW_AUDIO ((2 << 5) | (2 << 2)) +#define SW_DHOST ((1 << 5) | (1 << 2)) +#define SW_AUTO ((0 << 5) | (0 << 2)) + +/* Interrupt 1 */ +#define INT1_MASK (0xff << 0) +#define INT_DETACH (1 << 1) +#define INT_ATTACH (1 << 0) + +/* Interrupt 2 mask */ +#define INT2_MASK (0x1f << 0) + +/* Timing Set 1 */ +#define TIMING1_ADC_500MS (0x6 << 0) + +struct fsa9480_usbsw { + struct device *dev; + struct regmap *regmap; + struct extcon_dev *edev; + u16 cable; +}; + +static const unsigned int fsa9480_extcon_cable[] = { + EXTCON_USB_HOST, + EXTCON_USB, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_ACA, + EXTCON_JACK_LINE_OUT, + EXTCON_JACK_VIDEO_OUT, + EXTCON_JIG, + + EXTCON_NONE, +}; + +static const u64 cable_types[] = { + [DEV_USB_OTG] = BIT_ULL(EXTCON_USB_HOST), + [DEV_DEDICATED_CHG] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_DCP), + [DEV_USB_CHG] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP), + [DEV_CAR_KIT] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP) + | BIT_ULL(EXTCON_JACK_LINE_OUT), + [DEV_UART] = BIT_ULL(EXTCON_JIG), + [DEV_USB] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP), + [DEV_AUDIO_2] = BIT_ULL(EXTCON_JACK_LINE_OUT), + [DEV_AUDIO_1] = BIT_ULL(EXTCON_JACK_LINE_OUT), + [DEV_AV] = BIT_ULL(EXTCON_JACK_LINE_OUT) + | BIT_ULL(EXTCON_JACK_VIDEO_OUT), + [DEV_TTY] = BIT_ULL(EXTCON_JIG), + [DEV_PPD] = BIT_ULL(EXTCON_JACK_LINE_OUT) | BIT_ULL(EXTCON_CHG_USB_ACA), + [DEV_JIG_UART_OFF] = BIT_ULL(EXTCON_JIG), + [DEV_JIG_UART_ON] = BIT_ULL(EXTCON_JIG), + [DEV_JIG_USB_OFF] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_JIG), + [DEV_JIG_USB_ON] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_JIG), +}; + +/* Define regmap configuration of FSA9480 for I2C communication */ +static bool fsa9480_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSA9480_REG_INT1_MASK: + return true; + default: + break; + } + return false; +} + +static const struct regmap_config fsa9480_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = fsa9480_volatile_reg, + .max_register = FSA9480_REG_END, +}; + +static int fsa9480_write_reg(struct fsa9480_usbsw *usbsw, int reg, int value) +{ + int ret; + + ret = regmap_write(usbsw->regmap, reg, value); + if (ret < 0) + dev_err(usbsw->dev, "%s: err %d\n", __func__, ret); + + return ret; +} + +static int fsa9480_read_reg(struct fsa9480_usbsw *usbsw, int reg) +{ + int ret, val; + + ret = regmap_read(usbsw->regmap, reg, &val); + if (ret < 0) { + dev_err(usbsw->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + return val; +} + +static int fsa9480_read_irq(struct fsa9480_usbsw *usbsw, int *value) +{ + u8 regs[2]; + int ret; + + ret = regmap_bulk_read(usbsw->regmap, FSA9480_REG_INT1, regs, 2); + if (ret < 0) + dev_err(usbsw->dev, "%s: err %d\n", __func__, ret); + + *value = regs[1] << 8 | regs[0]; + return ret; +} + +static void fsa9480_handle_change(struct fsa9480_usbsw *usbsw, + u16 mask, bool attached) +{ + while (mask) { + int dev = fls64(mask) - 1; + u64 cables = cable_types[dev]; + + while (cables) { + int cable = fls64(cables) - 1; + + extcon_set_state_sync(usbsw->edev, cable, attached); + cables &= ~BIT_ULL(cable); + } + + mask &= ~BIT_ULL(dev); + } +} + +static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw) +{ + int val1, val2; + u16 val; + + val1 = fsa9480_read_reg(usbsw, FSA9480_REG_DEV_T1); + val2 = fsa9480_read_reg(usbsw, FSA9480_REG_DEV_T2); + if (val1 < 0 || val2 < 0) { + dev_err(usbsw->dev, "%s: failed to read registers", __func__); + return; + } + val = val2 << 8 | val1; + + dev_info(usbsw->dev, "dev1: 0x%x, dev2: 0x%x\n", val1, val2); + + /* handle detached cables first */ + fsa9480_handle_change(usbsw, usbsw->cable & ~val, false); + + /* then handle attached ones */ + fsa9480_handle_change(usbsw, val & ~usbsw->cable, true); + + usbsw->cable = val; +} + +static irqreturn_t fsa9480_irq_handler(int irq, void *data) +{ + struct fsa9480_usbsw *usbsw = data; + int intr = 0; + + /* clear interrupt */ + fsa9480_read_irq(usbsw, &intr); + if (!intr) + return IRQ_NONE; + + /* device detection */ + fsa9480_detect_dev(usbsw); + + return IRQ_HANDLED; +} + +static int fsa9480_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fsa9480_usbsw *info; + int ret; + + if (!client->irq) { + dev_err(&client->dev, "no interrupt provided\n"); + return -EINVAL; + } + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->dev = &client->dev; + + i2c_set_clientdata(client, info); + + /* External connector */ + info->edev = devm_extcon_dev_allocate(info->dev, + fsa9480_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(info->dev, "failed to allocate memory for extcon\n"); + ret = -ENOMEM; + return ret; + } + + ret = devm_extcon_dev_register(info->dev, info->edev); + if (ret) { + dev_err(info->dev, "failed to register extcon device\n"); + return ret; + } + + info->regmap = devm_regmap_init_i2c(client, &fsa9480_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(info->dev, "failed to allocate register map: %d\n", + ret); + return ret; + } + + /* ADC Detect Time: 500ms */ + fsa9480_write_reg(info, FSA9480_REG_TIMING1, TIMING1_ADC_500MS); + + /* configure automatic switching */ + fsa9480_write_reg(info, FSA9480_REG_CTRL, CON_MASK); + + /* unmask interrupt (attach/detach only) */ + fsa9480_write_reg(info, FSA9480_REG_INT1_MASK, + INT1_MASK & ~(INT_ATTACH | INT_DETACH)); + fsa9480_write_reg(info, FSA9480_REG_INT2_MASK, INT2_MASK); + + ret = devm_request_threaded_irq(info->dev, client->irq, NULL, + fsa9480_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "fsa9480", info); + if (ret) { + dev_err(info->dev, "failed to request IRQ\n"); + return ret; + } + + device_init_wakeup(info->dev, true); + fsa9480_detect_dev(info); + + return 0; +} + +static int fsa9480_remove(struct i2c_client *client) +{ + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fsa9480_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev) && client->irq) + enable_irq_wake(client->irq); + + return 0; +} + +static int fsa9480_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev) && client->irq) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops fsa9480_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsa9480_suspend, fsa9480_resume) +}; + +static const struct i2c_device_id fsa9480_id[] = { + { "fsa9480", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, fsa9480_id); + +static const struct of_device_id fsa9480_of_match[] = { + { .compatible = "fcs,fsa9480", }, + { }, +}; +MODULE_DEVICE_TABLE(of, fsa9480_of_match); + +static struct i2c_driver fsa9480_i2c_driver = { + .driver = { + .name = "fsa9480", + .pm = &fsa9480_pm_ops, + .of_match_table = fsa9480_of_match, + }, + .probe = fsa9480_probe, + .remove = fsa9480_remove, + .id_table = fsa9480_id, +}; + +static int __init fsa9480_module_init(void) +{ + return i2c_add_driver(&fsa9480_i2c_driver); +} +subsys_initcall(fsa9480_module_init); + +static void __exit fsa9480_module_exit(void) +{ + i2c_del_driver(&fsa9480_i2c_driver); +} +module_exit(fsa9480_module_exit); + +MODULE_DESCRIPTION("Fairchild Semiconductor FSA9480 extcon driver"); +MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index 1f63b3034b17..7b7b4a6eedda 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h @@ -12,7 +12,7 @@ #ifndef __COREBOOT_TABLE_H #define __COREBOOT_TABLE_H -#include <linux/io.h> +#include <linux/device.h> /* Coreboot table header structure */ struct coreboot_table_header { @@ -83,4 +83,13 @@ int coreboot_driver_register(struct coreboot_driver *driver); /* Unregister a driver that uses the data from a coreboot table. */ void coreboot_driver_unregister(struct coreboot_driver *driver); +/* module_coreboot_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_coreboot_driver(__coreboot_driver) \ + module_driver(__coreboot_driver, coreboot_driver_register, \ + coreboot_driver_unregister) + #endif /* __COREBOOT_TABLE_H */ diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c index 7e67b651e4ac..916f26adc595 100644 --- a/drivers/firmware/google/framebuffer-coreboot.c +++ b/drivers/firmware/google/framebuffer-coreboot.c @@ -89,19 +89,7 @@ static struct coreboot_driver framebuffer_driver = { }, .tag = CB_TAG_FRAMEBUFFER, }; - -static int __init coreboot_framebuffer_init(void) -{ - return coreboot_driver_register(&framebuffer_driver); -} - -static void coreboot_framebuffer_exit(void) -{ - coreboot_driver_unregister(&framebuffer_driver); -} - -module_init(coreboot_framebuffer_init); -module_exit(coreboot_framebuffer_exit); +module_coreboot_driver(framebuffer_driver); MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c index ac90e8536565..fd7f0fbec07e 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/google/memconsole-coreboot.c @@ -8,6 +8,7 @@ */ #include <linux/device.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -26,7 +27,7 @@ struct cbmem_cons { #define CURSOR_MASK ((1 << 28) - 1) #define OVERFLOW (1 << 31) -static struct cbmem_cons __iomem *cbmem_console; +static struct cbmem_cons *cbmem_console; static u32 cbmem_console_size; /* @@ -67,7 +68,7 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count) static int memconsole_probe(struct coreboot_device *dev) { - struct cbmem_cons __iomem *tmp_cbmc; + struct cbmem_cons *tmp_cbmc; tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr, sizeof(*tmp_cbmc), MEMREMAP_WB); @@ -77,13 +78,13 @@ static int memconsole_probe(struct coreboot_device *dev) /* Read size only once to prevent overrun attack through /dev/mem. */ cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; - cbmem_console = memremap(dev->cbmem_ref.cbmem_addr, + cbmem_console = devm_memremap(&dev->dev, dev->cbmem_ref.cbmem_addr, cbmem_console_size + sizeof(*cbmem_console), MEMREMAP_WB); memunmap(tmp_cbmc); - if (!cbmem_console) - return -ENOMEM; + if (IS_ERR(cbmem_console)) + return PTR_ERR(cbmem_console); memconsole_setup(memconsole_coreboot_read); @@ -94,9 +95,6 @@ static int memconsole_remove(struct coreboot_device *dev) { memconsole_exit(); - if (cbmem_console) - memunmap(cbmem_console); - return 0; } @@ -108,19 +106,7 @@ static struct coreboot_driver memconsole_driver = { }, .tag = CB_TAG_CBMEM_CONSOLE, }; - -static void coreboot_memconsole_exit(void) -{ - coreboot_driver_unregister(&memconsole_driver); -} - -static int __init coreboot_memconsole_init(void) -{ - return coreboot_driver_register(&memconsole_driver); -} - -module_exit(coreboot_memconsole_exit); -module_init(coreboot_memconsole_init); +module_coreboot_driver(memconsole_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c index fe5aa740c34d..44d314ad69e4 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/google/memconsole.c @@ -7,21 +7,22 @@ * Copyright 2017 Google Inc. */ -#include <linux/init.h> #include <linux/sysfs.h> #include <linux/kobject.h> #include <linux/module.h> #include "memconsole.h" -static ssize_t (*memconsole_read_func)(char *, loff_t, size_t); - static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { + ssize_t (*memconsole_read_func)(char *, loff_t, size_t); + + memconsole_read_func = bin_attr->private; if (WARN_ON_ONCE(!memconsole_read_func)) return -EIO; + return memconsole_read_func(buf, pos, count); } @@ -32,7 +33,7 @@ static struct bin_attribute memconsole_bin_attr = { void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t)) { - memconsole_read_func = read_func; + memconsole_bin_attr.private = read_func; } EXPORT_SYMBOL(memconsole_setup); diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index fd5212c395c0..0739f3b70347 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -316,19 +316,7 @@ static struct coreboot_driver vpd_driver = { }, .tag = CB_TAG_VPD, }; - -static int __init coreboot_vpd_init(void) -{ - return coreboot_driver_register(&vpd_driver); -} - -static void __exit coreboot_vpd_exit(void) -{ - coreboot_driver_unregister(&vpd_driver); -} - -module_init(coreboot_vpd_init); -module_exit(coreboot_vpd_exit); +module_coreboot_driver(vpd_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/google/vpd_decode.c index c62fa7063a7c..92e3258552fc 100644 --- a/drivers/firmware/google/vpd_decode.c +++ b/drivers/firmware/google/vpd_decode.c @@ -7,8 +7,6 @@ * Copyright 2017 Google Inc. */ -#include <linux/export.h> - #include "vpd_decode.h" static int vpd_decode_len(const s32 max_len, const u8 *in, diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 8072c195d831..474f304ec109 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -26,9 +26,9 @@ config FPGA_MGR_SOCFPGA_A10 FPGA manager driver support for Altera Arria10 SoCFPGA. config ALTERA_PR_IP_CORE - tristate "Altera Partial Reconfiguration IP Core" - help - Core driver support for Altera Partial Reconfiguration IP component + tristate "Altera Partial Reconfiguration IP Core" + help + Core driver support for Altera Partial Reconfiguration IP component config ALTERA_PR_IP_CORE_PLAT tristate "Platform support of Altera Partial Reconfiguration IP Core" diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c index 76f37709dd1a..b3f7eee3c93f 100644 --- a/drivers/fpga/dfl-fme-mgr.c +++ b/drivers/fpga/dfl-fme-mgr.c @@ -30,8 +30,8 @@ #define FME_PR_STS 0x10 #define FME_PR_DATA 0x18 #define FME_PR_ERR 0x20 -#define FME_PR_INTFC_ID_H 0xA8 -#define FME_PR_INTFC_ID_L 0xB0 +#define FME_PR_INTFC_ID_L 0xA8 +#define FME_PR_INTFC_ID_H 0xB0 /* FME PR Control Register Bitfield */ #define FME_PR_CTRL_PR_RST BIT_ULL(0) /* Reset PR engine */ diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index d9ca9554844a..3c71dc3faaf5 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -74,6 +74,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) struct dfl_fme *fme; unsigned long minsz; void *buf = NULL; + size_t length; int ret = 0; u64 v; @@ -85,9 +86,6 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) if (port_pr.argsz < minsz || port_pr.flags) return -EINVAL; - if (!IS_ALIGNED(port_pr.buffer_size, 4)) - return -EINVAL; - /* get fme header region */ fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, FME_FEATURE_ID_HEADER); @@ -103,7 +101,13 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) port_pr.buffer_size)) return -EFAULT; - buf = vmalloc(port_pr.buffer_size); + /* + * align PR buffer per PR bandwidth, as HW ignores the extra padding + * data automatically. + */ + length = ALIGN(port_pr.buffer_size, 4); + + buf = vmalloc(length); if (!buf) return -ENOMEM; @@ -140,7 +144,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) fpga_image_info_free(region->info); info->buf = buf; - info->count = port_pr.buffer_size; + info->count = length; info->region_id = port_pr.port_id; region->info = info; @@ -159,9 +163,6 @@ unlock_exit: mutex_unlock(&pdata->lock); free_exit: vfree(buf); - if (copy_to_user((void __user *)arg, &port_pr, minsz)) - return -EFAULT; - return ret; } diff --git a/drivers/fsi/cf-fsi-fw.h b/drivers/fsi/cf-fsi-fw.h index 712df0461911..1118eaf7ee39 100644 --- a/drivers/fsi/cf-fsi-fw.h +++ b/drivers/fsi/cf-fsi-fw.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ #ifndef __CF_FSI_FW_H #define __CF_FSI_FW_H diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 1d83f3ba478b..1f76740f33b6 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1029,6 +1029,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) } + rc = fsi_slave_set_smode(slave); + if (rc) { + dev_warn(&master->dev, + "can't set smode on slave:%02x:%02x %d\n", + link, id, rc); + goto err_free; + } + /* Allocate a minor in the FSI space */ rc = __fsi_get_new_minor(slave, fsi_dev_cfam, &slave->dev.devt, &slave->cdev_idx); @@ -1040,17 +1048,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) rc = cdev_device_add(&slave->cdev, &slave->dev); if (rc) { dev_err(&slave->dev, "Error %d creating slave device\n", rc); - goto err_free; + goto err_free_ida; } - rc = fsi_slave_set_smode(slave); - if (rc) { - dev_warn(&master->dev, - "can't set smode on slave:%02x:%02x %d\n", - link, id, rc); - kfree(slave); - return -ENODEV; - } + /* Now that we have the cdev registered with the core, any fatal + * failures beyond this point will need to clean up through + * cdev_device_del(). Fortunately though, nothing past here is fatal. + */ + if (master->link_config) master->link_config(master, link, slave->t_send_delay, @@ -1067,10 +1072,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) dev_dbg(&master->dev, "failed during slave scan with: %d\n", rc); - return rc; + return 0; - err_free: - put_device(&slave->dev); +err_free_ida: + fsi_free_minor(slave->dev.devt); +err_free: + of_node_put(slave->dev.of_node); + kfree(slave); return rc; } diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index a2301cea1cbb..7da9c81759ac 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -412,6 +412,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS); struct occ *occ = dev_get_drvdata(dev); struct occ_response *resp = response; + u8 seq_no; u16 resp_data_length; unsigned long start; int rc; @@ -426,6 +427,8 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, mutex_lock(&occ->occ_lock); + /* Extract the seq_no from the command (first byte) */ + seq_no = *(const u8 *)request; rc = occ_putsram(occ, OCC_SRAM_CMD_ADDR, request, req_len); if (rc) goto done; @@ -441,11 +444,17 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, if (rc) goto done; - if (resp->return_status == OCC_RESP_CMD_IN_PRG) { + if (resp->return_status == OCC_RESP_CMD_IN_PRG || + resp->seq_no != seq_no) { rc = -ETIMEDOUT; - if (time_after(jiffies, start + timeout)) - break; + if (time_after(jiffies, start + timeout)) { + dev_err(occ->dev, "resp timeout status=%02x " + "resp seq_no=%d our seq_no=%d\n", + resp->return_status, resp->seq_no, + seq_no); + goto done; + } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(wait_time); diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index d92f5b87c251..f54df9ebc8b3 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -289,11 +289,11 @@ static int sbefifo_check_sbe_state(struct sbefifo *sbefifo) switch ((sbm & CFAM_SBM_SBE_STATE_MASK) >> CFAM_SBM_SBE_STATE_SHIFT) { case SBE_STATE_UNKNOWN: return -ESHUTDOWN; + case SBE_STATE_DMT: + return -EBUSY; case SBE_STATE_IPLING: case SBE_STATE_ISTEP: case SBE_STATE_MPIPL: - case SBE_STATE_DMT: - return -EBUSY; case SBE_STATE_RUNTIME: case SBE_STATE_DUMP: /* Not sure about that one */ break; diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index d593517af5c2..a7d2b16dd702 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -124,12 +124,12 @@ struct extended_sensor { static int occ_poll(struct occ *occ) { int rc; - u16 checksum = occ->poll_cmd_data + 1; + u16 checksum = occ->poll_cmd_data + occ->seq_no + 1; u8 cmd[8]; struct occ_poll_response_header *header; /* big endian */ - cmd[0] = 0; /* sequence number */ + cmd[0] = occ->seq_no++; /* sequence number */ cmd[1] = 0; /* cmd type */ cmd[2] = 0; /* data length msb */ cmd[3] = 1; /* data length lsb */ diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index fc13f3c73c47..67e6968b8978 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -95,6 +95,7 @@ struct occ { struct occ_sensors sensors; int powr_sample_time_us; /* average power sample time */ + u8 seq_no; u8 poll_cmd_data; /* to perform OCC poll command */ int (*send_cmd)(struct occ *occ, u8 *cmd); diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 5487d4a1abc2..14638db4991d 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,6 +4,7 @@ # menuconfig CORESIGHT bool "CoreSight Tracing Support" + depends on OF || ACPI select ARM_AMBA select PERF_EVENTS help diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 3b435aa42af5..3c0ac421e211 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -2,8 +2,7 @@ # # Makefile for CoreSight drivers. # -obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o -obj-$(CONFIG_OF) += of_coresight.o +obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o coresight-platform.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index 4ea68a3522e9..16ebf38a9f66 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -28,6 +28,8 @@ #define catu_dbg(x, ...) do {} while (0) #endif +DEFINE_CORESIGHT_DEVLIST(catu_devs, "catu"); + struct catu_etr_buf { struct tmc_sg_table *catu_table; dma_addr_t sladdr; @@ -328,19 +330,18 @@ static int catu_alloc_etr_buf(struct tmc_drvdata *tmc_drvdata, struct etr_buf *etr_buf, int node, void **pages) { struct coresight_device *csdev; - struct device *catu_dev; struct tmc_sg_table *catu_table; struct catu_etr_buf *catu_buf; csdev = tmc_etr_get_catu_device(tmc_drvdata); if (!csdev) return -ENODEV; - catu_dev = csdev->dev.parent; catu_buf = kzalloc(sizeof(*catu_buf), GFP_KERNEL); if (!catu_buf) return -ENOMEM; - catu_table = catu_init_sg_table(catu_dev, node, etr_buf->size, pages); + catu_table = catu_init_sg_table(&csdev->dev, node, + etr_buf->size, pages); if (IS_ERR(catu_table)) { kfree(catu_buf); return PTR_ERR(catu_table); @@ -409,13 +410,14 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) int rc; u32 control, mode; struct etr_buf *etr_buf = data; + struct device *dev = &drvdata->csdev->dev; if (catu_wait_for_ready(drvdata)) - dev_warn(drvdata->dev, "Timeout while waiting for READY\n"); + dev_warn(dev, "Timeout while waiting for READY\n"); control = catu_read_control(drvdata); if (control & BIT(CATU_CONTROL_ENABLE)) { - dev_warn(drvdata->dev, "CATU is already enabled\n"); + dev_warn(dev, "CATU is already enabled\n"); return -EBUSY; } @@ -441,7 +443,7 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) catu_write_irqen(drvdata, 0); catu_write_mode(drvdata, mode); catu_write_control(drvdata, control); - dev_dbg(drvdata->dev, "Enabled in %s mode\n", + dev_dbg(dev, "Enabled in %s mode\n", (mode == CATU_MODE_PASS_THROUGH) ? "Pass through" : "Translate"); @@ -462,15 +464,16 @@ static int catu_enable(struct coresight_device *csdev, void *data) static int catu_disable_hw(struct catu_drvdata *drvdata) { int rc = 0; + struct device *dev = &drvdata->csdev->dev; catu_write_control(drvdata, 0); coresight_disclaim_device_unlocked(drvdata->base); if (catu_wait_for_ready(drvdata)) { - dev_info(drvdata->dev, "Timeout while waiting for READY\n"); + dev_info(dev, "Timeout while waiting for READY\n"); rc = -EAGAIN; } - dev_dbg(drvdata->dev, "Disabled\n"); + dev_dbg(dev, "Disabled\n"); return rc; } @@ -502,17 +505,11 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_desc catu_desc; struct coresight_platform_data *pdata = NULL; struct device *dev = &adev->dev; - struct device_node *np = dev->of_node; void __iomem *base; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - goto out; - } - dev->platform_data = pdata; - } + catu_desc.name = coresight_alloc_device_name(&catu_devs, dev); + if (!catu_desc.name) + return -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) { @@ -520,7 +517,6 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id) goto out; } - drvdata->dev = dev; dev_set_drvdata(dev, drvdata); base = devm_ioremap_resource(dev, &adev->res); if (IS_ERR(base)) { @@ -547,6 +543,13 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id) if (ret) goto out; + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto out; + } + dev->platform_data = pdata; + drvdata->base = base; catu_desc.pdata = pdata; catu_desc.dev = dev; @@ -554,6 +557,7 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id) catu_desc.type = CORESIGHT_DEV_TYPE_HELPER; catu_desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CATU; catu_desc.ops = &catu_ops; + drvdata->csdev = coresight_register(&catu_desc); if (IS_ERR(drvdata->csdev)) ret = PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-catu.h b/drivers/hwtracing/coresight/coresight-catu.h index 1d2ad183fd92..80ceee3c739c 100644 --- a/drivers/hwtracing/coresight/coresight-catu.h +++ b/drivers/hwtracing/coresight/coresight-catu.h @@ -61,7 +61,6 @@ #define CATU_IRQEN_OFF 0x0 struct catu_drvdata { - struct device *dev; void __iomem *base; struct coresight_device *csdev; int irq; diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index e8819d750938..58bfd6319f65 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -572,14 +572,16 @@ static int debug_probe(struct amba_device *adev, const struct amba_id *id) struct device *dev = &adev->dev; struct debug_drvdata *drvdata; struct resource *res = &adev->res; - struct device_node *np = adev->dev.of_node; int ret; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->cpu = np ? of_coresight_get_cpu(np) : 0; + drvdata->cpu = coresight_get_cpu(dev); + if (drvdata->cpu < 0) + return drvdata->cpu; + if (per_cpu(debug_drvdata, drvdata->cpu)) { dev_err(dev, "CPU%d drvdata has already been initialized\n", drvdata->cpu); diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 4ee4c80a4354..3810290e6d07 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -63,10 +63,11 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 +DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); + /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @atclk: optional clock for the core parts of the ETB. * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.etb" entry. @@ -81,7 +82,6 @@ */ struct etb_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; struct miscdevice miscdev; @@ -227,7 +227,6 @@ out: static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) { int ret; - struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); switch (mode) { case CS_MODE_SYSFS: @@ -244,13 +243,14 @@ static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) if (ret) return ret; - dev_dbg(drvdata->dev, "ETB enabled\n"); + dev_dbg(&csdev->dev, "ETB enabled\n"); return 0; } static void __etb_disable_hw(struct etb_drvdata *drvdata) { u32 ffcr; + struct device *dev = &drvdata->csdev->dev; CS_UNLOCK(drvdata->base); @@ -263,7 +263,7 @@ static void __etb_disable_hw(struct etb_drvdata *drvdata) writel_relaxed(ffcr, drvdata->base + ETB_FFCR); if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) { - dev_err(drvdata->dev, + dev_err(dev, "timeout while waiting for completion of Manual Flush\n"); } @@ -271,7 +271,7 @@ static void __etb_disable_hw(struct etb_drvdata *drvdata) writel_relaxed(0x0, drvdata->base + ETB_CTL_REG); if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) { - dev_err(drvdata->dev, + dev_err(dev, "timeout while waiting for Formatter to Stop\n"); } @@ -286,6 +286,7 @@ static void etb_dump_hw(struct etb_drvdata *drvdata) u32 read_data, depth; u32 read_ptr, write_ptr; u32 frame_off, frame_endoff; + struct device *dev = &drvdata->csdev->dev; CS_UNLOCK(drvdata->base); @@ -295,10 +296,10 @@ static void etb_dump_hw(struct etb_drvdata *drvdata) frame_off = write_ptr % ETB_FRAME_SIZE_WORDS; frame_endoff = ETB_FRAME_SIZE_WORDS - frame_off; if (frame_off) { - dev_err(drvdata->dev, + dev_err(dev, "write_ptr: %lu not aligned to formatter frame size\n", (unsigned long)write_ptr); - dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n", + dev_err(dev, "frameoff: %lu, frame_endoff: %lu\n", (unsigned long)frame_off, (unsigned long)frame_endoff); write_ptr += frame_endoff; } @@ -365,7 +366,7 @@ static int etb_disable(struct coresight_device *csdev) drvdata->mode = CS_MODE_DISABLED; spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "ETB disabled\n"); + dev_dbg(&csdev->dev, "ETB disabled\n"); return 0; } @@ -373,12 +374,10 @@ static void *etb_alloc_buffer(struct coresight_device *csdev, struct perf_event *event, void **pages, int nr_pages, bool overwrite) { - int node, cpu = event->cpu; + int node; struct cs_buffers *buf; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); if (!buf) @@ -460,7 +459,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, * chance to fix things. */ if (write_ptr % ETB_FRAME_SIZE_WORDS) { - dev_err(drvdata->dev, + dev_err(&csdev->dev, "write_ptr: %lu not aligned to formatter frame size\n", (unsigned long)write_ptr); @@ -512,7 +511,13 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, lost = true; } - if (lost) + /* + * Don't set the TRUNCATED flag in snapshot mode because 1) the + * captured buffer is expected to be truncated and 2) a full buffer + * prevents the event from being re-enabled by the perf core, + * resulting in stale data being send to user space. + */ + if (!buf->snapshot && lost) perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); /* finally tell HW where we want to start reading from */ @@ -548,13 +553,14 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); /* - * In snapshot mode we have to update the handle->head to point - * to the new location. + * In snapshot mode we simply increment the head by the number of byte + * that were written. User space function cs_etm_find_snapshot() will + * figure out how many bytes to get from the AUX buffer based on the + * position of the head. */ - if (buf->snapshot) { - handle->head = (cur * PAGE_SIZE) + offset; - to_read = buf->nr_pages << PAGE_SHIFT; - } + if (buf->snapshot) + handle->head += to_read; + __etb_enable_hw(drvdata); CS_LOCK(drvdata->base); out: @@ -587,7 +593,7 @@ static void etb_dump(struct etb_drvdata *drvdata) } spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "ETB dumped\n"); + dev_dbg(&drvdata->csdev->dev, "ETB dumped\n"); } static int etb_open(struct inode *inode, struct file *file) @@ -598,7 +604,7 @@ static int etb_open(struct inode *inode, struct file *file) if (local_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; - dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); + dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); return 0; } @@ -608,6 +614,7 @@ static ssize_t etb_read(struct file *file, char __user *data, u32 depth; struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); + struct device *dev = &drvdata->csdev->dev; etb_dump(drvdata); @@ -616,13 +623,14 @@ static ssize_t etb_read(struct file *file, char __user *data, len = depth * 4 - *ppos; if (copy_to_user(data, drvdata->buf + *ppos, len)) { - dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); + dev_dbg(dev, + "%s: copy_to_user failed\n", __func__); return -EFAULT; } *ppos += len; - dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", + dev_dbg(dev, "%s: %zu bytes copied, %d bytes left\n", __func__, len, (int)(depth * 4 - *ppos)); return len; } @@ -633,7 +641,7 @@ static int etb_release(struct inode *inode, struct file *file) struct etb_drvdata, miscdev); local_set(&drvdata->reading, 0); - dev_dbg(drvdata->dev, "%s: released\n", __func__); + dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); return 0; } @@ -724,20 +732,15 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) struct etb_drvdata *drvdata; struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - adev->dev.platform_data = pdata; - } + desc.name = coresight_alloc_device_name(&etb_devs, dev); + if (!desc.name) + return -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = &adev->dev; drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); @@ -768,6 +771,11 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) /* This device is not associated with a session */ drvdata->pid = -1; + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + adev->dev.platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &etb_cs_ops; @@ -778,7 +786,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - drvdata->miscdev.name = pdata->name; + drvdata->miscdev.name = desc.name; drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; drvdata->miscdev.fops = &etb_fops; ret = misc_register(&drvdata->miscdev); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 3c6294432748..5c1ca0df5cb0 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -523,7 +523,7 @@ int etm_perf_add_symlink_sink(struct coresight_device *csdev) unsigned long hash; const char *name; struct device *pmu_dev = etm_pmu.dev; - struct device *pdev = csdev->dev.parent; + struct device *dev = &csdev->dev; struct dev_ext_attribute *ea; if (csdev->type != CORESIGHT_DEV_TYPE_SINK && @@ -536,15 +536,15 @@ int etm_perf_add_symlink_sink(struct coresight_device *csdev) if (!etm_perf_up) return -EPROBE_DEFER; - ea = devm_kzalloc(pdev, sizeof(*ea), GFP_KERNEL); + ea = devm_kzalloc(dev, sizeof(*ea), GFP_KERNEL); if (!ea) return -ENOMEM; - name = dev_name(pdev); + name = dev_name(dev); /* See function coresight_get_sink_by_id() to know where this is used */ hash = hashlen_hash(hashlen_string(NULL, name)); - ea->attr.attr.name = devm_kstrdup(pdev, name, GFP_KERNEL); + ea->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL); if (!ea->attr.attr.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 79e1ad860d8a..f3ab96eaf44e 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -208,7 +208,6 @@ struct etm_config { /** * struct etm_drvdata - specifics associated to an ETM component * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @atclk: optional clock for the core parts of the ETM. * @csdev: component vitals needed by the framework. * @spinlock: only one at a time pls. @@ -232,7 +231,6 @@ struct etm_config { */ struct etm_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; spinlock_t spinlock; @@ -260,7 +258,7 @@ static inline void etm_writel(struct etm_drvdata *drvdata, { if (drvdata->use_cp14) { if (etm_writel_cp14(off, val)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "invalid CP14 access to ETM reg: %#x", off); } } else { @@ -274,7 +272,7 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) if (drvdata->use_cp14) { if (etm_readl_cp14(off, &val)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "invalid CP14 access to ETM reg: %#x", off); } } else { diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 75487b3fad86..e8c7649f123e 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -48,7 +48,7 @@ static ssize_t etmsr_show(struct device *dev, unsigned long flags, val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - pm_runtime_get_sync(drvdata->dev); + pm_runtime_get_sync(dev->parent); spin_lock_irqsave(&drvdata->spinlock, flags); CS_UNLOCK(drvdata->base); @@ -56,7 +56,7 @@ static ssize_t etmsr_show(struct device *dev, CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); + pm_runtime_put(dev->parent); return sprintf(buf, "%#lx\n", val); } @@ -131,7 +131,7 @@ static ssize_t mode_store(struct device *dev, if (config->mode & ETM_MODE_STALL) { if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { - dev_warn(drvdata->dev, "stall mode not supported\n"); + dev_warn(dev, "stall mode not supported\n"); ret = -EINVAL; goto err_unlock; } @@ -141,7 +141,7 @@ static ssize_t mode_store(struct device *dev, if (config->mode & ETM_MODE_TIMESTAMP) { if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { - dev_warn(drvdata->dev, "timestamp not supported\n"); + dev_warn(dev, "timestamp not supported\n"); ret = -EINVAL; goto err_unlock; } @@ -945,7 +945,7 @@ static ssize_t seq_curr_state_show(struct device *dev, goto out; } - pm_runtime_get_sync(drvdata->dev); + pm_runtime_get_sync(dev->parent); spin_lock_irqsave(&drvdata->spinlock, flags); CS_UNLOCK(drvdata->base); @@ -953,7 +953,7 @@ static ssize_t seq_curr_state_show(struct device *dev, CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); + pm_runtime_put(dev->parent); out: return sprintf(buf, "%#lx\n", val); } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index be302ec5f66b..e2cb6873c3f2 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -165,7 +165,7 @@ static void etm_set_prog(struct etm_drvdata *drvdata) */ isb(); if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 1)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "%s: timeout observed when probing at offset %#x\n", __func__, ETMSR); } @@ -184,7 +184,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) */ isb(); if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 0)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "%s: timeout observed when probing at offset %#x\n", __func__, ETMSR); } @@ -425,7 +425,7 @@ static int etm_enable_hw(struct etm_drvdata *drvdata) done: CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d enable smp call done: %d\n", + dev_dbg(&drvdata->csdev->dev, "cpu: %d enable smp call done: %d\n", drvdata->cpu, rc); return rc; } @@ -455,14 +455,16 @@ int etm_get_trace_id(struct etm_drvdata *drvdata) { unsigned long flags; int trace_id = -1; + struct device *etm_dev; if (!drvdata) goto out; + etm_dev = drvdata->csdev->dev.parent; if (!local_read(&drvdata->mode)) return drvdata->traceid; - pm_runtime_get_sync(drvdata->dev); + pm_runtime_get_sync(etm_dev); spin_lock_irqsave(&drvdata->spinlock, flags); @@ -471,7 +473,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); + pm_runtime_put(etm_dev); out: return trace_id; @@ -526,7 +528,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); if (!ret) - dev_dbg(drvdata->dev, "ETM tracing enabled\n"); + dev_dbg(&csdev->dev, "ETM tracing enabled\n"); return ret; } @@ -581,7 +583,8 @@ static void etm_disable_hw(void *info) CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); + dev_dbg(&drvdata->csdev->dev, + "cpu: %d disable smp call done\n", drvdata->cpu); } static void etm_disable_perf(struct coresight_device *csdev) @@ -628,7 +631,7 @@ static void etm_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); - dev_dbg(drvdata->dev, "ETM tracing disabled\n"); + dev_dbg(&csdev->dev, "ETM tracing disabled\n"); } static void etm_disable(struct coresight_device *csdev, @@ -788,22 +791,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) struct etm_drvdata *drvdata; struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - adev->dev.platform_data = pdata; - drvdata->use_cp14 = of_property_read_bool(np, "arm,cp14"); - } - - drvdata->dev = &adev->dev; + drvdata->use_cp14 = fwnode_property_read_bool(dev->fwnode, "arm,cp14"); dev_set_drvdata(dev, drvdata); /* Validity for the resource is already checked by the AMBA core */ @@ -822,7 +815,13 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) return ret; } - drvdata->cpu = pdata ? pdata->cpu : 0; + drvdata->cpu = coresight_get_cpu(dev); + if (drvdata->cpu < 0) + return drvdata->cpu; + + desc.name = devm_kasprintf(dev, GFP_KERNEL, "etm%d", drvdata->cpu); + if (!desc.name) + return -ENOMEM; cpus_read_lock(); etmdrvdata[drvdata->cpu] = drvdata; @@ -852,6 +851,13 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) etm_init_trace_id(drvdata); etm_set_default(&drvdata->config); + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_arch_supported; + } + adev->dev.platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SOURCE; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc.ops = &etm_cs_ops; @@ -871,7 +877,8 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) } pm_runtime_put(&adev->dev); - dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); + dev_info(&drvdata->csdev->dev, + "%s initialized\n", (char *)coresight_get_uci_data(id)); if (boot_enable) { coresight_enable(drvdata->csdev); drvdata->boot_enable = true; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 8bb0092c7ec2..7bcac8896fc1 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -88,6 +88,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) { int i, rc; struct etmv4_config *config = &drvdata->config; + struct device *etm_dev = &drvdata->csdev->dev; CS_UNLOCK(drvdata->base); @@ -102,7 +103,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) /* wait for TRCSTATR.IDLE to go up */ if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) - dev_err(drvdata->dev, + dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); @@ -184,13 +185,13 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) /* wait for TRCSTATR.IDLE to go back down to '0' */ if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0)) - dev_err(drvdata->dev, + dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); done: CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d enable smp call done: %d\n", + dev_dbg(etm_dev, "cpu: %d enable smp call done: %d\n", drvdata->cpu, rc); return rc; } @@ -400,7 +401,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); if (!ret) - dev_dbg(drvdata->dev, "ETM tracing enabled\n"); + dev_dbg(&csdev->dev, "ETM tracing enabled\n"); return ret; } @@ -461,7 +462,8 @@ static void etm4_disable_hw(void *info) CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); + dev_dbg(&drvdata->csdev->dev, + "cpu: %d disable smp call done\n", drvdata->cpu); } static int etm4_disable_perf(struct coresight_device *csdev, @@ -511,7 +513,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); - dev_dbg(drvdata->dev, "ETM tracing disabled\n"); + dev_dbg(&csdev->dev, "ETM tracing disabled\n"); } static void etm4_disable(struct coresight_device *csdev, @@ -1082,20 +1084,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) struct etmv4_drvdata *drvdata; struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - adev->dev.platform_data = pdata; - } - - drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); /* Validity for the resource is already checked by the AMBA core */ @@ -1107,7 +1100,13 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&drvdata->spinlock); - drvdata->cpu = pdata ? pdata->cpu : 0; + drvdata->cpu = coresight_get_cpu(dev); + if (drvdata->cpu < 0) + return drvdata->cpu; + + desc.name = devm_kasprintf(dev, GFP_KERNEL, "etm%d", drvdata->cpu); + if (!desc.name) + return -ENOMEM; cpus_read_lock(); etmdrvdata[drvdata->cpu] = drvdata; @@ -1138,6 +1137,13 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) etm4_init_trace_id(drvdata); etm4_set_default(&drvdata->config); + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_arch_supported; + } + adev->dev.platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SOURCE; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc.ops = &etm4_cs_ops; @@ -1157,7 +1163,7 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) } pm_runtime_put(&adev->dev); - dev_info(dev, "CPU%d: ETM v%d.%d initialized\n", + dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf); if (boot_enable) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 52786e9d8926..4523f10ddd0f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -284,7 +284,6 @@ struct etmv4_config { /** * struct etm4_drvdata - specifics associated to an ETM component * @base: Memory mapped base address for this component. - * @dev: The device entity associated to this component. * @csdev: Component vitals needed by the framework. * @spinlock: Only one at a time pls. * @mode: This tracer's mode, i.e sysFS, Perf or disabled. @@ -340,7 +339,6 @@ struct etmv4_config { */ struct etmv4_drvdata { void __iomem *base; - struct device *dev; struct coresight_device *csdev; spinlock_t spinlock; local_t mode; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 16b0c0e1e43a..fa97cb9ab4f9 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -29,17 +29,17 @@ #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) #define FUNNEL_ENSx_MASK 0xff +DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel"); + /** * struct funnel_drvdata - specifics associated to a funnel component * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @atclk: optional clock for the core parts of the funnel. * @csdev: component vitals needed by the framework. * @priority: port selection order. */ struct funnel_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; unsigned long priority; @@ -80,7 +80,7 @@ static int funnel_enable(struct coresight_device *csdev, int inport, rc = dynamic_funnel_enable_hw(drvdata, inport); if (!rc) - dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport); + dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport); return rc; } @@ -110,7 +110,7 @@ static void funnel_disable(struct coresight_device *csdev, int inport, if (drvdata->base) dynamic_funnel_disable_hw(drvdata, inport); - dev_dbg(drvdata->dev, "FUNNEL inport %d disabled\n", inport); + dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport); } static const struct coresight_ops_link funnel_link_ops = { @@ -165,11 +165,11 @@ static ssize_t funnel_ctrl_show(struct device *dev, u32 val; struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); - pm_runtime_get_sync(drvdata->dev); + pm_runtime_get_sync(dev->parent); val = get_funnel_ctrl_hw(drvdata); - pm_runtime_put(drvdata->dev); + pm_runtime_put(dev->parent); return sprintf(buf, "%#x\n", val); } @@ -189,23 +189,19 @@ static int funnel_probe(struct device *dev, struct resource *res) struct coresight_platform_data *pdata = NULL; struct funnel_drvdata *drvdata; struct coresight_desc desc = { 0 }; - struct device_node *np = dev->of_node; - - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - dev->platform_data = pdata; - } - if (of_device_is_compatible(np, "arm,coresight-funnel")) + if (is_of_node(dev_fwnode(dev)) && + of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) pr_warn_once("Uses OBSOLETE CoreSight funnel binding\n"); + desc.name = coresight_alloc_device_name(&funnel_devs, dev); + if (!desc.name) + return -ENOMEM; + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = dev; drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); @@ -229,6 +225,13 @@ static int funnel_probe(struct device *dev, struct resource *res) dev_set_drvdata(dev, drvdata); + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto out_disable_clk; + } + dev->platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_LINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; desc.ops = &funnel_cs_ops; @@ -241,6 +244,7 @@ static int funnel_probe(struct device *dev, struct resource *res) } pm_runtime_put(dev); + ret = 0; out_disable_clk: if (ret && !IS_ERR_OR_NULL(drvdata->atclk)) diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c new file mode 100644 index 000000000000..cf580ffbc27c --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -0,0 +1,815 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + */ + +#include <linux/acpi.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/amba/bus.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <asm/smp_plat.h> + +#include "coresight-priv.h" +/* + * coresight_alloc_conns: Allocate connections record for each output + * port from the device. + */ +static int coresight_alloc_conns(struct device *dev, + struct coresight_platform_data *pdata) +{ + if (pdata->nr_outport) { + pdata->conns = devm_kzalloc(dev, pdata->nr_outport * + sizeof(*pdata->conns), + GFP_KERNEL); + if (!pdata->conns) + return -ENOMEM; + } + + return 0; +} + +int coresight_device_fwnode_match(struct device *dev, void *fwnode) +{ + return dev_fwnode(dev) == fwnode; +} + +static struct device * +coresight_find_device_by_fwnode(struct fwnode_handle *fwnode) +{ + struct device *dev = NULL; + + /* + * If we have a non-configurable replicator, it will be found on the + * platform bus. + */ + dev = bus_find_device(&platform_bus_type, NULL, + fwnode, coresight_device_fwnode_match); + if (dev) + return dev; + + /* + * We have a configurable component - circle through the AMBA bus + * looking for the device that matches the endpoint node. + */ + return bus_find_device(&amba_bustype, NULL, + fwnode, coresight_device_fwnode_match); +} + +#ifdef CONFIG_OF +static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep) +{ + return of_property_read_bool(ep, "slave-mode"); +} + +static void of_coresight_get_ports_legacy(const struct device_node *node, + int *nr_inport, int *nr_outport) +{ + struct device_node *ep = NULL; + int in = 0, out = 0; + + do { + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + break; + + if (of_coresight_legacy_ep_is_input(ep)) + in++; + else + out++; + + } while (ep); + + *nr_inport = in; + *nr_outport = out; +} + +static struct device_node *of_coresight_get_port_parent(struct device_node *ep) +{ + struct device_node *parent = of_graph_get_port_parent(ep); + + /* + * Skip one-level up to the real device node, if we + * are using the new bindings. + */ + if (of_node_name_eq(parent, "in-ports") || + of_node_name_eq(parent, "out-ports")) + parent = of_get_next_parent(parent); + + return parent; +} + +static inline struct device_node * +of_coresight_get_input_ports_node(const struct device_node *node) +{ + return of_get_child_by_name(node, "in-ports"); +} + +static inline struct device_node * +of_coresight_get_output_ports_node(const struct device_node *node) +{ + return of_get_child_by_name(node, "out-ports"); +} + +static inline int +of_coresight_count_ports(struct device_node *port_parent) +{ + int i = 0; + struct device_node *ep = NULL; + + while ((ep = of_graph_get_next_endpoint(port_parent, ep))) + i++; + return i; +} + +static void of_coresight_get_ports(const struct device_node *node, + int *nr_inport, int *nr_outport) +{ + struct device_node *input_ports = NULL, *output_ports = NULL; + + input_ports = of_coresight_get_input_ports_node(node); + output_ports = of_coresight_get_output_ports_node(node); + + if (input_ports || output_ports) { + if (input_ports) { + *nr_inport = of_coresight_count_ports(input_ports); + of_node_put(input_ports); + } + if (output_ports) { + *nr_outport = of_coresight_count_ports(output_ports); + of_node_put(output_ports); + } + } else { + /* Fall back to legacy DT bindings parsing */ + of_coresight_get_ports_legacy(node, nr_inport, nr_outport); + } +} + +static int of_coresight_get_cpu(struct device *dev) +{ + int cpu; + struct device_node *dn; + + if (!dev->of_node) + return -ENODEV; + + dn = of_parse_phandle(dev->of_node, "cpu", 0); + if (!dn) + return -ENODEV; + + cpu = of_cpu_node_to_id(dn); + of_node_put(dn); + + return cpu; +} + +/* + * of_coresight_parse_endpoint : Parse the given output endpoint @ep + * and fill the connection information in @conn + * + * Parses the local port, remote device name and the remote port. + * + * Returns : + * 1 - If the parsing is successful and a connection record + * was created for an output connection. + * 0 - If the parsing completed without any fatal errors. + * -Errno - Fatal error, abort the scanning. + */ +static int of_coresight_parse_endpoint(struct device *dev, + struct device_node *ep, + struct coresight_connection *conn) +{ + int ret = 0; + struct of_endpoint endpoint, rendpoint; + struct device_node *rparent = NULL; + struct device_node *rep = NULL; + struct device *rdev = NULL; + struct fwnode_handle *rdev_fwnode; + + do { + /* Parse the local port details */ + if (of_graph_parse_endpoint(ep, &endpoint)) + break; + /* + * Get a handle on the remote endpoint and the device it is + * attached to. + */ + rep = of_graph_get_remote_endpoint(ep); + if (!rep) + break; + rparent = of_coresight_get_port_parent(rep); + if (!rparent) + break; + if (of_graph_parse_endpoint(rep, &rendpoint)) + break; + + rdev_fwnode = of_fwnode_handle(rparent); + /* If the remote device is not available, defer probing */ + rdev = coresight_find_device_by_fwnode(rdev_fwnode); + if (!rdev) { + ret = -EPROBE_DEFER; + break; + } + + conn->outport = endpoint.port; + /* + * Hold the refcount to the target device. This could be + * released via: + * 1) coresight_release_platform_data() if the probe fails or + * this device is unregistered. + * 2) While removing the target device via + * coresight_remove_match() + */ + conn->child_fwnode = fwnode_handle_get(rdev_fwnode); + conn->child_port = rendpoint.port; + /* Connection record updated */ + ret = 1; + } while (0); + + of_node_put(rparent); + of_node_put(rep); + put_device(rdev); + + return ret; +} + +static int of_get_coresight_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + int ret = 0; + struct coresight_connection *conn; + struct device_node *ep = NULL; + const struct device_node *parent = NULL; + bool legacy_binding = false; + struct device_node *node = dev->of_node; + + /* Get the number of input and output port for this component */ + of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport); + + /* If there are no output connections, we are done */ + if (!pdata->nr_outport) + return 0; + + ret = coresight_alloc_conns(dev, pdata); + if (ret) + return ret; + + parent = of_coresight_get_output_ports_node(node); + /* + * If the DT uses obsoleted bindings, the ports are listed + * under the device and we need to filter out the input + * ports. + */ + if (!parent) { + legacy_binding = true; + parent = node; + dev_warn_once(dev, "Uses obsolete Coresight DT bindings\n"); + } + + conn = pdata->conns; + + /* Iterate through each output port to discover topology */ + while ((ep = of_graph_get_next_endpoint(parent, ep))) { + /* + * Legacy binding mixes input/output ports under the + * same parent. So, skip the input ports if we are dealing + * with legacy binding, as they processed with their + * connected output ports. + */ + if (legacy_binding && of_coresight_legacy_ep_is_input(ep)) + continue; + + ret = of_coresight_parse_endpoint(dev, ep, conn); + switch (ret) { + case 1: + conn++; /* Fall through */ + case 0: + break; + default: + return ret; + } + } + + return 0; +} +#else +static inline int +of_get_coresight_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + return -ENOENT; +} + +static inline int of_coresight_get_cpu(struct device *dev) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_ACPI + +#include <acpi/actypes.h> +#include <acpi/processor.h> + +/* ACPI Graph _DSD UUID : "ab02a46b-74c7-45a2-bd68-f7d344ef2153" */ +static const guid_t acpi_graph_uuid = GUID_INIT(0xab02a46b, 0x74c7, 0x45a2, + 0xbd, 0x68, 0xf7, 0xd3, + 0x44, 0xef, 0x21, 0x53); +/* Coresight ACPI Graph UUID : "3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd" */ +static const guid_t coresight_graph_uuid = GUID_INIT(0x3ecbc8b6, 0x1d0e, 0x4fb3, + 0x81, 0x07, 0xe6, 0x27, + 0xf8, 0x05, 0xc6, 0xcd); +#define ACPI_CORESIGHT_LINK_SLAVE 0 +#define ACPI_CORESIGHT_LINK_MASTER 1 + +static inline bool is_acpi_guid(const union acpi_object *obj) +{ + return (obj->type == ACPI_TYPE_BUFFER) && (obj->buffer.length == 16); +} + +/* + * acpi_guid_matches - Checks if the given object is a GUID object and + * that it matches the supplied the GUID. + */ +static inline bool acpi_guid_matches(const union acpi_object *obj, + const guid_t *guid) +{ + return is_acpi_guid(obj) && + guid_equal((guid_t *)obj->buffer.pointer, guid); +} + +static inline bool is_acpi_dsd_graph_guid(const union acpi_object *obj) +{ + return acpi_guid_matches(obj, &acpi_graph_uuid); +} + +static inline bool is_acpi_coresight_graph_guid(const union acpi_object *obj) +{ + return acpi_guid_matches(obj, &coresight_graph_uuid); +} + +static inline bool is_acpi_coresight_graph(const union acpi_object *obj) +{ + const union acpi_object *graphid, *guid, *links; + + if (obj->type != ACPI_TYPE_PACKAGE || + obj->package.count < 3) + return false; + + graphid = &obj->package.elements[0]; + guid = &obj->package.elements[1]; + links = &obj->package.elements[2]; + + if (graphid->type != ACPI_TYPE_INTEGER || + links->type != ACPI_TYPE_INTEGER) + return false; + + return is_acpi_coresight_graph_guid(guid); +} + +/* + * acpi_validate_dsd_graph - Make sure the given _DSD graph conforms + * to the ACPI _DSD Graph specification. + * + * ACPI Devices Graph property has the following format: + * { + * Revision - Integer, must be 0 + * NumberOfGraphs - Integer, N indicating the following list. + * Graph[1], + * ... + * Graph[N] + * } + * + * And each Graph entry has the following format: + * { + * GraphID - Integer, identifying a graph the device belongs to. + * UUID - UUID identifying the specification that governs + * this graph. (e.g, see is_acpi_coresight_graph()) + * NumberOfLinks - Number "N" of connections on this node of the graph. + * Links[1] + * ... + * Links[N] + * } + * + * Where each "Links" entry has the following format: + * + * { + * SourcePortAddress - Integer + * DestinationPortAddress - Integer + * DestinationDeviceName - Reference to another device + * ( --- CoreSight specific extensions below ---) + * DirectionOfFlow - Integer 1 for output(master) + * 0 for input(slave) + * } + * + * e.g: + * For a Funnel device + * + * Device(MFUN) { + * ... + * + * Name (_DSD, Package() { + * // DSD Package contains tuples of { Proeprty_Type_UUID, Package() } + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), //Std. Property UUID + * Package() { + * Package(2) { "property-name", <property-value> } + * }, + * + * ToUUID("ab02a46b-74c7-45a2-bd68-f7d344ef2153"), // ACPI Graph UUID + * Package() { + * 0, // Revision + * 1, // NumberOfGraphs. + * Package() { // Graph[0] Package + * 1, // GraphID + * // Coresight Graph UUID + * ToUUID("3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd"), + * 3, // NumberOfLinks aka ports + * // Link[0]: Output_0 -> Replicator:Input_0 + * Package () { 0, 0, \_SB_.RPL0, 1 }, + * // Link[1]: Input_0 <- Cluster0_Funnel0:Output_0 + * Package () { 0, 0, \_SB_.CLU0.FUN0, 0 }, + * // Link[2]: Input_1 <- Cluster1_Funnel0:Output_0 + * Package () { 1, 0, \_SB_.CLU1.FUN0, 0 }, + * } // End of Graph[0] Package + * + * }, // End of ACPI Graph Property + * }) + */ +static inline bool acpi_validate_dsd_graph(const union acpi_object *graph) +{ + int i, n; + const union acpi_object *rev, *nr_graphs; + + /* The graph must contain at least the Revision and Number of Graphs */ + if (graph->package.count < 2) + return false; + + rev = &graph->package.elements[0]; + nr_graphs = &graph->package.elements[1]; + + if (rev->type != ACPI_TYPE_INTEGER || + nr_graphs->type != ACPI_TYPE_INTEGER) + return false; + + /* We only support revision 0 */ + if (rev->integer.value != 0) + return false; + + n = nr_graphs->integer.value; + /* CoreSight devices are only part of a single Graph */ + if (n != 1) + return false; + + /* Make sure the ACPI graph package has right number of elements */ + if (graph->package.count != (n + 2)) + return false; + + /* + * Each entry must be a graph package with at least 3 members : + * { GraphID, UUID, NumberOfLinks(n), Links[.],... } + */ + for (i = 2; i < n + 2; i++) { + const union acpi_object *obj = &graph->package.elements[i]; + + if (obj->type != ACPI_TYPE_PACKAGE || + obj->package.count < 3) + return false; + } + + return true; +} + +/* acpi_get_dsd_graph - Find the _DSD Graph property for the given device. */ +const union acpi_object * +acpi_get_dsd_graph(struct acpi_device *adev) +{ + int i; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_status status; + const union acpi_object *dsd; + + status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, + &buf, ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return NULL; + + dsd = buf.pointer; + + /* + * _DSD property consists tuples { Prop_UUID, Package() } + * Iterate through all the packages and find the Graph. + */ + for (i = 0; i + 1 < dsd->package.count; i += 2) { + const union acpi_object *guid, *package; + + guid = &dsd->package.elements[i]; + package = &dsd->package.elements[i + 1]; + + /* All _DSD elements must have a UUID and a Package */ + if (!is_acpi_guid(guid) || package->type != ACPI_TYPE_PACKAGE) + break; + /* Skip the non-Graph _DSD packages */ + if (!is_acpi_dsd_graph_guid(guid)) + continue; + if (acpi_validate_dsd_graph(package)) + return package; + /* Invalid graph format, continue */ + dev_warn(&adev->dev, "Invalid Graph _DSD property\n"); + } + + return NULL; +} + +static inline bool +acpi_validate_coresight_graph(const union acpi_object *cs_graph) +{ + int nlinks; + + nlinks = cs_graph->package.elements[2].integer.value; + /* + * Graph must have the following fields : + * { GraphID, GraphUUID, NumberOfLinks, Links... } + */ + if (cs_graph->package.count != (nlinks + 3)) + return false; + /* The links are validated in acpi_coresight_parse_link() */ + return true; +} + +/* + * acpi_get_coresight_graph - Parse the device _DSD tables and find + * the Graph property matching the CoreSight Graphs. + * + * Returns the pointer to the CoreSight Graph Package when found. Otherwise + * returns NULL. + */ +const union acpi_object * +acpi_get_coresight_graph(struct acpi_device *adev) +{ + const union acpi_object *graph_list, *graph; + int i, nr_graphs; + + graph_list = acpi_get_dsd_graph(adev); + if (!graph_list) + return graph_list; + + nr_graphs = graph_list->package.elements[1].integer.value; + + for (i = 2; i < nr_graphs + 2; i++) { + graph = &graph_list->package.elements[i]; + if (!is_acpi_coresight_graph(graph)) + continue; + if (acpi_validate_coresight_graph(graph)) + return graph; + /* Invalid graph format */ + break; + } + + return NULL; +} + +/* + * acpi_coresight_parse_link - Parse the given Graph connection + * of the device and populate the coresight_connection for an output + * connection. + * + * CoreSight Graph specification mandates that the direction of the data + * flow must be specified in the link. i.e, + * + * SourcePortAddress, // Integer + * DestinationPortAddress, // Integer + * DestinationDeviceName, // Reference to another device + * DirectionOfFlow, // 1 for output(master), 0 for input(slave) + * + * Returns the direction of the data flow [ Input(slave) or Output(master) ] + * upon success. + * Returns an negative error number otherwise. + */ +static int acpi_coresight_parse_link(struct acpi_device *adev, + const union acpi_object *link, + struct coresight_connection *conn) +{ + int rc, dir; + const union acpi_object *fields; + struct acpi_device *r_adev; + struct device *rdev; + + if (link->type != ACPI_TYPE_PACKAGE || + link->package.count != 4) + return -EINVAL; + + fields = link->package.elements; + + if (fields[0].type != ACPI_TYPE_INTEGER || + fields[1].type != ACPI_TYPE_INTEGER || + fields[2].type != ACPI_TYPE_LOCAL_REFERENCE || + fields[3].type != ACPI_TYPE_INTEGER) + return -EINVAL; + + rc = acpi_bus_get_device(fields[2].reference.handle, &r_adev); + if (rc) + return rc; + + dir = fields[3].integer.value; + if (dir == ACPI_CORESIGHT_LINK_MASTER) { + conn->outport = fields[0].integer.value; + conn->child_port = fields[1].integer.value; + rdev = coresight_find_device_by_fwnode(&r_adev->fwnode); + if (!rdev) + return -EPROBE_DEFER; + /* + * Hold the refcount to the target device. This could be + * released via: + * 1) coresight_release_platform_data() if the probe fails or + * this device is unregistered. + * 2) While removing the target device via + * coresight_remove_match(). + */ + conn->child_fwnode = fwnode_handle_get(&r_adev->fwnode); + } + + return dir; +} + +/* + * acpi_coresight_parse_graph - Parse the _DSD CoreSight graph + * connection information and populate the supplied coresight_platform_data + * instance. + */ +static int acpi_coresight_parse_graph(struct acpi_device *adev, + struct coresight_platform_data *pdata) +{ + int rc, i, nlinks; + const union acpi_object *graph; + struct coresight_connection *conns, *ptr; + + pdata->nr_inport = pdata->nr_outport = 0; + graph = acpi_get_coresight_graph(adev); + if (!graph) + return -ENOENT; + + nlinks = graph->package.elements[2].integer.value; + if (!nlinks) + return 0; + + /* + * To avoid scanning the table twice (once for finding the number of + * output links and then later for parsing the output links), + * cache the links information in one go and then later copy + * it to the pdata. + */ + conns = devm_kcalloc(&adev->dev, nlinks, sizeof(*conns), GFP_KERNEL); + if (!conns) + return -ENOMEM; + ptr = conns; + for (i = 0; i < nlinks; i++) { + const union acpi_object *link = &graph->package.elements[3 + i]; + int dir; + + dir = acpi_coresight_parse_link(adev, link, ptr); + if (dir < 0) + return dir; + + if (dir == ACPI_CORESIGHT_LINK_MASTER) { + pdata->nr_outport++; + ptr++; + } else { + pdata->nr_inport++; + } + } + + rc = coresight_alloc_conns(&adev->dev, pdata); + if (rc) + return rc; + + /* Copy the connection information to the final location */ + for (i = 0; i < pdata->nr_outport; i++) + pdata->conns[i] = conns[i]; + + devm_kfree(&adev->dev, conns); + return 0; +} + +/* + * acpi_handle_to_logical_cpuid - Map a given acpi_handle to the + * logical CPU id of the corresponding CPU device. + * + * Returns the logical CPU id when found. Otherwise returns >= nr_cpus_id. + */ +static int +acpi_handle_to_logical_cpuid(acpi_handle handle) +{ + int i; + struct acpi_processor *pr; + + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (pr && pr->handle == handle) + break; + } + + return i; +} + +/* + * acpi_coresigh_get_cpu - Find the logical CPU id of the CPU associated + * with this coresight device. With ACPI bindings, the CoreSight components + * are listed as child device of the associated CPU. + * + * Returns the logical CPU id when found. Otherwise returns 0. + */ +static int acpi_coresight_get_cpu(struct device *dev) +{ + int cpu; + acpi_handle cpu_handle; + acpi_status status; + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (!adev) + return -ENODEV; + status = acpi_get_parent(adev->handle, &cpu_handle); + if (ACPI_FAILURE(status)) + return -ENODEV; + + cpu = acpi_handle_to_logical_cpuid(cpu_handle); + if (cpu >= nr_cpu_ids) + return -ENODEV; + return cpu; +} + +static int +acpi_get_coresight_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -EINVAL; + + return acpi_coresight_parse_graph(adev, pdata); +} + +#else + +static inline int +acpi_get_coresight_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + return -ENOENT; +} + +static inline int acpi_coresight_get_cpu(struct device *dev) +{ + return -ENODEV; +} +#endif + +int coresight_get_cpu(struct device *dev) +{ + if (is_of_node(dev->fwnode)) + return of_coresight_get_cpu(dev); + else if (is_acpi_device_node(dev->fwnode)) + return acpi_coresight_get_cpu(dev); + return 0; +} +EXPORT_SYMBOL_GPL(coresight_get_cpu); + +struct coresight_platform_data * +coresight_get_platform_data(struct device *dev) +{ + int ret = -ENOENT; + struct coresight_platform_data *pdata = NULL; + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (IS_ERR_OR_NULL(fwnode)) + goto error; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto error; + } + + if (is_of_node(fwnode)) + ret = of_get_coresight_platform_data(dev, pdata); + else if (is_acpi_device_node(fwnode)) + ret = acpi_get_coresight_platform_data(dev, pdata); + + if (!ret) + return pdata; +error: + if (!IS_ERR_OR_NULL(pdata)) + /* Cleanup the connection information */ + coresight_release_platform_data(pdata); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(coresight_get_platform_data); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8b07fe55395a 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -200,4 +200,8 @@ static inline void *coresight_get_uci_data(const struct amba_id *id) return 0; } +void coresight_release_platform_data(struct coresight_platform_data *pdata); + +int coresight_device_fwnode_match(struct device *dev, void *fwnode); + #endif diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 8c9ce74498e1..b7d6d59d56db 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -5,6 +5,7 @@ * Description: CoreSight Replicator driver */ +#include <linux/acpi.h> #include <linux/amba/bus.h> #include <linux/kernel.h> #include <linux/device.h> @@ -22,17 +23,17 @@ #define REPLICATOR_IDFILTER0 0x000 #define REPLICATOR_IDFILTER1 0x004 +DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); + /** * struct replicator_drvdata - specifics associated to a replicator component * @base: memory mapped base address for this component. Also indicates * whether this one is programmable or not. - * @dev: the device entity associated with this component * @atclk: optional clock for the core parts of the replicator. * @csdev: component vitals needed by the framework */ struct replicator_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; }; @@ -100,7 +101,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport, if (drvdata->base) rc = dynamic_replicator_enable(drvdata, inport, outport); if (!rc) - dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); + dev_dbg(&csdev->dev, "REPLICATOR enabled\n"); return rc; } @@ -139,7 +140,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport, if (drvdata->base) dynamic_replicator_disable(drvdata, inport, outport); - dev_dbg(drvdata->dev, "REPLICATOR disabled\n"); + dev_dbg(&csdev->dev, "REPLICATOR disabled\n"); } static const struct coresight_ops_link replicator_link_ops = { @@ -179,24 +180,20 @@ static int replicator_probe(struct device *dev, struct resource *res) struct coresight_platform_data *pdata = NULL; struct replicator_drvdata *drvdata; struct coresight_desc desc = { 0 }; - struct device_node *np = dev->of_node; void __iomem *base; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - dev->platform_data = pdata; - } - - if (of_device_is_compatible(np, "arm,coresight-replicator")) + if (is_of_node(dev_fwnode(dev)) && + of_device_is_compatible(dev->of_node, "arm,coresight-replicator")) pr_warn_once("Uses OBSOLETE CoreSight replicator binding\n"); + desc.name = coresight_alloc_device_name(&replicator_devs, dev); + if (!desc.name) + return -ENOMEM; + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = dev; drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); @@ -220,11 +217,19 @@ static int replicator_probe(struct device *dev, struct resource *res) dev_set_drvdata(dev, drvdata); + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto out_disable_clk; + } + dev->platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_LINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; desc.ops = &replicator_cs_ops; desc.pdata = dev->platform_data; desc.dev = dev; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); @@ -292,11 +297,19 @@ static const struct of_device_id static_replicator_match[] = { {} }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id static_replicator_acpi_ids[] = { + {"ARMHC985", 0}, /* ARM CoreSight Static Replicator */ + {} +}; +#endif + static struct platform_driver static_replicator_driver = { .probe = static_replicator_probe, .driver = { .name = "coresight-static-replicator", - .of_match_table = static_replicator_match, + .of_match_table = of_match_ptr(static_replicator_match), + .acpi_match_table = ACPI_PTR(static_replicator_acpi_ids), .pm = &replicator_dev_pm_ops, .suppress_bind_attrs = true, }, diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 9f8a844ed7aa..b908ca104645 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -16,6 +16,7 @@ * (C) 2015-2016 Chunyan Zhang <zhang.chunyan@linaro.org> */ #include <asm/local.h> +#include <linux/acpi.h> #include <linux/amba/bus.h> #include <linux/bitmap.h> #include <linux/clk.h> @@ -107,10 +108,11 @@ struct channel_space { unsigned long *guaranteed; }; +DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm"); + /** * struct stm_drvdata - specifics associated to an STM component * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @atclk: optional clock for the core parts of the STM. * @csdev: component vitals needed by the framework. * @spinlock: only one at a time pls. @@ -128,7 +130,6 @@ struct channel_space { */ struct stm_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; spinlock_t spinlock; @@ -205,13 +206,13 @@ static int stm_enable(struct coresight_device *csdev, if (val) return -EBUSY; - pm_runtime_get_sync(drvdata->dev); + pm_runtime_get_sync(csdev->dev.parent); spin_lock(&drvdata->spinlock); stm_enable_hw(drvdata); spin_unlock(&drvdata->spinlock); - dev_dbg(drvdata->dev, "STM tracing enabled\n"); + dev_dbg(&csdev->dev, "STM tracing enabled\n"); return 0; } @@ -271,10 +272,10 @@ static void stm_disable(struct coresight_device *csdev, /* Wait until the engine has completely stopped */ coresight_timeout(drvdata->base, STMTCSR, STMTCSR_BUSY_BIT, 0); - pm_runtime_put(drvdata->dev); + pm_runtime_put(csdev->dev.parent); local_set(&drvdata->mode, CS_MODE_DISABLED); - dev_dbg(drvdata->dev, "STM tracing disabled\n"); + dev_dbg(&csdev->dev, "STM tracing disabled\n"); } } @@ -685,14 +686,15 @@ static const struct attribute_group *coresight_stm_groups[] = { NULL, }; -static int stm_get_resource_byname(struct device_node *np, - char *ch_base, struct resource *res) +#ifdef CONFIG_OF +static int of_stm_get_stimulus_area(struct device *dev, struct resource *res) { const char *name = NULL; int index = 0, found = 0; + struct device_node *np = dev->of_node; while (!of_property_read_string_index(np, "reg-names", index, &name)) { - if (strcmp(ch_base, name)) { + if (strcmp("stm-stimulus-base", name)) { index++; continue; } @@ -707,6 +709,70 @@ static int stm_get_resource_byname(struct device_node *np, return of_address_to_resource(np, index, res); } +#else +static inline int of_stm_get_stimulus_area(struct device *dev, + struct resource *res) +{ + return -ENOENT; +} +#endif + +#ifdef CONFIG_ACPI +static int acpi_stm_get_stimulus_area(struct device *dev, struct resource *res) +{ + int rc; + bool found_base = false; + struct resource_entry *rent; + LIST_HEAD(res_list); + + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (!adev) + return -ENODEV; + rc = acpi_dev_get_resources(adev, &res_list, NULL, NULL); + if (rc < 0) + return rc; + + /* + * The stimulus base for STM device must be listed as the second memory + * resource, followed by the programming base address as described in + * "Section 2.3 Resources" in ACPI for CoreSightTM 1.0 Platform Design + * document (DEN0067). + */ + rc = -ENOENT; + list_for_each_entry(rent, &res_list, node) { + if (resource_type(rent->res) != IORESOURCE_MEM) + continue; + if (found_base) { + *res = *rent->res; + rc = 0; + break; + } + + found_base = true; + } + + acpi_dev_free_resource_list(&res_list); + return rc; +} +#else +static inline int acpi_stm_get_stimulus_area(struct device *dev, + struct resource *res) +{ + return -ENOENT; +} +#endif + +static int stm_get_stimulus_area(struct device *dev, struct resource *res) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (is_of_node(fwnode)) + return of_stm_get_stimulus_area(dev, res); + else if (is_acpi_node(fwnode)) + return acpi_stm_get_stimulus_area(dev, res); + return -ENOENT; +} static u32 stm_fundamental_data_size(struct stm_drvdata *drvdata) { @@ -763,9 +829,10 @@ static void stm_init_default_data(struct stm_drvdata *drvdata) bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp); } -static void stm_init_generic_data(struct stm_drvdata *drvdata) +static void stm_init_generic_data(struct stm_drvdata *drvdata, + const char *name) { - drvdata->stm.name = dev_name(drvdata->dev); + drvdata->stm.name = name; /* * MasterIDs are assigned at HW design phase. As such the core is @@ -795,19 +862,15 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) struct resource ch_res; size_t bitmap_size; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - adev->dev.platform_data = pdata; - } + desc.name = coresight_alloc_device_name(&stm_devs, dev); + if (!desc.name) + return -ENOMEM; + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = &adev->dev; drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); @@ -821,7 +884,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(base); drvdata->base = base; - ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); + ret = stm_get_stimulus_area(dev, &ch_res); if (ret) return ret; drvdata->chs.phys = ch_res.start; @@ -848,14 +911,22 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&drvdata->spinlock); stm_init_default_data(drvdata); - stm_init_generic_data(drvdata); + stm_init_generic_data(drvdata, desc.name); if (stm_register_device(dev, &drvdata->stm, THIS_MODULE)) { dev_info(dev, - "stm_register_device failed, probing deferred\n"); + "%s : stm_register_device failed, probing deferred\n", + desc.name); return -EPROBE_DEFER; } + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto stm_unregister; + } + adev->dev.platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SOURCE; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE; desc.ops = &stm_cs_ops; @@ -870,7 +941,8 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); + dev_info(&drvdata->csdev->dev, "%s initialized\n", + (char *)coresight_get_uci_data(id)); return 0; stm_unregister: diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 2527b5d3b65e..23b7ff00af5c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -280,7 +280,6 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode, void *data) { int ret; - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); switch (mode) { case CS_MODE_SYSFS: @@ -298,7 +297,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, if (ret) return ret; - dev_dbg(drvdata->dev, "TMC-ETB/ETF enabled\n"); + dev_dbg(&csdev->dev, "TMC-ETB/ETF enabled\n"); return 0; } @@ -328,7 +327,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "TMC-ETB/ETF disabled\n"); + dev_dbg(&csdev->dev, "TMC-ETB/ETF disabled\n"); return 0; } @@ -351,7 +350,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, spin_unlock_irqrestore(&drvdata->spinlock, flags); if (!ret) - dev_dbg(drvdata->dev, "TMC-ETF enabled\n"); + dev_dbg(&csdev->dev, "TMC-ETF enabled\n"); return ret; } @@ -371,19 +370,17 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, drvdata->mode = CS_MODE_DISABLED; spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "TMC-ETF disabled\n"); + dev_dbg(&csdev->dev, "TMC-ETF disabled\n"); } static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, struct perf_event *event, void **pages, int nr_pages, bool overwrite) { - int node, cpu = event->cpu; + int node; struct cs_buffers *buf; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); /* Allocate memory structure for interaction with Perf */ buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); @@ -477,9 +474,11 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, /* * The TMC RAM buffer may be bigger than the space available in the * perf ring buffer (handle->size). If so advance the RRP so that we - * get the latest trace data. + * get the latest trace data. In snapshot mode none of that matters + * since we are expected to clobber stale data in favour of the latest + * traces. */ - if (to_read > handle->size) { + if (!buf->snapshot && to_read > handle->size) { u32 mask = 0; /* @@ -516,7 +515,13 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, lost = true; } - if (lost) + /* + * Don't set the TRUNCATED flag in snapshot mode because 1) the + * captured buffer is expected to be truncated and 2) a full buffer + * prevents the event from being re-enabled by the perf core, + * resulting in stale data being send to user space. + */ + if (!buf->snapshot && lost) perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); cur = buf->cur; @@ -542,11 +547,15 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, } } - /* In snapshot mode we have to update the head */ - if (buf->snapshot) { - handle->head = (cur * PAGE_SIZE) + offset; - to_read = buf->nr_pages << PAGE_SHIFT; - } + /* + * In snapshot mode we simply increment the head by the number of byte + * that were written. User space function cs_etm_find_snapshot() will + * figure out how many bytes to get from the AUX buffer based on the + * position of the head. + */ + if (buf->snapshot) + handle->head += to_read; + CS_LOCK(drvdata->base); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index df6e4b0b84e9..17006705287a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -162,10 +162,11 @@ static void tmc_pages_free(struct tmc_pages *tmc_pages, struct device *dev, enum dma_data_direction dir) { int i; + struct device *real_dev = dev->parent; for (i = 0; i < tmc_pages->nr_pages; i++) { if (tmc_pages->daddrs && tmc_pages->daddrs[i]) - dma_unmap_page(dev, tmc_pages->daddrs[i], + dma_unmap_page(real_dev, tmc_pages->daddrs[i], PAGE_SIZE, dir); if (tmc_pages->pages && tmc_pages->pages[i]) __free_page(tmc_pages->pages[i]); @@ -193,6 +194,7 @@ static int tmc_pages_alloc(struct tmc_pages *tmc_pages, int i, nr_pages; dma_addr_t paddr; struct page *page; + struct device *real_dev = dev->parent; nr_pages = tmc_pages->nr_pages; tmc_pages->daddrs = kcalloc(nr_pages, sizeof(*tmc_pages->daddrs), @@ -216,8 +218,8 @@ static int tmc_pages_alloc(struct tmc_pages *tmc_pages, page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); } - paddr = dma_map_page(dev, page, 0, PAGE_SIZE, dir); - if (dma_mapping_error(dev, paddr)) + paddr = dma_map_page(real_dev, page, 0, PAGE_SIZE, dir); + if (dma_mapping_error(real_dev, paddr)) goto err; tmc_pages->daddrs[i] = paddr; tmc_pages->pages[i] = page; @@ -304,7 +306,7 @@ static int tmc_alloc_data_pages(struct tmc_sg_table *sg_table, void **pages) * and data buffers. TMC writes to the data buffers and reads from the SG * Table pages. * - * @dev - Device to which page should be DMA mapped. + * @dev - Coresight device to which page should be DMA mapped. * @node - Numa node for mem allocations * @nr_tpages - Number of pages for the table entries. * @nr_dpages - Number of pages for Data buffer. @@ -348,13 +350,13 @@ void tmc_sg_table_sync_data_range(struct tmc_sg_table *table, { int i, index, start; int npages = DIV_ROUND_UP(size, PAGE_SIZE); - struct device *dev = table->dev; + struct device *real_dev = table->dev->parent; struct tmc_pages *data = &table->data_pages; start = offset >> PAGE_SHIFT; for (i = start; i < (start + npages); i++) { index = i % data->nr_pages; - dma_sync_single_for_cpu(dev, data->daddrs[index], + dma_sync_single_for_cpu(real_dev, data->daddrs[index], PAGE_SIZE, DMA_FROM_DEVICE); } } @@ -363,11 +365,11 @@ void tmc_sg_table_sync_data_range(struct tmc_sg_table *table, void tmc_sg_table_sync_table(struct tmc_sg_table *sg_table) { int i; - struct device *dev = sg_table->dev; + struct device *real_dev = sg_table->dev->parent; struct tmc_pages *table_pages = &sg_table->table_pages; for (i = 0; i < table_pages->nr_pages; i++) - dma_sync_single_for_device(dev, table_pages->daddrs[i], + dma_sync_single_for_device(real_dev, table_pages->daddrs[i], PAGE_SIZE, DMA_TO_DEVICE); } @@ -590,6 +592,7 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, void **pages) { struct etr_flat_buf *flat_buf; + struct device *real_dev = drvdata->csdev->dev.parent; /* We cannot reuse existing pages for flat buf */ if (pages) @@ -599,7 +602,7 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, if (!flat_buf) return -ENOMEM; - flat_buf->vaddr = dma_alloc_coherent(drvdata->dev, etr_buf->size, + flat_buf->vaddr = dma_alloc_coherent(real_dev, etr_buf->size, &flat_buf->daddr, GFP_KERNEL); if (!flat_buf->vaddr) { kfree(flat_buf); @@ -607,7 +610,7 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, } flat_buf->size = etr_buf->size; - flat_buf->dev = drvdata->dev; + flat_buf->dev = &drvdata->csdev->dev; etr_buf->hwaddr = flat_buf->daddr; etr_buf->mode = ETR_MODE_FLAT; etr_buf->private = flat_buf; @@ -618,9 +621,12 @@ static void tmc_etr_free_flat_buf(struct etr_buf *etr_buf) { struct etr_flat_buf *flat_buf = etr_buf->private; - if (flat_buf && flat_buf->daddr) - dma_free_coherent(flat_buf->dev, flat_buf->size, + if (flat_buf && flat_buf->daddr) { + struct device *real_dev = flat_buf->dev->parent; + + dma_free_coherent(real_dev, flat_buf->size, flat_buf->vaddr, flat_buf->daddr); + } kfree(flat_buf); } @@ -666,8 +672,9 @@ static int tmc_etr_alloc_sg_buf(struct tmc_drvdata *drvdata, void **pages) { struct etr_sg_table *etr_table; + struct device *dev = &drvdata->csdev->dev; - etr_table = tmc_init_etr_sg_table(drvdata->dev, node, + etr_table = tmc_init_etr_sg_table(dev, node, etr_buf->size, pages); if (IS_ERR(etr_table)) return -ENOMEM; @@ -751,8 +758,8 @@ tmc_etr_get_catu_device(struct tmc_drvdata *drvdata) if (!IS_ENABLED(CONFIG_CORESIGHT_CATU)) return NULL; - for (i = 0; i < etr->nr_outport; i++) { - tmp = etr->conns[i].child_dev; + for (i = 0; i < etr->pdata->nr_outport; i++) { + tmp = etr->pdata->conns[i].child_dev; if (tmp && coresight_is_catu_device(tmp)) return tmp; } @@ -823,9 +830,10 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, bool has_etr_sg, has_iommu; bool has_sg, has_catu; struct etr_buf *etr_buf; + struct device *dev = &drvdata->csdev->dev; has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG); - has_iommu = iommu_get_domain_for_dev(drvdata->dev); + has_iommu = iommu_get_domain_for_dev(dev->parent); has_catu = !!tmc_etr_get_catu_device(drvdata); has_sg = has_catu || has_etr_sg; @@ -863,7 +871,7 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, return ERR_PTR(rc); } - dev_dbg(drvdata->dev, "allocated buffer of size %ldKB in mode %d\n", + dev_dbg(dev, "allocated buffer of size %ldKB in mode %d\n", (unsigned long)size >> 10, etr_buf->mode); return etr_buf; } @@ -1162,7 +1170,7 @@ out: tmc_etr_free_sysfs_buf(free_buf); if (!ret) - dev_dbg(drvdata->dev, "TMC-ETR enabled\n"); + dev_dbg(&csdev->dev, "TMC-ETR enabled\n"); return ret; } @@ -1178,14 +1186,11 @@ static struct etr_buf * alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event, int nr_pages, void **pages, bool snapshot) { - int node, cpu = event->cpu; + int node; struct etr_buf *etr_buf; unsigned long size; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); - + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); /* * Try to match the perf ring buffer size if it is larger * than the size requested via sysfs. @@ -1317,13 +1322,11 @@ static struct etr_perf_buffer * tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, struct perf_event *event, int nr_pages, void **pages, bool snapshot) { - int node, cpu = event->cpu; + int node; struct etr_buf *etr_buf; struct etr_perf_buffer *etr_perf; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); if (!etr_perf) @@ -1358,7 +1361,7 @@ static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, etr_perf = tmc_etr_setup_perf_buf(drvdata, event, nr_pages, pages, snapshot); if (IS_ERR(etr_perf)) { - dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); + dev_dbg(&csdev->dev, "Unable to allocate ETR buffer\n"); return NULL; } @@ -1501,18 +1504,23 @@ tmc_update_etr_buffer(struct coresight_device *csdev, tmc_etr_sync_perf_buffer(etr_perf); /* - * Update handle->head in snapshot mode. Also update the size to the - * hardware buffer size if there was an overflow. + * In snapshot mode we simply increment the head by the number of byte + * that were written. User space function cs_etm_find_snapshot() will + * figure out how many bytes to get from the AUX buffer based on the + * position of the head. */ - if (etr_perf->snapshot) { + if (etr_perf->snapshot) handle->head += size; - if (etr_buf->full) - size = etr_buf->size; - } lost |= etr_buf->full; out: - if (lost) + /* + * Don't set the TRUNCATED flag in snapshot mode because 1) the + * captured buffer is expected to be truncated and 2) a full buffer + * prevents the event from being re-enabled by the perf core, + * resulting in stale data being send to user space. + */ + if (!etr_perf->snapshot && lost) perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); return size; } @@ -1612,7 +1620,7 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "TMC-ETR disabled\n"); + dev_dbg(&csdev->dev, "TMC-ETR disabled\n"); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3f718729d741..be37aff573b4 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -27,12 +27,16 @@ #include "coresight-priv.h" #include "coresight-tmc.h" +DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); +DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); +DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr"); + void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { /* Ensure formatter, unformatter and hardware fifo are empty */ if (coresight_timeout(drvdata->base, TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "timeout while waiting for TMC to be Ready\n"); } } @@ -49,7 +53,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata) /* Ensure flush completes */ if (coresight_timeout(drvdata->base, TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { - dev_err(drvdata->dev, + dev_err(&drvdata->csdev->dev, "timeout while waiting for completion of Manual Flush\n"); } @@ -83,7 +87,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) } if (!ret) - dev_dbg(drvdata->dev, "TMC read start\n"); + dev_dbg(&drvdata->csdev->dev, "TMC read start\n"); return ret; } @@ -105,7 +109,7 @@ static int tmc_read_unprepare(struct tmc_drvdata *drvdata) } if (!ret) - dev_dbg(drvdata->dev, "TMC read end\n"); + dev_dbg(&drvdata->csdev->dev, "TMC read end\n"); return ret; } @@ -122,7 +126,7 @@ static int tmc_open(struct inode *inode, struct file *file) nonseekable_open(inode, file); - dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); + dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); return 0; } @@ -152,12 +156,13 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, return 0; if (copy_to_user(data, bufp, actual)) { - dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); + dev_dbg(&drvdata->csdev->dev, + "%s: copy_to_user failed\n", __func__); return -EFAULT; } *ppos += actual; - dev_dbg(drvdata->dev, "%zu bytes copied\n", actual); + dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual); return actual; } @@ -172,7 +177,7 @@ static int tmc_release(struct inode *inode, struct file *file) if (ret) return ret; - dev_dbg(drvdata->dev, "%s: released\n", __func__); + dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); return 0; } @@ -332,24 +337,22 @@ const struct attribute_group *coresight_tmc_groups[] = { NULL, }; -static inline bool tmc_etr_can_use_sg(struct tmc_drvdata *drvdata) +static inline bool tmc_etr_can_use_sg(struct device *dev) { - return fwnode_property_present(drvdata->dev->fwnode, - "arm,scatter-gather"); + return fwnode_property_present(dev->fwnode, "arm,scatter-gather"); } /* Detect and initialise the capabilities of a TMC ETR */ -static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, - u32 devid, void *dev_caps) +static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) { int rc; - u32 dma_mask = 0; + struct tmc_drvdata *drvdata = dev_get_drvdata(parent); /* Set the unadvertised capabilities */ tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); - if (!(devid & TMC_DEVID_NOSCAT) && tmc_etr_can_use_sg(drvdata)) + if (!(devid & TMC_DEVID_NOSCAT) && tmc_etr_can_use_sg(parent)) tmc_etr_set_cap(drvdata, TMC_ETR_SG); /* Check if the AXI address width is available */ @@ -367,18 +370,27 @@ static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, case 44: case 48: case 52: - dev_info(drvdata->dev, "Detected dma mask %dbits\n", dma_mask); + dev_info(parent, "Detected dma mask %dbits\n", dma_mask); break; default: dma_mask = 40; } - rc = dma_set_mask_and_coherent(drvdata->dev, DMA_BIT_MASK(dma_mask)); + rc = dma_set_mask_and_coherent(parent, DMA_BIT_MASK(dma_mask)); if (rc) - dev_err(drvdata->dev, "Failed to setup DMA mask: %d\n", rc); + dev_err(parent, "Failed to setup DMA mask: %d\n", rc); return rc; } +static u32 tmc_etr_get_default_buffer_size(struct device *dev) +{ + u32 size; + + if (fwnode_property_read_u32(dev->fwnode, "arm,buffer-size", &size)) + size = SZ_1M; + return size; +} + static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -389,23 +401,13 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) struct tmc_drvdata *drvdata; struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; - - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - goto out; - } - adev->dev.platform_data = pdata; - } + struct coresight_dev_list *dev_list = NULL; ret = -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) goto out; - drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); /* Validity for the resource is already checked by the AMBA core */ @@ -425,18 +427,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) /* This device is not associated with a session */ drvdata->pid = -1; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - if (np) - ret = of_property_read_u32(np, - "arm,buffer-size", - &drvdata->size); - if (ret) - drvdata->size = SZ_1M; - } else { + if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) + drvdata->size = tmc_etr_get_default_buffer_size(dev); + else drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; - } - desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_tmc_groups; @@ -445,36 +440,53 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; + dev_list = &etb_devs; break; case TMC_CONFIG_TYPE_ETR: desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etr_cs_ops; - ret = tmc_etr_setup_caps(drvdata, devid, + ret = tmc_etr_setup_caps(dev, devid, coresight_get_uci_data(id)); if (ret) goto out; idr_init(&drvdata->idr); mutex_init(&drvdata->idr_mutex); + dev_list = &etr_devs; break; case TMC_CONFIG_TYPE_ETF: desc.type = CORESIGHT_DEV_TYPE_LINKSINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; + dev_list = &etf_devs; break; default: - pr_err("%s: Unsupported TMC config\n", pdata->name); + pr_err("%s: Unsupported TMC config\n", desc.name); ret = -EINVAL; goto out; } + desc.name = coresight_alloc_device_name(dev_list, dev); + if (!desc.name) { + ret = -ENOMEM; + goto out; + } + + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto out; + } + adev->dev.platform_data = pdata; + desc.pdata = pdata; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); goto out; } - drvdata->miscdev.name = pdata->name; + drvdata->miscdev.name = desc.name; drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; drvdata->miscdev.fops = &tmc_fops; ret = misc_register(&drvdata->miscdev); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 503f1b3a3741..1ed50411cc3c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -161,7 +161,6 @@ struct etr_buf { /** * struct tmc_drvdata - specifics associated to an TMC component * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.tmc" entry. * @spinlock: only one at a time pls. @@ -184,7 +183,6 @@ struct etr_buf { */ struct tmc_drvdata { void __iomem *base; - struct device *dev; struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 63d9af31f57f..f8583e4032a6 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -47,15 +47,15 @@ #define FFCR_FON_MAN BIT(6) #define FFCR_STOP_FI BIT(12) +DEFINE_CORESIGHT_DEVLIST(tpiu_devs, "tpiu"); + /** * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. * @atclk: optional clock for the core parts of the TPIU. * @csdev: component vitals needed by the framework. */ struct tpiu_drvdata { void __iomem *base; - struct device *dev; struct clk *atclk; struct coresight_device *csdev; }; @@ -75,7 +75,7 @@ static int tpiu_enable(struct coresight_device *csdev, u32 mode, void *__unused) tpiu_enable_hw(drvdata); atomic_inc(csdev->refcnt); - dev_dbg(drvdata->dev, "TPIU enabled\n"); + dev_dbg(&csdev->dev, "TPIU enabled\n"); return 0; } @@ -104,7 +104,7 @@ static int tpiu_disable(struct coresight_device *csdev) tpiu_disable_hw(drvdata); - dev_dbg(drvdata->dev, "TPIU disabled\n"); + dev_dbg(&csdev->dev, "TPIU disabled\n"); return 0; } @@ -126,20 +126,15 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) struct tpiu_drvdata *drvdata; struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - struct device_node *np = adev->dev.of_node; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - adev->dev.platform_data = pdata; - } + desc.name = coresight_alloc_device_name(&tpiu_devs, dev); + if (!desc.name) + return -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = &adev->dev; drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); @@ -158,6 +153,11 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) /* Disable tpiu to support older devices */ tpiu_disable_hw(drvdata); + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + dev->platform_data = pdata; + desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; desc.ops = &tpiu_cs_ops; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 4b130281236a..86d1fc2c1bd4 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -100,8 +100,8 @@ static int coresight_find_link_inport(struct coresight_device *csdev, int i; struct coresight_connection *conn; - for (i = 0; i < parent->nr_outport; i++) { - conn = &parent->conns[i]; + for (i = 0; i < parent->pdata->nr_outport; i++) { + conn = &parent->pdata->conns[i]; if (conn->child_dev == csdev) return conn->child_port; } @@ -118,8 +118,8 @@ static int coresight_find_link_outport(struct coresight_device *csdev, int i; struct coresight_connection *conn; - for (i = 0; i < csdev->nr_outport; i++) { - conn = &csdev->conns[i]; + for (i = 0; i < csdev->pdata->nr_outport; i++) { + conn = &csdev->pdata->conns[i]; if (conn->child_dev == child) return conn->outport; } @@ -306,10 +306,10 @@ static void coresight_disable_link(struct coresight_device *csdev, if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { refport = inport; - nr_conns = csdev->nr_inport; + nr_conns = csdev->pdata->nr_inport; } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) { refport = outport; - nr_conns = csdev->nr_outport; + nr_conns = csdev->pdata->nr_outport; } else { refport = 0; nr_conns = 1; @@ -595,9 +595,10 @@ static void coresight_grab_device(struct coresight_device *csdev) { int i; - for (i = 0; i < csdev->nr_outport; i++) { - struct coresight_device *child = csdev->conns[i].child_dev; + for (i = 0; i < csdev->pdata->nr_outport; i++) { + struct coresight_device *child; + child = csdev->pdata->conns[i].child_dev; if (child && child->type == CORESIGHT_DEV_TYPE_HELPER) pm_runtime_get_sync(child->dev.parent); } @@ -613,9 +614,10 @@ static void coresight_drop_device(struct coresight_device *csdev) int i; pm_runtime_put(csdev->dev.parent); - for (i = 0; i < csdev->nr_outport; i++) { - struct coresight_device *child = csdev->conns[i].child_dev; + for (i = 0; i < csdev->pdata->nr_outport; i++) { + struct coresight_device *child; + child = csdev->pdata->conns[i].child_dev; if (child && child->type == CORESIGHT_DEV_TYPE_HELPER) pm_runtime_put(child->dev.parent); } @@ -645,9 +647,10 @@ static int _coresight_build_path(struct coresight_device *csdev, goto out; /* Not a sink - recursively explore each port found on this element */ - for (i = 0; i < csdev->nr_outport; i++) { - struct coresight_device *child_dev = csdev->conns[i].child_dev; + for (i = 0; i < csdev->pdata->nr_outport; i++) { + struct coresight_device *child_dev; + child_dev = csdev->pdata->conns[i].child_dev; if (child_dev && _coresight_build_path(child_dev, sink, path) == 0) { found = true; @@ -975,6 +978,7 @@ static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev); + fwnode_handle_put(csdev->dev.fwnode); kfree(csdev->refcnt); kfree(csdev); } @@ -1000,19 +1004,17 @@ static int coresight_orphan_match(struct device *dev, void *data) * Circle throuch all the connection of that component. If we find * an orphan connection whose name matches @csdev, link it. */ - for (i = 0; i < i_csdev->nr_outport; i++) { - conn = &i_csdev->conns[i]; + for (i = 0; i < i_csdev->pdata->nr_outport; i++) { + conn = &i_csdev->pdata->conns[i]; /* We have found at least one orphan connection */ if (conn->child_dev == NULL) { /* Does it match this newly added device? */ - if (conn->child_name && - !strcmp(dev_name(&csdev->dev), conn->child_name)) { + if (conn->child_fwnode == csdev->dev.fwnode) conn->child_dev = csdev; - } else { + else /* This component still has an orphan */ still_orphan = true; - } } } @@ -1040,13 +1042,13 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) { int i; - for (i = 0; i < csdev->nr_outport; i++) { - struct coresight_connection *conn = &csdev->conns[i]; + for (i = 0; i < csdev->pdata->nr_outport; i++) { + struct coresight_connection *conn = &csdev->pdata->conns[i]; struct device *dev = NULL; - if (conn->child_name) - dev = bus_find_device_by_name(&coresight_bustype, NULL, - conn->child_name); + dev = bus_find_device(&coresight_bustype, NULL, + (void *)conn->child_fwnode, + coresight_device_fwnode_match); if (dev) { conn->child_dev = to_coresight_device(dev); /* and put reference from 'bus_find_device()' */ @@ -1075,15 +1077,21 @@ static int coresight_remove_match(struct device *dev, void *data) * Circle throuch all the connection of that component. If we find * a connection whose name matches @csdev, remove it. */ - for (i = 0; i < iterator->nr_outport; i++) { - conn = &iterator->conns[i]; + for (i = 0; i < iterator->pdata->nr_outport; i++) { + conn = &iterator->pdata->conns[i]; if (conn->child_dev == NULL) continue; - if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { + if (csdev->dev.fwnode == conn->child_fwnode) { iterator->orphan = true; conn->child_dev = NULL; + /* + * Drop the reference to the handle for the remote + * device acquired in parsing the connections from + * platform data. + */ + fwnode_handle_put(conn->child_fwnode); /* No need to continue */ break; } @@ -1096,10 +1104,21 @@ static int coresight_remove_match(struct device *dev, void *data) return 0; } +/* + * coresight_remove_conns - Remove references to this given devices + * from the connections of other devices. + */ static void coresight_remove_conns(struct coresight_device *csdev) { - bus_for_each_dev(&coresight_bustype, NULL, - csdev, coresight_remove_match); + /* + * Another device will point to this device only if there is + * an output port connected to this one. i.e, if the device + * doesn't have at least one input port, there is no point + * in searching all the devices. + */ + if (csdev->pdata->nr_inport) + bus_for_each_dev(&coresight_bustype, NULL, + csdev, coresight_remove_match); } /** @@ -1152,6 +1171,22 @@ static int __init coresight_init(void) } postcore_initcall(coresight_init); +/* + * coresight_release_platform_data: Release references to the devices connected + * to the output port of this device. + */ +void coresight_release_platform_data(struct coresight_platform_data *pdata) +{ + int i; + + for (i = 0; i < pdata->nr_outport; i++) { + if (pdata->conns[i].child_fwnode) { + fwnode_handle_put(pdata->conns[i].child_fwnode); + pdata->conns[i].child_fwnode = NULL; + } + } +} + struct coresight_device *coresight_register(struct coresight_desc *desc) { int ret; @@ -1184,10 +1219,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->refcnt = refcnts; - csdev->nr_inport = desc->pdata->nr_inport; - csdev->nr_outport = desc->pdata->nr_outport; - - csdev->conns = desc->pdata->conns; + csdev->pdata = desc->pdata; csdev->type = desc->type; csdev->subtype = desc->subtype; @@ -1199,7 +1231,12 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->dev.parent = desc->dev; csdev->dev.release = coresight_device_release; csdev->dev.bus = &coresight_bustype; - dev_set_name(&csdev->dev, "%s", desc->pdata->name); + /* + * Hold the reference to our parent device. This will be + * dropped only in coresight_device_release(). + */ + csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); + dev_set_name(&csdev->dev, "%s", desc->name); ret = device_register(&csdev->dev); if (ret) { @@ -1239,6 +1276,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) err_free_csdev: kfree(csdev); err_out: + /* Cleanup the connection information */ + coresight_release_platform_data(desc->pdata); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_register); @@ -1248,6 +1287,65 @@ void coresight_unregister(struct coresight_device *csdev) etm_perf_del_symlink_sink(csdev); /* Remove references of that device in the topology */ coresight_remove_conns(csdev); + coresight_release_platform_data(csdev->pdata); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); + + +/* + * coresight_search_device_idx - Search the fwnode handle of a device + * in the given dev_idx list. Must be called with the coresight_mutex held. + * + * Returns the index of the entry, when found. Otherwise, -ENOENT. + */ +static inline int coresight_search_device_idx(struct coresight_dev_list *dict, + struct fwnode_handle *fwnode) +{ + int i; + + for (i = 0; i < dict->nr_idx; i++) + if (dict->fwnode_list[i] == fwnode) + return i; + return -ENOENT; +} + +/* + * coresight_alloc_device_name - Get an index for a given device in the + * device index list specific to a driver. An index is allocated for a + * device and is tracked with the fwnode_handle to prevent allocating + * duplicate indices for the same device (e.g, if we defer probing of + * a device due to dependencies), in case the index is requested again. + */ +char *coresight_alloc_device_name(struct coresight_dev_list *dict, + struct device *dev) +{ + int idx; + char *name = NULL; + struct fwnode_handle **list; + + mutex_lock(&coresight_mutex); + + idx = coresight_search_device_idx(dict, dev_fwnode(dev)); + if (idx < 0) { + /* Make space for the new entry */ + idx = dict->nr_idx; + list = krealloc(dict->fwnode_list, + (idx + 1) * sizeof(*dict->fwnode_list), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(list)) { + idx = -ENOMEM; + goto done; + } + + list[idx] = dev_fwnode(dev); + dict->fwnode_list = list; + dict->nr_idx = idx + 1; + } + + name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", dict->pfx, idx); +done: + mutex_unlock(&coresight_mutex); + return name; +} +EXPORT_SYMBOL_GPL(coresight_alloc_device_name); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c deleted file mode 100644 index 7045930fc958..000000000000 --- a/drivers/hwtracing/coresight/of_coresight.c +++ /dev/null @@ -1,297 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2012, The Linux Foundation. All rights reserved. - */ - -#include <linux/types.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/clk.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_graph.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> -#include <linux/amba/bus.h> -#include <linux/coresight.h> -#include <linux/cpumask.h> -#include <asm/smp_plat.h> - - -static int of_dev_node_match(struct device *dev, void *data) -{ - return dev->of_node == data; -} - -static struct device * -of_coresight_get_endpoint_device(struct device_node *endpoint) -{ - struct device *dev = NULL; - - /* - * If we have a non-configurable replicator, it will be found on the - * platform bus. - */ - dev = bus_find_device(&platform_bus_type, NULL, - endpoint, of_dev_node_match); - if (dev) - return dev; - - /* - * We have a configurable component - circle through the AMBA bus - * looking for the device that matches the endpoint node. - */ - return bus_find_device(&amba_bustype, NULL, - endpoint, of_dev_node_match); -} - -static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep) -{ - return of_property_read_bool(ep, "slave-mode"); -} - -static void of_coresight_get_ports_legacy(const struct device_node *node, - int *nr_inport, int *nr_outport) -{ - struct device_node *ep = NULL; - int in = 0, out = 0; - - do { - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - break; - - if (of_coresight_legacy_ep_is_input(ep)) - in++; - else - out++; - - } while (ep); - - *nr_inport = in; - *nr_outport = out; -} - -static struct device_node *of_coresight_get_port_parent(struct device_node *ep) -{ - struct device_node *parent = of_graph_get_port_parent(ep); - - /* - * Skip one-level up to the real device node, if we - * are using the new bindings. - */ - if (of_node_name_eq(parent, "in-ports") || - of_node_name_eq(parent, "out-ports")) - parent = of_get_next_parent(parent); - - return parent; -} - -static inline struct device_node * -of_coresight_get_input_ports_node(const struct device_node *node) -{ - return of_get_child_by_name(node, "in-ports"); -} - -static inline struct device_node * -of_coresight_get_output_ports_node(const struct device_node *node) -{ - return of_get_child_by_name(node, "out-ports"); -} - -static inline int -of_coresight_count_ports(struct device_node *port_parent) -{ - int i = 0; - struct device_node *ep = NULL; - - while ((ep = of_graph_get_next_endpoint(port_parent, ep))) - i++; - return i; -} - -static void of_coresight_get_ports(const struct device_node *node, - int *nr_inport, int *nr_outport) -{ - struct device_node *input_ports = NULL, *output_ports = NULL; - - input_ports = of_coresight_get_input_ports_node(node); - output_ports = of_coresight_get_output_ports_node(node); - - if (input_ports || output_ports) { - if (input_ports) { - *nr_inport = of_coresight_count_ports(input_ports); - of_node_put(input_ports); - } - if (output_ports) { - *nr_outport = of_coresight_count_ports(output_ports); - of_node_put(output_ports); - } - } else { - /* Fall back to legacy DT bindings parsing */ - of_coresight_get_ports_legacy(node, nr_inport, nr_outport); - } -} - -static int of_coresight_alloc_memory(struct device *dev, - struct coresight_platform_data *pdata) -{ - if (pdata->nr_outport) { - pdata->conns = devm_kzalloc(dev, pdata->nr_outport * - sizeof(*pdata->conns), - GFP_KERNEL); - if (!pdata->conns) - return -ENOMEM; - } - - return 0; -} - -int of_coresight_get_cpu(const struct device_node *node) -{ - int cpu; - struct device_node *dn; - - dn = of_parse_phandle(node, "cpu", 0); - /* Affinity defaults to CPU0 */ - if (!dn) - return 0; - cpu = of_cpu_node_to_id(dn); - of_node_put(dn); - - /* Affinity to CPU0 if no cpu nodes are found */ - return (cpu < 0) ? 0 : cpu; -} -EXPORT_SYMBOL_GPL(of_coresight_get_cpu); - -/* - * of_coresight_parse_endpoint : Parse the given output endpoint @ep - * and fill the connection information in @conn - * - * Parses the local port, remote device name and the remote port. - * - * Returns : - * 1 - If the parsing is successful and a connection record - * was created for an output connection. - * 0 - If the parsing completed without any fatal errors. - * -Errno - Fatal error, abort the scanning. - */ -static int of_coresight_parse_endpoint(struct device *dev, - struct device_node *ep, - struct coresight_connection *conn) -{ - int ret = 0; - struct of_endpoint endpoint, rendpoint; - struct device_node *rparent = NULL; - struct device_node *rep = NULL; - struct device *rdev = NULL; - - do { - /* Parse the local port details */ - if (of_graph_parse_endpoint(ep, &endpoint)) - break; - /* - * Get a handle on the remote endpoint and the device it is - * attached to. - */ - rep = of_graph_get_remote_endpoint(ep); - if (!rep) - break; - rparent = of_coresight_get_port_parent(rep); - if (!rparent) - break; - if (of_graph_parse_endpoint(rep, &rendpoint)) - break; - - /* If the remote device is not available, defer probing */ - rdev = of_coresight_get_endpoint_device(rparent); - if (!rdev) { - ret = -EPROBE_DEFER; - break; - } - - conn->outport = endpoint.port; - conn->child_name = devm_kstrdup(dev, - dev_name(rdev), - GFP_KERNEL); - conn->child_port = rendpoint.port; - /* Connection record updated */ - ret = 1; - } while (0); - - of_node_put(rparent); - of_node_put(rep); - put_device(rdev); - - return ret; -} - -struct coresight_platform_data * -of_get_coresight_platform_data(struct device *dev, - const struct device_node *node) -{ - int ret = 0; - struct coresight_platform_data *pdata; - struct coresight_connection *conn; - struct device_node *ep = NULL; - const struct device_node *parent = NULL; - bool legacy_binding = false; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - /* Use device name as sysfs handle */ - pdata->name = dev_name(dev); - pdata->cpu = of_coresight_get_cpu(node); - - /* Get the number of input and output port for this component */ - of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport); - - /* If there are no output connections, we are done */ - if (!pdata->nr_outport) - return pdata; - - ret = of_coresight_alloc_memory(dev, pdata); - if (ret) - return ERR_PTR(ret); - - parent = of_coresight_get_output_ports_node(node); - /* - * If the DT uses obsoleted bindings, the ports are listed - * under the device and we need to filter out the input - * ports. - */ - if (!parent) { - legacy_binding = true; - parent = node; - dev_warn_once(dev, "Uses obsolete Coresight DT bindings\n"); - } - - conn = pdata->conns; - - /* Iterate through each output port to discover topology */ - while ((ep = of_graph_get_next_endpoint(parent, ep))) { - /* - * Legacy binding mixes input/output ports under the - * same parent. So, skip the input ports if we are dealing - * with legacy binding, as they processed with their - * connected output ports. - */ - if (legacy_binding && of_coresight_legacy_ep_is_input(ep)) - continue; - - ret = of_coresight_parse_endpoint(dev, ep, conn); - switch (ret) { - case 1: - conn++; /* Fall through */ - case 0: - break; - default: - return ERR_PTR(ret); - } - } - - return pdata; -} -EXPORT_SYMBOL_GPL(of_get_coresight_platform_data); diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 81bb54fa3ce8..8ab28e5fb366 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -33,14 +33,18 @@ * @entry: window list linkage (msc::win_list) * @pgoff: page offset into the buffer that this window starts at * @nr_blocks: number of blocks (pages) in this window + * @nr_segs: number of segments in this window (<= @nr_blocks) + * @_sgt: array of block descriptors * @sgt: array of block descriptors */ struct msc_window { struct list_head entry; unsigned long pgoff; unsigned int nr_blocks; + unsigned int nr_segs; struct msc *msc; - struct sg_table sgt; + struct sg_table _sgt; + struct sg_table *sgt; }; /** @@ -138,13 +142,19 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) static inline struct msc_block_desc * msc_win_block(struct msc_window *win, unsigned int block) { - return sg_virt(&win->sgt.sgl[block]); + return sg_virt(&win->sgt->sgl[block]); +} + +static inline size_t +msc_win_actual_bsz(struct msc_window *win, unsigned int block) +{ + return win->sgt->sgl[block].length; } static inline dma_addr_t msc_win_baddr(struct msc_window *win, unsigned int block) { - return sg_dma_address(&win->sgt.sgl[block]); + return sg_dma_address(&win->sgt->sgl[block]); } static inline unsigned long @@ -179,17 +189,18 @@ static struct msc_window *msc_next_window(struct msc_window *win) } /** - * msc_oldest_window() - locate the window with oldest data + * msc_find_window() - find a window matching a given sg_table * @msc: MSC device + * @sgt: SG table of the window + * @nonempty: skip over empty windows * - * This should only be used in multiblock mode. Caller should hold the - * msc::user_count reference. - * - * Return: the oldest window with valid data + * Return: MSC window structure pointer or NULL if the window + * could not be found. */ -static struct msc_window *msc_oldest_window(struct msc *msc) +static struct msc_window * +msc_find_window(struct msc *msc, struct sg_table *sgt, bool nonempty) { - struct msc_window *win, *next = msc_next_window(msc->cur_win); + struct msc_window *win; unsigned int found = 0; if (list_empty(&msc->win_list)) @@ -201,17 +212,40 @@ static struct msc_window *msc_oldest_window(struct msc *msc) * something like 2, in which case we're good */ list_for_each_entry(win, &msc->win_list, entry) { - if (win == next) + if (win->sgt == sgt) found++; /* skip the empty ones */ - if (msc_block_is_empty(msc_win_block(win, 0))) + if (nonempty && msc_block_is_empty(msc_win_block(win, 0))) continue; if (found) return win; } + return NULL; +} + +/** + * msc_oldest_window() - locate the window with oldest data + * @msc: MSC device + * + * This should only be used in multiblock mode. Caller should hold the + * msc::user_count reference. + * + * Return: the oldest window with valid data + */ +static struct msc_window *msc_oldest_window(struct msc *msc) +{ + struct msc_window *win; + + if (list_empty(&msc->win_list)) + return NULL; + + win = msc_find_window(msc, msc_next_window(msc->cur_win)->sgt, true); + if (win) + return win; + return list_first_entry(&msc->win_list, struct msc_window, entry); } @@ -234,7 +268,7 @@ static unsigned int msc_win_oldest_block(struct msc_window *win) * with wrapping, last written block contains both the newest and the * oldest data for this window. */ - for (blk = 0; blk < win->nr_blocks; blk++) { + for (blk = 0; blk < win->nr_segs; blk++) { bdesc = msc_win_block(win, blk); if (msc_block_last_written(bdesc)) @@ -366,7 +400,7 @@ static int msc_iter_block_advance(struct msc_iter *iter) return msc_iter_win_advance(iter); /* block advance */ - if (++iter->block == iter->win->nr_blocks) + if (++iter->block == iter->win->nr_segs) iter->block = 0; /* no wrapping, sanity check in case there is no last written block */ @@ -478,7 +512,7 @@ static void msc_buffer_clear_hw_header(struct msc *msc) size_t hw_sz = sizeof(struct msc_block_desc) - offsetof(struct msc_block_desc, hw_tag); - for (blk = 0; blk < win->nr_blocks; blk++) { + for (blk = 0; blk < win->nr_segs; blk++) { struct msc_block_desc *bdesc = msc_win_block(win, blk); memset(&bdesc->hw_tag, 0, hw_sz); @@ -667,7 +701,7 @@ static int msc_buffer_contig_alloc(struct msc *msc, unsigned long size) goto err_out; ret = -ENOMEM; - page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); + page = alloc_pages(GFP_KERNEL | __GFP_ZERO | GFP_DMA32, order); if (!page) goto err_free_sgt; @@ -734,17 +768,17 @@ static struct page *msc_buffer_contig_get_page(struct msc *msc, } static int __msc_buffer_win_alloc(struct msc_window *win, - unsigned int nr_blocks) + unsigned int nr_segs) { struct scatterlist *sg_ptr; void *block; int i, ret; - ret = sg_alloc_table(&win->sgt, nr_blocks, GFP_KERNEL); + ret = sg_alloc_table(win->sgt, nr_segs, GFP_KERNEL); if (ret) return -ENOMEM; - for_each_sg(win->sgt.sgl, sg_ptr, nr_blocks, i) { + for_each_sg(win->sgt->sgl, sg_ptr, nr_segs, i) { block = dma_alloc_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, &sg_dma_address(sg_ptr), GFP_KERNEL); @@ -754,7 +788,7 @@ static int __msc_buffer_win_alloc(struct msc_window *win, sg_set_buf(sg_ptr, block, PAGE_SIZE); } - return nr_blocks; + return nr_segs; err_nomem: for (i--; i >= 0; i--) @@ -762,11 +796,35 @@ err_nomem: msc_win_block(win, i), msc_win_baddr(win, i)); - sg_free_table(&win->sgt); + sg_free_table(win->sgt); return -ENOMEM; } +#ifdef CONFIG_X86 +static void msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) +{ + int i; + + for (i = 0; i < nr_segs; i++) + /* Set the page as uncached */ + set_memory_uc((unsigned long)msc_win_block(win, i), 1); +} + +static void msc_buffer_set_wb(struct msc_window *win) +{ + int i; + + for (i = 0; i < win->nr_segs; i++) + /* Reset the page to write-back */ + set_memory_wb((unsigned long)msc_win_block(win, i), 1); +} +#else /* !X86 */ +static inline void +msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) {} +static inline void msc_buffer_set_wb(struct msc_window *win) {} +#endif /* CONFIG_X86 */ + /** * msc_buffer_win_alloc() - alloc a window for a multiblock mode * @msc: MSC device @@ -780,7 +838,7 @@ err_nomem: static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) { struct msc_window *win; - int ret = -ENOMEM, i; + int ret = -ENOMEM; if (!nr_blocks) return 0; @@ -797,13 +855,13 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) return -ENOMEM; win->msc = msc; + win->sgt = &win->_sgt; if (!list_empty(&msc->win_list)) { struct msc_window *prev = list_last_entry(&msc->win_list, struct msc_window, entry); - /* This works as long as blocks are page-sized */ win->pgoff = prev->pgoff + prev->nr_blocks; } @@ -811,13 +869,10 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) if (ret < 0) goto err_nomem; -#ifdef CONFIG_X86 - for (i = 0; i < ret; i++) - /* Set the page as uncached */ - set_memory_uc((unsigned long)msc_win_block(win, i), 1); -#endif + msc_buffer_set_uc(win, ret); - win->nr_blocks = ret; + win->nr_segs = ret; + win->nr_blocks = nr_blocks; if (list_empty(&msc->win_list)) { msc->base = msc_win_block(win, 0); @@ -840,14 +895,14 @@ static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win) { int i; - for (i = 0; i < win->nr_blocks; i++) { - struct page *page = sg_page(&win->sgt.sgl[i]); + for (i = 0; i < win->nr_segs; i++) { + struct page *page = sg_page(&win->sgt->sgl[i]); page->mapping = NULL; dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, msc_win_block(win, i), msc_win_baddr(win, i)); } - sg_free_table(&win->sgt); + sg_free_table(win->sgt); } /** @@ -860,8 +915,6 @@ static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win) */ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win) { - int i; - msc->nr_pages -= win->nr_blocks; list_del(&win->entry); @@ -870,11 +923,7 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win) msc->base_addr = 0; } -#ifdef CONFIG_X86 - for (i = 0; i < win->nr_blocks; i++) - /* Reset the page to write-back */ - set_memory_wb((unsigned long)msc_win_block(win, i), 1); -#endif + msc_buffer_set_wb(win); __msc_buffer_win_free(msc, win); @@ -909,7 +958,7 @@ static void msc_buffer_relink(struct msc *msc) next_win = list_next_entry(win, entry); } - for (blk = 0; blk < win->nr_blocks; blk++) { + for (blk = 0; blk < win->nr_segs; blk++) { struct msc_block_desc *bdesc = msc_win_block(win, blk); memset(bdesc, 0, sizeof(*bdesc)); @@ -920,7 +969,7 @@ static void msc_buffer_relink(struct msc *msc) * Similarly to last window, last block should point * to the first one. */ - if (blk == win->nr_blocks - 1) { + if (blk == win->nr_segs - 1) { sw_tag |= MSC_SW_TAG_LASTBLK; bdesc->next_blk = msc_win_bpfn(win, 0); } else { @@ -928,7 +977,7 @@ static void msc_buffer_relink(struct msc *msc) } bdesc->sw_tag = sw_tag; - bdesc->block_sz = PAGE_SIZE / 64; + bdesc->block_sz = msc_win_actual_bsz(win, blk) / 64; } } @@ -1087,6 +1136,7 @@ static int msc_buffer_free_unless_used(struct msc *msc) static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff) { struct msc_window *win; + unsigned int blk; if (msc->mode == MSC_MODE_SINGLE) return msc_buffer_contig_get_page(msc, pgoff); @@ -1099,7 +1149,18 @@ static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff) found: pgoff -= win->pgoff; - return sg_page(&win->sgt.sgl[pgoff]); + + for (blk = 0; blk < win->nr_segs; blk++) { + struct page *page = sg_page(&win->sgt->sgl[blk]); + size_t pgsz = PFN_DOWN(msc_win_actual_bsz(win, blk)); + + if (pgoff < pgsz) + return page + pgoff; + + pgoff -= pgsz; + } + + return NULL; } /** @@ -1386,10 +1447,9 @@ static int intel_th_msc_init(struct msc *msc) static void msc_win_switch(struct msc *msc) { - struct msc_window *last, *first; + struct msc_window *first; first = list_first_entry(&msc->win_list, struct msc_window, entry); - last = list_last_entry(&msc->win_list, struct msc_window, entry); if (msc_is_last_win(msc->cur_win)) msc->cur_win = first; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index f1228708f2a2..c0378c3de9a4 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -194,6 +194,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x02a6), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Ice Lake NNPI */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { 0 }, }; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 392ad4f5c570..dbdee02bb592 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -123,7 +123,7 @@ config FSL_IFC config JZ4780_NEMC bool "Ingenic JZ4780 SoC NEMC driver" default y - depends on MACH_JZ4780 || COMPILE_TEST + depends on MIPS || COMPILE_TEST depends on HAS_IOMEM && OF help This driver is for the NAND/External Memory Controller (NEMC) in diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c index 698da973de35..2a3f7ef1c8c4 100644 --- a/drivers/memory/jz4780-nemc.c +++ b/drivers/memory/jz4780-nemc.c @@ -41,9 +41,14 @@ #define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1) #define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1) +struct jz_soc_info { + u8 tas_tah_cycles_max; +}; + struct jz4780_nemc { spinlock_t lock; struct device *dev; + const struct jz_soc_info *soc_info; void __iomem *base; struct clk *clk; uint32_t clk_period; @@ -158,7 +163,7 @@ static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, * Conversion of tBP and tAW cycle counts to values supported by the * hardware (round up to the next supported value). */ - static const uint32_t convert_tBP_tAW[] = { + static const u8 convert_tBP_tAW[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 11 - 12 -> 12 cycles */ @@ -199,7 +204,7 @@ static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) { smcr &= ~NEMC_SMCR_TAS_MASK; cycles = jz4780_nemc_ns_to_cycles(nemc, val); - if (cycles > 15) { + if (cycles > nemc->soc_info->tas_tah_cycles_max) { dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n", val, cycles); return false; @@ -211,7 +216,7 @@ static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) { smcr &= ~NEMC_SMCR_TAH_MASK; cycles = jz4780_nemc_ns_to_cycles(nemc, val); - if (cycles > 15) { + if (cycles > nemc->soc_info->tas_tah_cycles_max) { dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n", val, cycles); return false; @@ -275,6 +280,10 @@ static int jz4780_nemc_probe(struct platform_device *pdev) if (!nemc) return -ENOMEM; + nemc->soc_info = device_get_match_data(dev); + if (!nemc->soc_info) + return -EINVAL; + spin_lock_init(&nemc->lock); nemc->dev = dev; @@ -367,8 +376,17 @@ static int jz4780_nemc_remove(struct platform_device *pdev) return 0; } +static const struct jz_soc_info jz4740_soc_info = { + .tas_tah_cycles_max = 7, +}; + +static const struct jz_soc_info jz4780_soc_info = { + .tas_tah_cycles_max = 15, +}; + static const struct of_device_id jz4780_nemc_dt_match[] = { - { .compatible = "ingenic,jz4780-nemc" }, + { .compatible = "ingenic,jz4740-nemc", .data = &jz4740_soc_info, }, + { .compatible = "ingenic,jz4780-nemc", .data = &jz4780_soc_info, }, {}, }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 85fc77148d19..6abfc8e92fcc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -9,7 +9,6 @@ config SENSORS_LIS3LV02D tristate depends on INPUT select INPUT_POLLDEV - default n config AD525X_DPOT tristate "Analog Devices Digital Potentiometers" @@ -62,7 +61,6 @@ config ATMEL_TCLIB config DUMMY_IRQ tristate "Dummy IRQ handler" - default n ---help--- This module accepts a single 'irq' parameter, which it should register for. The sole purpose of this module is to help with debugging of systems on @@ -118,7 +116,6 @@ config PHANTOM config INTEL_MID_PTI tristate "Parallel Trace Interface for MIPI P1149.7 cJTAG standard" depends on PCI && TTY && (X86_INTEL_MID || COMPILE_TEST) - default n help The PTI (Parallel Trace Interface) driver directs trace data routed from various parts in the system out @@ -194,7 +191,6 @@ config ATMEL_SSC config ENCLOSURE_SERVICES tristate "Enclosure Services" - default n help Provides support for intelligent enclosures (bays which contain storage devices). You also need either a host @@ -218,7 +214,6 @@ config SGI_XP config CS5535_MFGPT tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support" depends on MFD_CS5535 - default n help This driver provides access to MFGPT functionality for other drivers that need timers. MFGPTs are available in the CS5535 and @@ -251,7 +246,6 @@ config CS5535_CLOCK_EVENT_SRC config HP_ILO tristate "Channel interface driver for the HP iLO processor" depends on PCI - default n help The channel interface driver allows applications to communicate with iLO management processors present on HP ProLiant servers. @@ -286,7 +280,6 @@ config QCOM_FASTRPC config SGI_GRU tristate "SGI GRU driver" depends on X86_UV && SMP - default n select MMU_NOTIFIER ---help--- The GRU is a hardware resource located in the system chipset. The GRU @@ -301,7 +294,6 @@ config SGI_GRU config SGI_GRU_DEBUG bool "SGI GRU driver debug" depends on SGI_GRU - default n ---help--- This option enables additional debugging code for the SGI GRU driver. If you are unsure, say N. @@ -359,7 +351,6 @@ config SENSORS_BH1770 config SENSORS_APDS990X tristate "APDS990X combined als and proximity sensors" depends on I2C - default n ---help--- Say Y here if you want to build a driver for Avago APDS990x combined ambient light and proximity sensor chip. @@ -387,7 +378,6 @@ config DS1682 config SPEAR13XX_PCIE_GADGET bool "PCIe gadget support for SPEAr13XX platform" depends on ARCH_SPEAR13XX && BROKEN - default n help This option enables gadget support for PCIe controller. If board file defines any controller as PCIe endpoint then a sysfs @@ -397,6 +387,7 @@ config SPEAR13XX_PCIE_GADGET config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST + select MEMORY_BALLOON help This is VMware physical memory management driver which acts like a "balloon" that can be inflated to reclaim physical pages @@ -431,15 +422,6 @@ config PCH_PHUB To compile this driver as a module, choose M here: the module will be called pch_phub. -config USB_SWITCH_FSA9480 - tristate "FSA9480 USB Switch" - depends on I2C - help - The FSA9480 is a USB port accessory detector and switch. - The FSA9480 is fully controlled using I2C and enables USB data, - stereo and mono audio, video, microphone and UART data to use - a common connector port. - config LATTICE_ECP3_CONFIG tristate "Lattice ECP3 FPGA bitstream configuration via SPI" depends on SPI && SYSFS @@ -481,6 +463,18 @@ config PCI_ENDPOINT_TEST Enable this configuration option to enable the host side test driver for PCI Endpoint. +config XILINX_SDFEC + tristate "Xilinx SDFEC 16" + help + This option enables support for the Xilinx SDFEC (Soft Decision + Forward Error Correction) driver. This enables a char driver + for the SDFEC. + + You may select this driver if your design instantiates the + SDFEC(16nm) hardened block. To compile this as a module choose M. + + If unsure, say N. + config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b9affcdaa3d6..abd8ae249746 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ obj-y += lis3lv02d/ -obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ @@ -59,3 +58,4 @@ obj-$(CONFIG_OCXL) += ocxl/ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HABANA_AI) += habanalabs/ +obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o diff --git a/drivers/misc/altera-stapl/Kconfig b/drivers/misc/altera-stapl/Kconfig index b34863544365..6c4c6575ec31 100644 --- a/drivers/misc/altera-stapl/Kconfig +++ b/drivers/misc/altera-stapl/Kconfig @@ -5,6 +5,5 @@ comment "Altera FPGA firmware download module (requires I2C)" config ALTERA_STAPL tristate "Altera FPGA firmware download module" depends on I2C - default n help An Altera FPGA module. Say Y when you want to support this tool. diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig index 192e25094bd4..e20516ffd91e 100644 --- a/drivers/misc/c2port/Kconfig +++ b/drivers/misc/c2port/Kconfig @@ -5,7 +5,6 @@ menuconfig C2PORT tristate "Silicon Labs C2 port support" - default n help This option enables support for Silicon Labs C2 port used to program Silicon micro controller chips (and other 8051 compatible). @@ -24,7 +23,6 @@ if C2PORT config C2PORT_DURAMAR_2150 tristate "C2 port support for Eurotech's Duramar 2150" depends on X86 - default n help This option enables C2 support for the Eurotech's Duramar 2150 on board micro controller. diff --git a/drivers/misc/cb710/Kconfig b/drivers/misc/cb710/Kconfig index 3c7356d55423..a696d7509024 100644 --- a/drivers/misc/cb710/Kconfig +++ b/drivers/misc/cb710/Kconfig @@ -15,7 +15,6 @@ config CB710_CORE config CB710_DEBUG bool "Enable driver debugging" depends on CB710_CORE != n - default n help This is an option for use by developers; most people should say N here. This adds a lot of debugging output to dmesg. diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig index f1d9a843e361..39eec9031487 100644 --- a/drivers/misc/cxl/Kconfig +++ b/drivers/misc/cxl/Kconfig @@ -5,16 +5,13 @@ config CXL_BASE bool - default n select PPC_COPRO_BASE config CXL_AFU_DRIVER_OPS bool - default n config CXL_LIB bool - default n config CXL tristate "Support for IBM Coherent Accelerators (CXL)" diff --git a/drivers/misc/echo/Kconfig b/drivers/misc/echo/Kconfig index 39656413e70d..be70b263e271 100644 --- a/drivers/misc/echo/Kconfig +++ b/drivers/misc/echo/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config ECHO tristate "Line Echo Canceller support" - default n ---help--- This driver provides line echo cancelling support for mISDN and Zaptel drivers. diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index be3263df278a..6f00c33cfe22 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -2,7 +2,7 @@ /* * ee1004 - driver for DDR4 SPD EEPROMs * - * Copyright (C) 2017 Jean Delvare + * Copyright (C) 2017-2019 Jean Delvare * * Based on the at24 driver: * Copyright (C) 2005-2007 David Brownell @@ -53,6 +53,24 @@ MODULE_DEVICE_TABLE(i2c, ee1004_ids); /*-------------------------------------------------------------------------*/ +static int ee1004_get_current_page(void) +{ + int err; + + err = i2c_smbus_read_byte(ee1004_set_page[0]); + if (err == -ENXIO) { + /* Nack means page 1 is selected */ + return 1; + } + if (err < 0) { + /* Anything else is a real error, bail out */ + return err; + } + + /* Ack means page 0 is selected, returned value meaningless */ + return 0; +} + static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, unsigned int offset, size_t count) { @@ -102,6 +120,16 @@ static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, /* Data is ignored */ status = i2c_smbus_write_byte(ee1004_set_page[page], 0x00); + if (status == -ENXIO) { + /* + * Don't give up just yet. Some memory + * modules will select the page but not + * ack the command. Check which page is + * selected now. + */ + if (ee1004_get_current_page() == page) + status = 0; + } if (status < 0) { dev_err(dev, "Failed to select page %d (%d)\n", page, status); @@ -186,17 +214,10 @@ static int ee1004_probe(struct i2c_client *client, } /* Remember current page to avoid unneeded page select */ - err = i2c_smbus_read_byte(ee1004_set_page[0]); - if (err == -ENXIO) { - /* Nack means page 1 is selected */ - ee1004_current_page = 1; - } else if (err < 0) { - /* Anything else is a real error, bail out */ + err = ee1004_get_current_page(); + if (err < 0) goto err_clients; - } else { - /* Ack means page 0 is selected, returned value meaningless */ - ee1004_current_page = 0; - } + ee1004_current_page = err; dev_dbg(&client->dev, "Currently selected page: %d\n", ee1004_current_page); mutex_unlock(&ee1004_bus_lock); diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 8a4659518c33..81c70e5bc168 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -115,7 +115,6 @@ static struct dentry *csr_dbgdir; * @client: i2c client used to perform IO operations * * @ee_file: EEPROM read/write sysfs-file - * @csr_file: CSR read/write debugfs-node */ struct idt_smb_seq; struct idt_89hpesx_dev { @@ -137,7 +136,6 @@ struct idt_89hpesx_dev { struct bin_attribute *ee_file; struct dentry *csr_dir; - struct dentry *csr_file; }; /* @@ -1378,8 +1376,8 @@ static void idt_create_dbgfs_files(struct idt_89hpesx_dev *pdev) pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir); /* Create Debugfs file for CSR read/write operations */ - pdev->csr_file = debugfs_create_file(cli->name, 0600, - pdev->csr_dir, pdev, &csr_dbgfs_ops); + debugfs_create_file(cli->name, 0600, pdev->csr_dir, pdev, + &csr_dbgfs_ops); } /* diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c deleted file mode 100644 index fab02f2da077..000000000000 --- a/drivers/misc/fsa9480.c +++ /dev/null @@ -1,547 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * fsa9480.c - FSA9480 micro USB switch device driver - * - * Copyright (C) 2010 Samsung Electronics - * Minkyu Kang <mk7.kang@samsung.com> - * Wonguk Jeong <wonguk.jeong@samsung.com> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/platform_data/fsa9480.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> - -/* FSA9480 I2C registers */ -#define FSA9480_REG_DEVID 0x01 -#define FSA9480_REG_CTRL 0x02 -#define FSA9480_REG_INT1 0x03 -#define FSA9480_REG_INT2 0x04 -#define FSA9480_REG_INT1_MASK 0x05 -#define FSA9480_REG_INT2_MASK 0x06 -#define FSA9480_REG_ADC 0x07 -#define FSA9480_REG_TIMING1 0x08 -#define FSA9480_REG_TIMING2 0x09 -#define FSA9480_REG_DEV_T1 0x0a -#define FSA9480_REG_DEV_T2 0x0b -#define FSA9480_REG_BTN1 0x0c -#define FSA9480_REG_BTN2 0x0d -#define FSA9480_REG_CK 0x0e -#define FSA9480_REG_CK_INT1 0x0f -#define FSA9480_REG_CK_INT2 0x10 -#define FSA9480_REG_CK_INTMASK1 0x11 -#define FSA9480_REG_CK_INTMASK2 0x12 -#define FSA9480_REG_MANSW1 0x13 -#define FSA9480_REG_MANSW2 0x14 - -/* Control */ -#define CON_SWITCH_OPEN (1 << 4) -#define CON_RAW_DATA (1 << 3) -#define CON_MANUAL_SW (1 << 2) -#define CON_WAIT (1 << 1) -#define CON_INT_MASK (1 << 0) -#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \ - CON_MANUAL_SW | CON_WAIT) - -/* Device Type 1 */ -#define DEV_USB_OTG (1 << 7) -#define DEV_DEDICATED_CHG (1 << 6) -#define DEV_USB_CHG (1 << 5) -#define DEV_CAR_KIT (1 << 4) -#define DEV_UART (1 << 3) -#define DEV_USB (1 << 2) -#define DEV_AUDIO_2 (1 << 1) -#define DEV_AUDIO_1 (1 << 0) - -#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB) -#define DEV_T1_UART_MASK (DEV_UART) -#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG) - -/* Device Type 2 */ -#define DEV_AV (1 << 6) -#define DEV_TTY (1 << 5) -#define DEV_PPD (1 << 4) -#define DEV_JIG_UART_OFF (1 << 3) -#define DEV_JIG_UART_ON (1 << 2) -#define DEV_JIG_USB_OFF (1 << 1) -#define DEV_JIG_USB_ON (1 << 0) - -#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON) -#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON) -#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \ - DEV_JIG_UART_OFF | DEV_JIG_UART_ON) - -/* - * Manual Switch - * D- [7:5] / D+ [4:2] - * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO - */ -#define SW_VAUDIO ((4 << 5) | (4 << 2)) -#define SW_UART ((3 << 5) | (3 << 2)) -#define SW_AUDIO ((2 << 5) | (2 << 2)) -#define SW_DHOST ((1 << 5) | (1 << 2)) -#define SW_AUTO ((0 << 5) | (0 << 2)) - -/* Interrupt 1 */ -#define INT_DETACH (1 << 1) -#define INT_ATTACH (1 << 0) - -struct fsa9480_usbsw { - struct i2c_client *client; - struct fsa9480_platform_data *pdata; - int dev1; - int dev2; - int mansw; -}; - -static struct fsa9480_usbsw *chip; - -static int fsa9480_write_reg(struct i2c_client *client, - int reg, int value) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, reg, value); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int fsa9480_read_reg(struct i2c_client *client, int reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int fsa9480_read_irq(struct i2c_client *client, int *value) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(client, - FSA9480_REG_INT1, 2, (u8 *)value); - *value &= 0xffff; - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static void fsa9480_set_switch(const char *buf) -{ - struct fsa9480_usbsw *usbsw = chip; - struct i2c_client *client = usbsw->client; - unsigned int value; - unsigned int path = 0; - - value = fsa9480_read_reg(client, FSA9480_REG_CTRL); - - if (!strncmp(buf, "VAUDIO", 6)) { - path = SW_VAUDIO; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "UART", 4)) { - path = SW_UART; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "AUDIO", 5)) { - path = SW_AUDIO; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "DHOST", 5)) { - path = SW_DHOST; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "AUTO", 4)) { - path = SW_AUTO; - value |= CON_MANUAL_SW; - } else { - printk(KERN_ERR "Wrong command\n"); - return; - } - - usbsw->mansw = path; - fsa9480_write_reg(client, FSA9480_REG_MANSW1, path); - fsa9480_write_reg(client, FSA9480_REG_CTRL, value); -} - -static ssize_t fsa9480_get_switch(char *buf) -{ - struct fsa9480_usbsw *usbsw = chip; - struct i2c_client *client = usbsw->client; - unsigned int value; - - value = fsa9480_read_reg(client, FSA9480_REG_MANSW1); - - if (value == SW_VAUDIO) - return sprintf(buf, "VAUDIO\n"); - else if (value == SW_UART) - return sprintf(buf, "UART\n"); - else if (value == SW_AUDIO) - return sprintf(buf, "AUDIO\n"); - else if (value == SW_DHOST) - return sprintf(buf, "DHOST\n"); - else if (value == SW_AUTO) - return sprintf(buf, "AUTO\n"); - else - return sprintf(buf, "%x", value); -} - -static ssize_t fsa9480_show_device(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev); - struct i2c_client *client = usbsw->client; - int dev1, dev2; - - dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - - if (!dev1 && !dev2) - return sprintf(buf, "NONE\n"); - - /* USB */ - if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK) - return sprintf(buf, "USB\n"); - - /* UART */ - if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK) - return sprintf(buf, "UART\n"); - - /* CHARGER */ - if (dev1 & DEV_T1_CHARGER_MASK) - return sprintf(buf, "CHARGER\n"); - - /* JIG */ - if (dev2 & DEV_T2_JIG_MASK) - return sprintf(buf, "JIG\n"); - - return sprintf(buf, "UNKNOWN\n"); -} - -static ssize_t fsa9480_show_manualsw(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return fsa9480_get_switch(buf); - -} - -static ssize_t fsa9480_set_manualsw(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - fsa9480_set_switch(buf); - - return count; -} - -static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL); -static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR, - fsa9480_show_manualsw, fsa9480_set_manualsw); - -static struct attribute *fsa9480_attributes[] = { - &dev_attr_device.attr, - &dev_attr_switch.attr, - NULL -}; - -static const struct attribute_group fsa9480_group = { - .attrs = fsa9480_attributes, -}; - -static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr) -{ - int val1, val2, ctrl; - struct fsa9480_platform_data *pdata = usbsw->pdata; - struct i2c_client *client = usbsw->client; - - val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL); - - dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n", - intr, val1, val2); - - if (!intr) - goto out; - - if (intr & INT_ATTACH) { /* Attached */ - /* USB */ - if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) { - if (pdata->usb_cb) - pdata->usb_cb(FSA9480_ATTACHED); - - if (usbsw->mansw) { - fsa9480_write_reg(client, - FSA9480_REG_MANSW1, usbsw->mansw); - } - } - - /* UART */ - if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) { - if (pdata->uart_cb) - pdata->uart_cb(FSA9480_ATTACHED); - - if (!(ctrl & CON_MANUAL_SW)) { - fsa9480_write_reg(client, - FSA9480_REG_MANSW1, SW_UART); - } - } - - /* CHARGER */ - if (val1 & DEV_T1_CHARGER_MASK) { - if (pdata->charger_cb) - pdata->charger_cb(FSA9480_ATTACHED); - } - - /* JIG */ - if (val2 & DEV_T2_JIG_MASK) { - if (pdata->jig_cb) - pdata->jig_cb(FSA9480_ATTACHED); - } - } else if (intr & INT_DETACH) { /* Detached */ - /* USB */ - if (usbsw->dev1 & DEV_T1_USB_MASK || - usbsw->dev2 & DEV_T2_USB_MASK) { - if (pdata->usb_cb) - pdata->usb_cb(FSA9480_DETACHED); - } - - /* UART */ - if (usbsw->dev1 & DEV_T1_UART_MASK || - usbsw->dev2 & DEV_T2_UART_MASK) { - if (pdata->uart_cb) - pdata->uart_cb(FSA9480_DETACHED); - } - - /* CHARGER */ - if (usbsw->dev1 & DEV_T1_CHARGER_MASK) { - if (pdata->charger_cb) - pdata->charger_cb(FSA9480_DETACHED); - } - - /* JIG */ - if (usbsw->dev2 & DEV_T2_JIG_MASK) { - if (pdata->jig_cb) - pdata->jig_cb(FSA9480_DETACHED); - } - } - - usbsw->dev1 = val1; - usbsw->dev2 = val2; - -out: - ctrl &= ~CON_INT_MASK; - fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl); -} - -static irqreturn_t fsa9480_irq_handler(int irq, void *data) -{ - struct fsa9480_usbsw *usbsw = data; - struct i2c_client *client = usbsw->client; - int intr; - - /* clear interrupt */ - fsa9480_read_irq(client, &intr); - - /* device detection */ - fsa9480_detect_dev(usbsw, intr); - - return IRQ_HANDLED; -} - -static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw) -{ - struct fsa9480_platform_data *pdata = usbsw->pdata; - struct i2c_client *client = usbsw->client; - int ret; - int intr; - unsigned int ctrl = CON_MASK; - - /* clear interrupt */ - fsa9480_read_irq(client, &intr); - - /* unmask interrupt (attach/detach only) */ - fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc); - fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f); - - usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1); - - if (usbsw->mansw) - ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */ - - fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl); - - if (pdata && pdata->cfg_gpio) - pdata->cfg_gpio(); - - if (client->irq) { - ret = request_threaded_irq(client->irq, NULL, - fsa9480_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "fsa9480 micro USB", usbsw); - if (ret) { - dev_err(&client->dev, "failed to request IRQ\n"); - return ret; - } - - if (pdata) - device_init_wakeup(&client->dev, pdata->wakeup); - } - - return 0; -} - -static int fsa9480_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct fsa9480_usbsw *usbsw; - int ret = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL); - if (!usbsw) { - dev_err(&client->dev, "failed to allocate driver data\n"); - return -ENOMEM; - } - - usbsw->client = client; - usbsw->pdata = client->dev.platform_data; - - chip = usbsw; - - i2c_set_clientdata(client, usbsw); - - ret = fsa9480_irq_init(usbsw); - if (ret) - goto fail1; - - ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group); - if (ret) { - dev_err(&client->dev, - "failed to create fsa9480 attribute group\n"); - goto fail2; - } - - /* ADC Detect Time: 500ms */ - fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6); - - if (chip->pdata->reset_cb) - chip->pdata->reset_cb(); - - /* device detection */ - fsa9480_detect_dev(usbsw, INT_ATTACH); - - pm_runtime_set_active(&client->dev); - - return 0; - -fail2: - if (client->irq) - free_irq(client->irq, usbsw); -fail1: - kfree(usbsw); - return ret; -} - -static int fsa9480_remove(struct i2c_client *client) -{ - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - - if (client->irq) - free_irq(client->irq, usbsw); - - sysfs_remove_group(&client->dev.kobj, &fsa9480_group); - device_init_wakeup(&client->dev, 0); - kfree(usbsw); - return 0; -} - -#ifdef CONFIG_PM_SLEEP - -static int fsa9480_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - struct fsa9480_platform_data *pdata = usbsw->pdata; - - if (device_may_wakeup(&client->dev) && client->irq) - enable_irq_wake(client->irq); - - if (pdata->usb_power) - pdata->usb_power(0); - - return 0; -} - -static int fsa9480_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - int dev1, dev2; - - if (device_may_wakeup(&client->dev) && client->irq) - disable_irq_wake(client->irq); - - /* - * Clear Pending interrupt. Note that detect_dev does what - * the interrupt handler does. So, we don't miss pending and - * we reenable interrupt if there is one. - */ - fsa9480_read_reg(client, FSA9480_REG_INT1); - fsa9480_read_reg(client, FSA9480_REG_INT2); - - dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - - /* device detection */ - fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume); -#define FSA9480_PM_OPS (&fsa9480_pm_ops) - -#else - -#define FSA9480_PM_OPS NULL - -#endif /* CONFIG_PM_SLEEP */ - -static const struct i2c_device_id fsa9480_id[] = { - {"fsa9480", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, fsa9480_id); - -static struct i2c_driver fsa9480_i2c_driver = { - .driver = { - .name = "fsa9480", - .pm = FSA9480_PM_OPS, - }, - .probe = fsa9480_probe, - .remove = fsa9480_remove, - .id_table = fsa9480_id, -}; - -module_i2c_driver(fsa9480_i2c_driver); - -MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); -MODULE_DESCRIPTION("FSA9480 USB Switch driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/genwqe/Kconfig b/drivers/misc/genwqe/Kconfig index a8a608713d26..97f64bcf9fe0 100644 --- a/drivers/misc/genwqe/Kconfig +++ b/drivers/misc/genwqe/Kconfig @@ -7,7 +7,6 @@ menuconfig GENWQE tristate "GenWQE PCIe Accelerator" depends on PCI && 64BIT select CRC_ITU_T - default n help Enables PCIe card driver for IBM GenWQE accelerators. The user-space interface is described in diff --git a/drivers/misc/habanalabs/asid.c b/drivers/misc/habanalabs/asid.c index f54e7971a762..2c01461701a3 100644 --- a/drivers/misc/habanalabs/asid.c +++ b/drivers/misc/habanalabs/asid.c @@ -18,7 +18,7 @@ int hl_asid_init(struct hl_device *hdev) mutex_init(&hdev->asid_mutex); - /* ASID 0 is reserved for KMD */ + /* ASID 0 is reserved for KMD and device CPU */ set_bit(0, hdev->asid_bitmap); return 0; diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 6fe785e26859..6ad83d5ef4b0 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -682,14 +682,12 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) u32 tmp; rc = hl_poll_timeout_memory(hdev, - (u64) (uintptr_t) &ctx->thread_ctx_switch_wait_token, - jiffies_to_usecs(hdev->timeout_jiffies), - &tmp); + &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1), + 100, jiffies_to_usecs(hdev->timeout_jiffies)); - if (rc || !tmp) { + if (rc == -ETIMEDOUT) { dev_err(hdev->dev, - "context switch phase didn't finish in time\n"); - rc = -ETIMEDOUT; + "context switch phase timeout (%d)\n", tmp); goto out; } } diff --git a/drivers/misc/habanalabs/context.c b/drivers/misc/habanalabs/context.c index f4c92f110a72..8682590e3f6e 100644 --- a/drivers/misc/habanalabs/context.c +++ b/drivers/misc/habanalabs/context.c @@ -31,9 +31,13 @@ static void hl_ctx_fini(struct hl_ctx *ctx) * Coresight might be still working by accessing addresses * related to the stopped engines. Hence stop it explicitly. */ - hdev->asic_funcs->halt_coresight(hdev); + if (hdev->in_debug) + hl_device_set_debug_mode(hdev, false); + hl_vm_ctx_fini(ctx); hl_asid_free(hdev, ctx->asid); + } else { + hl_mmu_ctx_fini(ctx); } } @@ -117,6 +121,11 @@ int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) if (is_kernel_ctx) { ctx->asid = HL_KERNEL_ASID_ID; /* KMD gets ASID 0 */ + rc = hl_mmu_ctx_init(ctx); + if (rc) { + dev_err(hdev->dev, "Failed to init mmu ctx module\n"); + goto mem_ctx_err; + } } else { ctx->asid = hl_asid_alloc(hdev); if (!ctx->asid) { diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c index ba418aaa404c..18e499c900c7 100644 --- a/drivers/misc/habanalabs/debugfs.c +++ b/drivers/misc/habanalabs/debugfs.c @@ -355,7 +355,7 @@ static int mmu_show(struct seq_file *s, void *data) struct hl_debugfs_entry *entry = s->private; struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; - struct hl_ctx *ctx = hdev->user_ctx; + struct hl_ctx *ctx; u64 hop0_addr = 0, hop0_pte_addr = 0, hop0_pte = 0, hop1_addr = 0, hop1_pte_addr = 0, hop1_pte = 0, @@ -367,6 +367,11 @@ static int mmu_show(struct seq_file *s, void *data) if (!hdev->mmu_enable) return 0; + if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID) + ctx = hdev->kernel_ctx; + else + ctx = hdev->user_ctx; + if (!ctx) { dev_err(hdev->dev, "no ctx available\n"); return 0; @@ -495,6 +500,36 @@ err: return -EINVAL; } +static int engines_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_device *hdev = dev_entry->hdev; + + hdev->asic_funcs->is_device_idle(hdev, NULL, s); + + return 0; +} + +static bool hl_is_device_va(struct hl_device *hdev, u64 addr) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + + if (!hdev->mmu_enable) + goto out; + + if (hdev->dram_supports_virtual_memory && + addr >= prop->va_space_dram_start_address && + addr < prop->va_space_dram_end_address) + return true; + + if (addr >= prop->va_space_host_start_address && + addr < prop->va_space_host_end_address) + return true; +out: + return false; +} + static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u64 *phys_addr) { @@ -568,7 +603,6 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, { struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_device *hdev = entry->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; char tmp_buf[32]; u64 addr = entry->addr; u32 val; @@ -577,11 +611,8 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, if (*ppos) return 0; - if (addr >= prop->va_space_dram_start_address && - addr < prop->va_space_dram_end_address && - hdev->mmu_enable && - hdev->dram_supports_virtual_memory) { - rc = device_va_to_pa(hdev, entry->addr, &addr); + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); if (rc) return rc; } @@ -602,7 +633,6 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf, { struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_device *hdev = entry->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; u64 addr = entry->addr; u32 value; ssize_t rc; @@ -611,11 +641,8 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf, if (rc) return rc; - if (addr >= prop->va_space_dram_start_address && - addr < prop->va_space_dram_end_address && - hdev->mmu_enable && - hdev->dram_supports_virtual_memory) { - rc = device_va_to_pa(hdev, entry->addr, &addr); + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); if (rc) return rc; } @@ -877,6 +904,7 @@ static const struct hl_info_list hl_debugfs_list[] = { {"userptr", userptr_show, NULL}, {"vm", vm_show, NULL}, {"mmu", mmu_show, mmu_write}, + {"engines", engines_show, NULL} }; static int hl_debugfs_open(struct inode *inode, struct file *file) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 0b19d3eefb98..0c4894dd9c02 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -231,6 +231,7 @@ static int device_early_init(struct hl_device *hdev) mutex_init(&hdev->fd_open_cnt_lock); mutex_init(&hdev->send_cpu_message_lock); + mutex_init(&hdev->debug_lock); mutex_init(&hdev->mmu_cache_lock); INIT_LIST_HEAD(&hdev->hw_queues_mirror_list); spin_lock_init(&hdev->hw_queues_mirror_lock); @@ -262,6 +263,7 @@ early_fini: static void device_early_fini(struct hl_device *hdev) { mutex_destroy(&hdev->mmu_cache_lock); + mutex_destroy(&hdev->debug_lock); mutex_destroy(&hdev->send_cpu_message_lock); hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); @@ -324,7 +326,15 @@ static int device_late_init(struct hl_device *hdev) { int rc; - INIT_DELAYED_WORK(&hdev->work_freq, set_freq_to_low_job); + if (hdev->asic_funcs->late_init) { + rc = hdev->asic_funcs->late_init(hdev); + if (rc) { + dev_err(hdev->dev, + "failed late initialization for the H/W\n"); + return rc; + } + } + hdev->high_pll = hdev->asic_prop.high_pll; /* force setting to low frequency */ @@ -335,17 +345,9 @@ static int device_late_init(struct hl_device *hdev) else hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); - if (hdev->asic_funcs->late_init) { - rc = hdev->asic_funcs->late_init(hdev); - if (rc) { - dev_err(hdev->dev, - "failed late initialization for the H/W\n"); - return rc; - } - } - + INIT_DELAYED_WORK(&hdev->work_freq, set_freq_to_low_job); schedule_delayed_work(&hdev->work_freq, - usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); + usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); if (hdev->heartbeat) { INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat); @@ -420,6 +422,52 @@ int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq) return 1; } +int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) +{ + int rc = 0; + + mutex_lock(&hdev->debug_lock); + + if (!enable) { + if (!hdev->in_debug) { + dev_err(hdev->dev, + "Failed to disable debug mode because device was not in debug mode\n"); + rc = -EFAULT; + goto out; + } + + hdev->asic_funcs->halt_coresight(hdev); + hdev->in_debug = 0; + + goto out; + } + + if (hdev->in_debug) { + dev_err(hdev->dev, + "Failed to enable debug mode because device is already in debug mode\n"); + rc = -EFAULT; + goto out; + } + + mutex_lock(&hdev->fd_open_cnt_lock); + + if (atomic_read(&hdev->fd_open_cnt) > 1) { + dev_err(hdev->dev, + "Failed to enable debug mode. More then a single user is using the device\n"); + rc = -EPERM; + goto unlock_fd_open_lock; + } + + hdev->in_debug = 1; + +unlock_fd_open_lock: + mutex_unlock(&hdev->fd_open_cnt_lock); +out: + mutex_unlock(&hdev->debug_lock); + + return rc; +} + /* * hl_device_suspend - initiate device suspend * @@ -647,13 +695,6 @@ again: hdev->hard_reset_pending = true; - if (!hdev->pdev) { - dev_err(hdev->dev, - "Reset action is NOT supported in simulator\n"); - rc = -EINVAL; - goto out_err; - } - device_reset_work = kzalloc(sizeof(*device_reset_work), GFP_ATOMIC); if (!device_reset_work) { @@ -704,6 +745,7 @@ again: if (hard_reset) { hl_vm_fini(hdev); + hl_mmu_fini(hdev); hl_eq_reset(hdev, &hdev->event_queue); } @@ -731,6 +773,13 @@ again: goto out_err; } + rc = hl_mmu_init(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to initialize MMU S/W after hard reset\n"); + goto out_err; + } + /* Allocate the kernel context */ hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); @@ -902,11 +951,18 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) goto cq_fini; } + /* MMU S/W must be initialized before kernel context is created */ + rc = hl_mmu_init(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize MMU S/W structures\n"); + goto eq_fini; + } + /* Allocate the kernel context */ hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); if (!hdev->kernel_ctx) { rc = -ENOMEM; - goto eq_fini; + goto mmu_fini; } hdev->user_ctx = NULL; @@ -954,8 +1010,6 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) goto out_disabled; } - /* After test_queues, KMD can start sending messages to device CPU */ - rc = device_late_init(hdev); if (rc) { dev_err(hdev->dev, "Failed late initialization\n"); @@ -1001,6 +1055,8 @@ release_ctx: "kernel ctx is still alive on initialization failure\n"); free_ctx: kfree(hdev->kernel_ctx); +mmu_fini: + hl_mmu_fini(hdev); eq_fini: hl_eq_fini(hdev, &hdev->event_queue); cq_fini: @@ -1105,6 +1161,8 @@ void hl_device_fini(struct hl_device *hdev) hl_vm_fini(hdev); + hl_mmu_fini(hdev); + hl_eq_fini(hdev, &hdev->event_queue); for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) @@ -1126,95 +1184,6 @@ void hl_device_fini(struct hl_device *hdev) } /* - * hl_poll_timeout_memory - Periodically poll a host memory address - * until it is not zero or a timeout occurs - * @hdev: pointer to habanalabs device structure - * @addr: Address to poll - * @timeout_us: timeout in us - * @val: Variable to read the value into - * - * Returns 0 on success and -ETIMEDOUT upon a timeout. In either - * case, the last read value at @addr is stored in @val. Must not - * be called from atomic context if sleep_us or timeout_us are used. - * - * The function sleeps for 100us with timeout value of - * timeout_us - */ -int hl_poll_timeout_memory(struct hl_device *hdev, u64 addr, - u32 timeout_us, u32 *val) -{ - /* - * address in this function points always to a memory location in the - * host's (server's) memory. That location is updated asynchronously - * either by the direct access of the device or by another core - */ - u32 *paddr = (u32 *) (uintptr_t) addr; - ktime_t timeout; - - /* timeout should be longer when working with simulator */ - if (!hdev->pdev) - timeout_us *= 10; - - timeout = ktime_add_us(ktime_get(), timeout_us); - - might_sleep(); - - for (;;) { - /* - * Flush CPU read/write buffers to make sure we read updates - * done by other cores or by the device - */ - mb(); - *val = *paddr; - if (*val) - break; - if (ktime_compare(ktime_get(), timeout) > 0) { - *val = *paddr; - break; - } - usleep_range((100 >> 2) + 1, 100); - } - - return *val ? 0 : -ETIMEDOUT; -} - -/* - * hl_poll_timeout_devicememory - Periodically poll a device memory address - * until it is not zero or a timeout occurs - * @hdev: pointer to habanalabs device structure - * @addr: Device address to poll - * @timeout_us: timeout in us - * @val: Variable to read the value into - * - * Returns 0 on success and -ETIMEDOUT upon a timeout. In either - * case, the last read value at @addr is stored in @val. Must not - * be called from atomic context if sleep_us or timeout_us are used. - * - * The function sleeps for 100us with timeout value of - * timeout_us - */ -int hl_poll_timeout_device_memory(struct hl_device *hdev, void __iomem *addr, - u32 timeout_us, u32 *val) -{ - ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); - - might_sleep(); - - for (;;) { - *val = readl(addr); - if (*val) - break; - if (ktime_compare(ktime_get(), timeout) > 0) { - *val = readl(addr); - break; - } - usleep_range((100 >> 2) + 1, 100); - } - - return *val ? 0 : -ETIMEDOUT; -} - -/* * MMIO register access helper functions. */ diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index eda5d7fcb79f..cc8168bacb24 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -29,13 +29,13 @@ int hl_fw_push_fw_to_device(struct hl_device *hdev, const char *fw_name, rc = request_firmware(&fw, fw_name, hdev->dev); if (rc) { - dev_err(hdev->dev, "Failed to request %s\n", fw_name); + dev_err(hdev->dev, "Firmware file %s is not found!\n", fw_name); goto out; } fw_size = fw->size; if ((fw_size % 4) != 0) { - dev_err(hdev->dev, "illegal %s firmware size %zu\n", + dev_err(hdev->dev, "Illegal %s firmware size %zu\n", fw_name, fw_size); rc = -EINVAL; goto out; @@ -85,12 +85,6 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, u32 tmp; int rc = 0; - if (len > HL_CPU_CB_SIZE) { - dev_err(hdev->dev, "Invalid CPU message size of %d bytes\n", - len); - return -ENOMEM; - } - pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr); if (!pkt) { @@ -117,33 +111,28 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, goto out; } - rc = hl_poll_timeout_memory(hdev, (u64) (uintptr_t) &pkt->fence, - timeout, &tmp); + rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp, + (tmp == ARMCP_PACKET_FENCE_VAL), 1000, timeout); hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); if (rc == -ETIMEDOUT) { - dev_err(hdev->dev, "Timeout while waiting for device CPU\n"); + dev_err(hdev->dev, "Device CPU packet timeout (0x%x)\n", tmp); hdev->device_cpu_disabled = true; goto out; } - if (tmp == ARMCP_PACKET_FENCE_VAL) { - u32 ctl = le32_to_cpu(pkt->ctl); + tmp = le32_to_cpu(pkt->ctl); - rc = (ctl & ARMCP_PKT_CTL_RC_MASK) >> ARMCP_PKT_CTL_RC_SHIFT; - if (rc) { - dev_err(hdev->dev, - "F/W ERROR %d for CPU packet %d\n", - rc, (ctl & ARMCP_PKT_CTL_OPCODE_MASK) + rc = (tmp & ARMCP_PKT_CTL_RC_MASK) >> ARMCP_PKT_CTL_RC_SHIFT; + if (rc) { + dev_err(hdev->dev, "F/W ERROR %d for CPU packet %d\n", + rc, + (tmp & ARMCP_PKT_CTL_OPCODE_MASK) >> ARMCP_PKT_CTL_OPCODE_SHIFT); - rc = -EINVAL; - } else if (result) { - *result = (long) le64_to_cpu(pkt->result); - } - } else { - dev_err(hdev->dev, "CPU packet wrong fence value\n"); - rc = -EINVAL; + rc = -EIO; + } else if (result) { + *result = (long) le64_to_cpu(pkt->result); } out: @@ -186,9 +175,6 @@ void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, { u64 kernel_addr; - /* roundup to HL_CPU_PKT_SIZE */ - size = (size + (HL_CPU_PKT_SIZE - 1)) & HL_CPU_PKT_MASK; - kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size); *dma_handle = hdev->cpu_accessible_dma_address + @@ -200,9 +186,6 @@ void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr) { - /* roundup to HL_CPU_PKT_SIZE */ - size = (size + (HL_CPU_PKT_SIZE - 1)) & HL_CPU_PKT_MASK; - gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) (uintptr_t) vaddr, size); } @@ -256,7 +239,7 @@ int hl_fw_armcp_info_get(struct hl_device *hdev) HL_ARMCP_INFO_TIMEOUT_USEC, &result); if (rc) { dev_err(hdev->dev, - "Failed to send armcp info pkt, error %d\n", rc); + "Failed to send ArmCP info pkt, error %d\n", rc); goto out; } @@ -291,7 +274,7 @@ int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) max_size, &eeprom_info_dma_addr); if (!eeprom_info_cpu_addr) { dev_err(hdev->dev, - "Failed to allocate DMA memory for EEPROM info packet\n"); + "Failed to allocate DMA memory for ArmCP EEPROM packet\n"); return -ENOMEM; } @@ -307,7 +290,7 @@ int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) if (rc) { dev_err(hdev->dev, - "Failed to send armcp EEPROM pkt, error %d\n", rc); + "Failed to send ArmCP EEPROM packet, error %d\n", rc); goto out; } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 02d116b01a1a..75294ec65257 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -14,6 +14,8 @@ #include <linux/genalloc.h> #include <linux/hwmon.h> #include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/iommu.h> +#include <linux/seq_file.h> /* * GOYA security scheme: @@ -89,6 +91,30 @@ #define GOYA_CB_POOL_CB_CNT 512 #define GOYA_CB_POOL_CB_SIZE 0x20000 /* 128KB */ +#define IS_QM_IDLE(engine, qm_glbl_sts0) \ + (((qm_glbl_sts0) & engine##_QM_IDLE_MASK) == engine##_QM_IDLE_MASK) +#define IS_DMA_QM_IDLE(qm_glbl_sts0) IS_QM_IDLE(DMA, qm_glbl_sts0) +#define IS_TPC_QM_IDLE(qm_glbl_sts0) IS_QM_IDLE(TPC, qm_glbl_sts0) +#define IS_MME_QM_IDLE(qm_glbl_sts0) IS_QM_IDLE(MME, qm_glbl_sts0) + +#define IS_CMDQ_IDLE(engine, cmdq_glbl_sts0) \ + (((cmdq_glbl_sts0) & engine##_CMDQ_IDLE_MASK) == \ + engine##_CMDQ_IDLE_MASK) +#define IS_TPC_CMDQ_IDLE(cmdq_glbl_sts0) \ + IS_CMDQ_IDLE(TPC, cmdq_glbl_sts0) +#define IS_MME_CMDQ_IDLE(cmdq_glbl_sts0) \ + IS_CMDQ_IDLE(MME, cmdq_glbl_sts0) + +#define IS_DMA_IDLE(dma_core_sts0) \ + !((dma_core_sts0) & DMA_CH_0_STS0_DMA_BUSY_MASK) + +#define IS_TPC_IDLE(tpc_cfg_sts) \ + (((tpc_cfg_sts) & TPC_CFG_IDLE_MASK) == TPC_CFG_IDLE_MASK) + +#define IS_MME_IDLE(mme_arch_sts) \ + (((mme_arch_sts) & MME_ARCH_IDLE_MASK) == MME_ARCH_IDLE_MASK) + + static const char goya_irq_name[GOYA_MSIX_ENTRIES][GOYA_MAX_STRING_LEN] = { "goya cq 0", "goya cq 1", "goya cq 2", "goya cq 3", "goya cq 4", "goya cpu eq" @@ -297,6 +323,11 @@ static u32 goya_all_events[] = { GOYA_ASYNC_EVENT_ID_DMA_BM_CH4 }; +static int goya_mmu_clear_pgt_range(struct hl_device *hdev); +static int goya_mmu_set_dram_default_page(struct hl_device *hdev); +static int goya_mmu_add_mappings_for_device_cpu(struct hl_device *hdev); +static void goya_mmu_prepare(struct hl_device *hdev, u32 asid); + void goya_get_fixed_properties(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; @@ -467,7 +498,7 @@ static int goya_early_init(struct hl_device *hdev) prop->dram_pci_bar_size = pci_resource_len(pdev, DDR_BAR_ID); - rc = hl_pci_init(hdev, 39); + rc = hl_pci_init(hdev, 48); if (rc) return rc; @@ -539,9 +570,36 @@ int goya_late_init(struct hl_device *hdev) struct asic_fixed_properties *prop = &hdev->asic_prop; int rc; + goya_fetch_psoc_frequency(hdev); + + rc = goya_mmu_clear_pgt_range(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to clear MMU page tables range %d\n", rc); + return rc; + } + + rc = goya_mmu_set_dram_default_page(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to set DRAM default page %d\n", rc); + return rc; + } + + rc = goya_mmu_add_mappings_for_device_cpu(hdev); + if (rc) + return rc; + + rc = goya_init_cpu_queues(hdev); + if (rc) + return rc; + + rc = goya_test_cpu_queue(hdev); + if (rc) + return rc; + rc = goya_armcp_info_get(hdev); if (rc) { - dev_err(hdev->dev, "Failed to get armcp info\n"); + dev_err(hdev->dev, "Failed to get armcp info %d\n", rc); return rc; } @@ -553,33 +611,15 @@ int goya_late_init(struct hl_device *hdev) rc = hl_fw_send_pci_access_msg(hdev, ARMCP_PACKET_ENABLE_PCI_ACCESS); if (rc) { - dev_err(hdev->dev, "Failed to enable PCI access from CPU\n"); + dev_err(hdev->dev, + "Failed to enable PCI access from CPU %d\n", rc); return rc; } WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GOYA_ASYNC_EVENT_ID_INTS_REGISTER); - goya_fetch_psoc_frequency(hdev); - - rc = goya_mmu_clear_pgt_range(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to clear MMU page tables range\n"); - goto disable_pci_access; - } - - rc = goya_mmu_set_dram_default_page(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to set DRAM default page\n"); - goto disable_pci_access; - } - return 0; - -disable_pci_access: - hl_fw_send_pci_access_msg(hdev, ARMCP_PACKET_DISABLE_PCI_ACCESS); - - return rc; } /* @@ -655,7 +695,10 @@ static int goya_sw_init(struct hl_device *hdev) goto free_dma_pool; } - hdev->cpu_accessible_dma_pool = gen_pool_create(HL_CPU_PKT_SHIFT, -1); + dev_dbg(hdev->dev, "cpu accessible memory at bus address 0x%llx\n", + hdev->cpu_accessible_dma_address); + + hdev->cpu_accessible_dma_pool = gen_pool_create(ilog2(32), -1); if (!hdev->cpu_accessible_dma_pool) { dev_err(hdev->dev, "Failed to create CPU accessible DMA pool\n"); @@ -786,7 +829,6 @@ static void goya_init_dma_ch(struct hl_device *hdev, int dma_id) else sob_addr = CFG_BASE + mmSYNC_MNGR_SOB_OBJ_1007; - WREG32(mmDMA_CH_0_WR_COMP_ADDR_LO + reg_off, lower_32_bits(sob_addr)); WREG32(mmDMA_CH_0_WR_COMP_ADDR_HI + reg_off, upper_32_bits(sob_addr)); WREG32(mmDMA_CH_0_WR_COMP_WDATA + reg_off, 0x80000001); } @@ -973,9 +1015,9 @@ int goya_init_cpu_queues(struct hl_device *hdev) WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_3, upper_32_bits(eq->bus_address)); WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_8, - lower_32_bits(hdev->cpu_accessible_dma_address)); + lower_32_bits(VA_CPU_ACCESSIBLE_MEM_ADDR)); WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_9, - upper_32_bits(hdev->cpu_accessible_dma_address)); + upper_32_bits(VA_CPU_ACCESSIBLE_MEM_ADDR)); WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_5, HL_QUEUE_SIZE_IN_BYTES); WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_4, HL_EQ_SIZE_IN_BYTES); @@ -1001,7 +1043,7 @@ int goya_init_cpu_queues(struct hl_device *hdev) if (err) { dev_err(hdev->dev, - "Failed to communicate with ARM CPU (ArmCP timeout)\n"); + "Failed to setup communication with device CPU\n"); return -EIO; } @@ -2061,10 +2103,12 @@ static void goya_halt_engines(struct hl_device *hdev, bool hard_reset) goya_disable_external_queues(hdev); goya_disable_internal_queues(hdev); - if (hard_reset) + if (hard_reset) { goya_disable_msix(hdev); - else + goya_mmu_remove_device_cpu_mappings(hdev); + } else { goya_sync_irqs(hdev); + } } /* @@ -2277,14 +2321,14 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout) goya_read_device_fw_version(hdev, FW_COMP_UBOOT); goya_read_device_fw_version(hdev, FW_COMP_PREBOOT); - if (status == CPU_BOOT_STATUS_SRAM_AVAIL) - goto out; - if (!hdev->fw_loading) { dev_info(hdev->dev, "Skip loading FW\n"); goto out; } + if (status == CPU_BOOT_STATUS_SRAM_AVAIL) + goto out; + rc = goya_push_linux_to_device(hdev); if (rc) return rc; @@ -2466,34 +2510,11 @@ static int goya_hw_init(struct hl_device *hdev) if (rc) goto disable_queues; - rc = goya_init_cpu_queues(hdev); - if (rc) { - dev_err(hdev->dev, "failed to initialize CPU H/W queues %d\n", - rc); - goto disable_msix; - } - - /* - * Check if we managed to set the DMA mask to more then 32 bits. If so, - * let's try to increase it again because in Goya we set the initial - * dma mask to less then 39 bits so that the allocation of the memory - * area for the device's cpu will be under 39 bits - */ - if (hdev->dma_mask > 32) { - rc = hl_pci_set_dma_mask(hdev, 48); - if (rc) - goto disable_pci_access; - } - /* Perform read from the device to flush all MSI-X configuration */ val = RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); return 0; -disable_pci_access: - hl_fw_send_pci_access_msg(hdev, ARMCP_PACKET_DISABLE_PCI_ACCESS); -disable_msix: - goya_disable_msix(hdev); disable_queues: goya_disable_internal_queues(hdev); goya_disable_external_queues(hdev); @@ -2629,7 +2650,6 @@ static int goya_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma, void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) { u32 db_reg_offset, db_value; - bool invalid_queue = false; switch (hw_queue_id) { case GOYA_QUEUE_ID_DMA_0: @@ -2653,10 +2673,7 @@ void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) break; case GOYA_QUEUE_ID_CPU_PQ: - if (hdev->cpu_queues_enable) - db_reg_offset = mmCPU_IF_PF_PQ_PI; - else - invalid_queue = true; + db_reg_offset = mmCPU_IF_PF_PQ_PI; break; case GOYA_QUEUE_ID_MME: @@ -2696,12 +2713,8 @@ void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) break; default: - invalid_queue = true; - } - - if (invalid_queue) { /* Should never get here */ - dev_err(hdev->dev, "h/w queue %d is invalid. Can't set pi\n", + dev_err(hdev->dev, "H/W queue %d is invalid. Can't set pi\n", hw_queue_id); return; } @@ -2808,7 +2821,6 @@ static int goya_send_job_on_qman0(struct hl_device *hdev, struct hl_cs_job *job) dma_addr_t fence_dma_addr; struct hl_cb *cb; u32 tmp, timeout; - char buf[16] = {}; int rc; if (hdev->pldm) @@ -2816,10 +2828,9 @@ static int goya_send_job_on_qman0(struct hl_device *hdev, struct hl_cs_job *job) else timeout = HL_DEVICE_TIMEOUT_USEC; - if (!hdev->asic_funcs->is_device_idle(hdev, buf, sizeof(buf))) { + if (!hdev->asic_funcs->is_device_idle(hdev, NULL, NULL)) { dev_err_ratelimited(hdev->dev, - "Can't send KMD job on QMAN0 because %s is busy\n", - buf); + "Can't send KMD job on QMAN0 because the device is not idle\n"); return -EBUSY; } @@ -2831,16 +2842,8 @@ static int goya_send_job_on_qman0(struct hl_device *hdev, struct hl_cs_job *job) return -ENOMEM; } - *fence_ptr = 0; - goya_qman0_set_security(hdev, true); - /* - * goya cs parser saves space for 2xpacket_msg_prot at end of CB. For - * synchronized kernel jobs we only need space for 1 packet_msg_prot - */ - job->job_cb_size -= sizeof(struct packet_msg_prot); - cb = job->patched_cb; fence_pkt = (struct packet_msg_prot *) (uintptr_t) (cb->kernel_address + @@ -2860,14 +2863,14 @@ static int goya_send_job_on_qman0(struct hl_device *hdev, struct hl_cs_job *job) goto free_fence_ptr; } - rc = hl_poll_timeout_memory(hdev, (u64) (uintptr_t) fence_ptr, timeout, - &tmp); + rc = hl_poll_timeout_memory(hdev, fence_ptr, tmp, + (tmp == GOYA_QMAN0_FENCE_VAL), 1000, timeout); hl_hw_queue_inc_ci_kernel(hdev, GOYA_QUEUE_ID_DMA_0); - if ((rc) || (tmp != GOYA_QMAN0_FENCE_VAL)) { - dev_err(hdev->dev, "QMAN0 Job hasn't finished in time\n"); - rc = -ETIMEDOUT; + if (rc == -ETIMEDOUT) { + dev_err(hdev->dev, "QMAN0 Job timeout (0x%x)\n", tmp); + goto free_fence_ptr; } free_fence_ptr: @@ -2941,20 +2944,19 @@ int goya_test_queue(struct hl_device *hdev, u32 hw_queue_id) goto free_pkt; } - rc = hl_poll_timeout_memory(hdev, (u64) (uintptr_t) fence_ptr, - GOYA_TEST_QUEUE_WAIT_USEC, &tmp); + rc = hl_poll_timeout_memory(hdev, fence_ptr, tmp, (tmp == fence_val), + 1000, GOYA_TEST_QUEUE_WAIT_USEC); hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); - if ((!rc) && (tmp == fence_val)) { - dev_info(hdev->dev, - "queue test on H/W queue %d succeeded\n", - hw_queue_id); - } else { + if (rc == -ETIMEDOUT) { dev_err(hdev->dev, "H/W queue %d test failed (scratch(0x%08llX) == 0x%08X)\n", hw_queue_id, (unsigned long long) fence_dma_addr, tmp); - rc = -EINVAL; + rc = -EIO; + } else { + dev_info(hdev->dev, "queue test on H/W queue %d succeeded\n", + hw_queue_id); } free_pkt: @@ -2990,12 +2992,6 @@ int goya_test_queues(struct hl_device *hdev) ret_val = -EINVAL; } - if (hdev->cpu_queues_enable) { - rc = goya_test_cpu_queue(hdev); - if (rc) - ret_val = -EINVAL; - } - return ret_val; } @@ -3028,7 +3024,13 @@ static void goya_dma_pool_free(struct hl_device *hdev, void *vaddr, void *goya_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle) { - return hl_fw_cpu_accessible_dma_pool_alloc(hdev, size, dma_handle); + void *vaddr; + + vaddr = hl_fw_cpu_accessible_dma_pool_alloc(hdev, size, dma_handle); + *dma_handle = (*dma_handle) - hdev->cpu_accessible_dma_address + + VA_CPU_ACCESSIBLE_MEM_ADDR; + + return vaddr; } void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, @@ -3907,8 +3909,8 @@ int goya_cs_parser(struct hl_device *hdev, struct hl_cs_parser *parser) return goya_parse_cb_no_mmu(hdev, parser); } -void goya_add_end_of_cb_packets(u64 kernel_address, u32 len, u64 cq_addr, - u32 cq_val, u32 msix_vec) +void goya_add_end_of_cb_packets(struct hl_device *hdev, u64 kernel_address, + u32 len, u64 cq_addr, u32 cq_val, u32 msix_vec) { struct packet_msg_prot *cq_pkt; u32 tmp; @@ -3939,6 +3941,11 @@ void goya_update_eq_ci(struct hl_device *hdev, u32 val) void goya_restore_phase_topology(struct hl_device *hdev) { + +} + +static void goya_clear_sm_regs(struct hl_device *hdev) +{ int i, num_of_sob_in_longs, num_of_mon_in_longs; num_of_sob_in_longs = @@ -3958,10 +3965,11 @@ void goya_restore_phase_topology(struct hl_device *hdev) } /* - * goya_debugfs_read32 - read a 32bit value from a given device address + * goya_debugfs_read32 - read a 32bit value from a given device or a host mapped + * address. * * @hdev: pointer to hl_device structure - * @addr: address in device + * @addr: device or host mapped address * @val: returned value * * In case of DDR address that is not mapped into the default aperture that @@ -4002,6 +4010,10 @@ static int goya_debugfs_read32(struct hl_device *hdev, u64 addr, u32 *val) } if (ddr_bar_addr == U64_MAX) rc = -EIO; + + } else if (addr >= HOST_PHYS_BASE && !iommu_present(&pci_bus_type)) { + *val = *(u32 *) phys_to_virt(addr - HOST_PHYS_BASE); + } else { rc = -EFAULT; } @@ -4010,10 +4022,11 @@ static int goya_debugfs_read32(struct hl_device *hdev, u64 addr, u32 *val) } /* - * goya_debugfs_write32 - write a 32bit value to a given device address + * goya_debugfs_write32 - write a 32bit value to a given device or a host mapped + * address. * * @hdev: pointer to hl_device structure - * @addr: address in device + * @addr: device or host mapped address * @val: returned value * * In case of DDR address that is not mapped into the default aperture that @@ -4054,6 +4067,10 @@ static int goya_debugfs_write32(struct hl_device *hdev, u64 addr, u32 val) } if (ddr_bar_addr == U64_MAX) rc = -EIO; + + } else if (addr >= HOST_PHYS_BASE && !iommu_present(&pci_bus_type)) { + *(u32 *) phys_to_virt(addr - HOST_PHYS_BASE) = val; + } else { rc = -EFAULT; } @@ -4086,6 +4103,47 @@ static void goya_write_pte(struct hl_device *hdev, u64 addr, u64 val) static const char *_goya_get_event_desc(u16 event_type) { switch (event_type) { + case GOYA_ASYNC_EVENT_ID_PCIE_IF: + return "PCIe_if"; + case GOYA_ASYNC_EVENT_ID_TPC0_ECC: + case GOYA_ASYNC_EVENT_ID_TPC1_ECC: + case GOYA_ASYNC_EVENT_ID_TPC2_ECC: + case GOYA_ASYNC_EVENT_ID_TPC3_ECC: + case GOYA_ASYNC_EVENT_ID_TPC4_ECC: + case GOYA_ASYNC_EVENT_ID_TPC5_ECC: + case GOYA_ASYNC_EVENT_ID_TPC6_ECC: + case GOYA_ASYNC_EVENT_ID_TPC7_ECC: + return "TPC%d_ecc"; + case GOYA_ASYNC_EVENT_ID_MME_ECC: + return "MME_ecc"; + case GOYA_ASYNC_EVENT_ID_MME_ECC_EXT: + return "MME_ecc_ext"; + case GOYA_ASYNC_EVENT_ID_MMU_ECC: + return "MMU_ecc"; + case GOYA_ASYNC_EVENT_ID_DMA_MACRO: + return "DMA_macro"; + case GOYA_ASYNC_EVENT_ID_DMA_ECC: + return "DMA_ecc"; + case GOYA_ASYNC_EVENT_ID_CPU_IF_ECC: + return "CPU_if_ecc"; + case GOYA_ASYNC_EVENT_ID_PSOC_MEM: + return "PSOC_mem"; + case GOYA_ASYNC_EVENT_ID_PSOC_CORESIGHT: + return "PSOC_coresight"; + case GOYA_ASYNC_EVENT_ID_SRAM0 ... GOYA_ASYNC_EVENT_ID_SRAM29: + return "SRAM%d"; + case GOYA_ASYNC_EVENT_ID_GIC500: + return "GIC500"; + case GOYA_ASYNC_EVENT_ID_PLL0 ... GOYA_ASYNC_EVENT_ID_PLL6: + return "PLL%d"; + case GOYA_ASYNC_EVENT_ID_AXI_ECC: + return "AXI_ecc"; + case GOYA_ASYNC_EVENT_ID_L2_RAM_ECC: + return "L2_ram_ecc"; + case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_05_SW_RESET: + return "PSOC_gpio_05_sw_reset"; + case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_10_VRHOT_ICRIT: + return "PSOC_gpio_10_vrhot_icrit"; case GOYA_ASYNC_EVENT_ID_PCIE_DEC: return "PCIe_dec"; case GOYA_ASYNC_EVENT_ID_TPC0_DEC: @@ -4128,6 +4186,17 @@ static const char *_goya_get_event_desc(u16 event_type) return "DMA%d_qm"; case GOYA_ASYNC_EVENT_ID_DMA0_CH ... GOYA_ASYNC_EVENT_ID_DMA4_CH: return "DMA%d_ch"; + case GOYA_ASYNC_EVENT_ID_TPC0_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC1_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC2_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC3_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC4_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC5_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC6_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC7_BMON_SPMU: + return "TPC%d_bmon_spmu"; + case GOYA_ASYNC_EVENT_ID_DMA_BM_CH0 ... GOYA_ASYNC_EVENT_ID_DMA_BM_CH4: + return "DMA_bm_ch%d"; default: return "N/A"; } @@ -4138,6 +4207,25 @@ static void goya_get_event_desc(u16 event_type, char *desc, size_t size) u8 index; switch (event_type) { + case GOYA_ASYNC_EVENT_ID_TPC0_ECC: + case GOYA_ASYNC_EVENT_ID_TPC1_ECC: + case GOYA_ASYNC_EVENT_ID_TPC2_ECC: + case GOYA_ASYNC_EVENT_ID_TPC3_ECC: + case GOYA_ASYNC_EVENT_ID_TPC4_ECC: + case GOYA_ASYNC_EVENT_ID_TPC5_ECC: + case GOYA_ASYNC_EVENT_ID_TPC6_ECC: + case GOYA_ASYNC_EVENT_ID_TPC7_ECC: + index = (event_type - GOYA_ASYNC_EVENT_ID_TPC0_ECC) / 3; + snprintf(desc, size, _goya_get_event_desc(event_type), index); + break; + case GOYA_ASYNC_EVENT_ID_SRAM0 ... GOYA_ASYNC_EVENT_ID_SRAM29: + index = event_type - GOYA_ASYNC_EVENT_ID_SRAM0; + snprintf(desc, size, _goya_get_event_desc(event_type), index); + break; + case GOYA_ASYNC_EVENT_ID_PLL0 ... GOYA_ASYNC_EVENT_ID_PLL6: + index = event_type - GOYA_ASYNC_EVENT_ID_PLL0; + snprintf(desc, size, _goya_get_event_desc(event_type), index); + break; case GOYA_ASYNC_EVENT_ID_TPC0_DEC: case GOYA_ASYNC_EVENT_ID_TPC1_DEC: case GOYA_ASYNC_EVENT_ID_TPC2_DEC: @@ -4176,6 +4264,21 @@ static void goya_get_event_desc(u16 event_type, char *desc, size_t size) index = event_type - GOYA_ASYNC_EVENT_ID_DMA0_CH; snprintf(desc, size, _goya_get_event_desc(event_type), index); break; + case GOYA_ASYNC_EVENT_ID_TPC0_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC1_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC2_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC3_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC4_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC5_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC6_BMON_SPMU: + case GOYA_ASYNC_EVENT_ID_TPC7_BMON_SPMU: + index = (event_type - GOYA_ASYNC_EVENT_ID_TPC0_BMON_SPMU) / 10; + snprintf(desc, size, _goya_get_event_desc(event_type), index); + break; + case GOYA_ASYNC_EVENT_ID_DMA_BM_CH0 ... GOYA_ASYNC_EVENT_ID_DMA_BM_CH4: + index = event_type - GOYA_ASYNC_EVENT_ID_DMA_BM_CH0; + snprintf(desc, size, _goya_get_event_desc(event_type), index); + break; default: snprintf(desc, size, _goya_get_event_desc(event_type)); break; @@ -4226,7 +4329,8 @@ static void goya_print_mmu_error_info(struct hl_device *hdev) } } -static void goya_print_irq_info(struct hl_device *hdev, u16 event_type) +static void goya_print_irq_info(struct hl_device *hdev, u16 event_type, + bool razwi) { char desc[20] = ""; @@ -4234,8 +4338,10 @@ static void goya_print_irq_info(struct hl_device *hdev, u16 event_type) dev_err(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n", event_type, desc); - goya_print_razwi_info(hdev); - goya_print_mmu_error_info(hdev); + if (razwi) { + goya_print_razwi_info(hdev); + goya_print_mmu_error_info(hdev); + } } static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr, @@ -4339,19 +4445,12 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) case GOYA_ASYNC_EVENT_ID_PSOC_CORESIGHT: case GOYA_ASYNC_EVENT_ID_SRAM0 ... GOYA_ASYNC_EVENT_ID_SRAM29: case GOYA_ASYNC_EVENT_ID_GIC500: - case GOYA_ASYNC_EVENT_ID_PLL0: - case GOYA_ASYNC_EVENT_ID_PLL1: - case GOYA_ASYNC_EVENT_ID_PLL3: - case GOYA_ASYNC_EVENT_ID_PLL4: - case GOYA_ASYNC_EVENT_ID_PLL5: - case GOYA_ASYNC_EVENT_ID_PLL6: + case GOYA_ASYNC_EVENT_ID_PLL0 ... GOYA_ASYNC_EVENT_ID_PLL6: case GOYA_ASYNC_EVENT_ID_AXI_ECC: case GOYA_ASYNC_EVENT_ID_L2_RAM_ECC: case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_05_SW_RESET: case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_10_VRHOT_ICRIT: - dev_err(hdev->dev, - "Received H/W interrupt %d, reset the chip\n", - event_type); + goya_print_irq_info(hdev, event_type, false); hl_device_reset(hdev, true, false); break; @@ -4382,7 +4481,7 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) case GOYA_ASYNC_EVENT_ID_MME_CMDQ: case GOYA_ASYNC_EVENT_ID_DMA0_QM ... GOYA_ASYNC_EVENT_ID_DMA4_QM: case GOYA_ASYNC_EVENT_ID_DMA0_CH ... GOYA_ASYNC_EVENT_ID_DMA4_CH: - goya_print_irq_info(hdev, event_type); + goya_print_irq_info(hdev, event_type, true); goya_unmask_irq(hdev, event_type); break; @@ -4394,12 +4493,9 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) case GOYA_ASYNC_EVENT_ID_TPC5_BMON_SPMU: case GOYA_ASYNC_EVENT_ID_TPC6_BMON_SPMU: case GOYA_ASYNC_EVENT_ID_TPC7_BMON_SPMU: - case GOYA_ASYNC_EVENT_ID_DMA_BM_CH0: - case GOYA_ASYNC_EVENT_ID_DMA_BM_CH1: - case GOYA_ASYNC_EVENT_ID_DMA_BM_CH2: - case GOYA_ASYNC_EVENT_ID_DMA_BM_CH3: - case GOYA_ASYNC_EVENT_ID_DMA_BM_CH4: - dev_info(hdev->dev, "Received H/W interrupt %d\n", event_type); + case GOYA_ASYNC_EVENT_ID_DMA_BM_CH0 ... GOYA_ASYNC_EVENT_ID_DMA_BM_CH4: + goya_print_irq_info(hdev, event_type, false); + goya_unmask_irq(hdev, event_type); break; default: @@ -4418,36 +4514,47 @@ void *goya_get_events_stat(struct hl_device *hdev, u32 *size) return goya->events_stat; } -static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u32 size, +static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, u64 val, bool is_dram) { struct packet_lin_dma *lin_dma_pkt; struct hl_cs_job *job; u32 cb_size, ctl; struct hl_cb *cb; - int rc; + int rc, lin_dma_pkts_cnt; - cb = hl_cb_kernel_create(hdev, PAGE_SIZE); + lin_dma_pkts_cnt = DIV_ROUND_UP_ULL(size, SZ_2G); + cb_size = lin_dma_pkts_cnt * sizeof(struct packet_lin_dma) + + sizeof(struct packet_msg_prot); + cb = hl_cb_kernel_create(hdev, cb_size); if (!cb) - return -EFAULT; + return -ENOMEM; lin_dma_pkt = (struct packet_lin_dma *) (uintptr_t) cb->kernel_address; - memset(lin_dma_pkt, 0, sizeof(*lin_dma_pkt)); - cb_size = sizeof(*lin_dma_pkt); - - ctl = ((PACKET_LIN_DMA << GOYA_PKT_CTL_OPCODE_SHIFT) | - (1 << GOYA_PKT_LIN_DMA_CTL_MEMSET_SHIFT) | - (1 << GOYA_PKT_LIN_DMA_CTL_WO_SHIFT) | - (1 << GOYA_PKT_CTL_RB_SHIFT) | - (1 << GOYA_PKT_CTL_MB_SHIFT)); - ctl |= (is_dram ? DMA_HOST_TO_DRAM : DMA_HOST_TO_SRAM) << - GOYA_PKT_LIN_DMA_CTL_DMA_DIR_SHIFT; - lin_dma_pkt->ctl = cpu_to_le32(ctl); + do { + memset(lin_dma_pkt, 0, sizeof(*lin_dma_pkt)); + + ctl = ((PACKET_LIN_DMA << GOYA_PKT_CTL_OPCODE_SHIFT) | + (1 << GOYA_PKT_LIN_DMA_CTL_MEMSET_SHIFT) | + (1 << GOYA_PKT_LIN_DMA_CTL_WO_SHIFT) | + (1 << GOYA_PKT_CTL_RB_SHIFT) | + (1 << GOYA_PKT_CTL_MB_SHIFT)); + ctl |= (is_dram ? DMA_HOST_TO_DRAM : DMA_HOST_TO_SRAM) << + GOYA_PKT_LIN_DMA_CTL_DMA_DIR_SHIFT; + lin_dma_pkt->ctl = cpu_to_le32(ctl); + + lin_dma_pkt->src_addr = cpu_to_le64(val); + lin_dma_pkt->dst_addr = cpu_to_le64(addr); + if (lin_dma_pkts_cnt > 1) + lin_dma_pkt->tsize = cpu_to_le32(SZ_2G); + else + lin_dma_pkt->tsize = cpu_to_le32(size); - lin_dma_pkt->src_addr = cpu_to_le64(val); - lin_dma_pkt->dst_addr = cpu_to_le64(addr); - lin_dma_pkt->tsize = cpu_to_le32(size); + size -= SZ_2G; + addr += SZ_2G; + lin_dma_pkt++; + } while (--lin_dma_pkts_cnt); job = hl_cs_allocate_job(hdev, true); if (!job) { @@ -4462,8 +4569,7 @@ static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u32 size, job->user_cb_size = cb_size; job->hw_queue_id = GOYA_QUEUE_ID_DMA_0; job->patched_cb = job->user_cb; - job->job_cb_size = job->user_cb_size + - sizeof(struct packet_msg_prot) * 2; + job->job_cb_size = job->user_cb_size; hl_debugfs_add_job(hdev, job); @@ -4485,10 +4591,12 @@ release_cb: int goya_context_switch(struct hl_device *hdev, u32 asid) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 addr = prop->sram_base_address; + u64 addr = prop->sram_base_address, sob_addr; u32 size = hdev->pldm ? 0x10000 : prop->sram_size; u64 val = 0x7777777777777777ull; - int rc; + int rc, dma_id; + u32 channel_off = mmDMA_CH_1_WR_COMP_ADDR_LO - + mmDMA_CH_0_WR_COMP_ADDR_LO; rc = goya_memset_device_memory(hdev, addr, size, val, false); if (rc) { @@ -4496,13 +4604,27 @@ int goya_context_switch(struct hl_device *hdev, u32 asid) return rc; } + /* we need to reset registers that the user is allowed to change */ + sob_addr = CFG_BASE + mmSYNC_MNGR_SOB_OBJ_1007; + WREG32(mmDMA_CH_0_WR_COMP_ADDR_LO, lower_32_bits(sob_addr)); + + for (dma_id = 1 ; dma_id < NUMBER_OF_EXT_HW_QUEUES ; dma_id++) { + sob_addr = CFG_BASE + mmSYNC_MNGR_SOB_OBJ_1000 + + (dma_id - 1) * 4; + WREG32(mmDMA_CH_0_WR_COMP_ADDR_LO + channel_off * dma_id, + lower_32_bits(sob_addr)); + } + WREG32(mmTPC_PLL_CLK_RLX_0, 0x200020); + goya_mmu_prepare(hdev, asid); + goya_clear_sm_regs(hdev); + return 0; } -int goya_mmu_clear_pgt_range(struct hl_device *hdev) +static int goya_mmu_clear_pgt_range(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; struct goya_device *goya = hdev->asic_specific; @@ -4516,7 +4638,7 @@ int goya_mmu_clear_pgt_range(struct hl_device *hdev) return goya_memset_device_memory(hdev, addr, size, 0, true); } -int goya_mmu_set_dram_default_page(struct hl_device *hdev) +static int goya_mmu_set_dram_default_page(struct hl_device *hdev) { struct goya_device *goya = hdev->asic_specific; u64 addr = hdev->asic_prop.mmu_dram_default_page_addr; @@ -4529,7 +4651,123 @@ int goya_mmu_set_dram_default_page(struct hl_device *hdev) return goya_memset_device_memory(hdev, addr, size, val, true); } -void goya_mmu_prepare(struct hl_device *hdev, u32 asid) +static int goya_mmu_add_mappings_for_device_cpu(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct goya_device *goya = hdev->asic_specific; + s64 off, cpu_off; + int rc; + + if (!(goya->hw_cap_initialized & HW_CAP_MMU)) + return 0; + + for (off = 0 ; off < CPU_FW_IMAGE_SIZE ; off += PAGE_SIZE_2MB) { + rc = hl_mmu_map(hdev->kernel_ctx, prop->dram_base_address + off, + prop->dram_base_address + off, PAGE_SIZE_2MB); + if (rc) { + dev_err(hdev->dev, "Map failed for address 0x%llx\n", + prop->dram_base_address + off); + goto unmap; + } + } + + if (!(hdev->cpu_accessible_dma_address & (PAGE_SIZE_2MB - 1))) { + rc = hl_mmu_map(hdev->kernel_ctx, VA_CPU_ACCESSIBLE_MEM_ADDR, + hdev->cpu_accessible_dma_address, PAGE_SIZE_2MB); + + if (rc) { + dev_err(hdev->dev, + "Map failed for CPU accessible memory\n"); + off -= PAGE_SIZE_2MB; + goto unmap; + } + } else { + for (cpu_off = 0 ; cpu_off < SZ_2M ; cpu_off += PAGE_SIZE_4KB) { + rc = hl_mmu_map(hdev->kernel_ctx, + VA_CPU_ACCESSIBLE_MEM_ADDR + cpu_off, + hdev->cpu_accessible_dma_address + cpu_off, + PAGE_SIZE_4KB); + if (rc) { + dev_err(hdev->dev, + "Map failed for CPU accessible memory\n"); + cpu_off -= PAGE_SIZE_4KB; + goto unmap_cpu; + } + } + } + + goya_mmu_prepare_reg(hdev, mmCPU_IF_ARUSER_OVR, HL_KERNEL_ASID_ID); + goya_mmu_prepare_reg(hdev, mmCPU_IF_AWUSER_OVR, HL_KERNEL_ASID_ID); + WREG32(mmCPU_IF_ARUSER_OVR_EN, 0x7FF); + WREG32(mmCPU_IF_AWUSER_OVR_EN, 0x7FF); + + /* Make sure configuration is flushed to device */ + RREG32(mmCPU_IF_AWUSER_OVR_EN); + + goya->device_cpu_mmu_mappings_done = true; + + return 0; + +unmap_cpu: + for (; cpu_off >= 0 ; cpu_off -= PAGE_SIZE_4KB) + if (hl_mmu_unmap(hdev->kernel_ctx, + VA_CPU_ACCESSIBLE_MEM_ADDR + cpu_off, + PAGE_SIZE_4KB)) + dev_warn_ratelimited(hdev->dev, + "failed to unmap address 0x%llx\n", + VA_CPU_ACCESSIBLE_MEM_ADDR + cpu_off); +unmap: + for (; off >= 0 ; off -= PAGE_SIZE_2MB) + if (hl_mmu_unmap(hdev->kernel_ctx, + prop->dram_base_address + off, PAGE_SIZE_2MB)) + dev_warn_ratelimited(hdev->dev, + "failed to unmap address 0x%llx\n", + prop->dram_base_address + off); + + return rc; +} + +void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct goya_device *goya = hdev->asic_specific; + u32 off, cpu_off; + + if (!(goya->hw_cap_initialized & HW_CAP_MMU)) + return; + + if (!goya->device_cpu_mmu_mappings_done) + return; + + WREG32(mmCPU_IF_ARUSER_OVR_EN, 0); + WREG32(mmCPU_IF_AWUSER_OVR_EN, 0); + + if (!(hdev->cpu_accessible_dma_address & (PAGE_SIZE_2MB - 1))) { + if (hl_mmu_unmap(hdev->kernel_ctx, VA_CPU_ACCESSIBLE_MEM_ADDR, + PAGE_SIZE_2MB)) + dev_warn(hdev->dev, + "Failed to unmap CPU accessible memory\n"); + } else { + for (cpu_off = 0 ; cpu_off < SZ_2M ; cpu_off += PAGE_SIZE_4KB) + if (hl_mmu_unmap(hdev->kernel_ctx, + VA_CPU_ACCESSIBLE_MEM_ADDR + cpu_off, + PAGE_SIZE_4KB)) + dev_warn_ratelimited(hdev->dev, + "failed to unmap address 0x%llx\n", + VA_CPU_ACCESSIBLE_MEM_ADDR + cpu_off); + } + + for (off = 0 ; off < CPU_FW_IMAGE_SIZE ; off += PAGE_SIZE_2MB) + if (hl_mmu_unmap(hdev->kernel_ctx, + prop->dram_base_address + off, PAGE_SIZE_2MB)) + dev_warn_ratelimited(hdev->dev, + "Failed to unmap address 0x%llx\n", + prop->dram_base_address + off); + + goya->device_cpu_mmu_mappings_done = false; +} + +static void goya_mmu_prepare(struct hl_device *hdev, u32 asid) { struct goya_device *goya = hdev->asic_specific; int i; @@ -4676,57 +4914,82 @@ int goya_armcp_info_get(struct hl_device *hdev) return 0; } -static bool goya_is_device_idle(struct hl_device *hdev, char *buf, size_t size) +static bool goya_is_device_idle(struct hl_device *hdev, u32 *mask, + struct seq_file *s) { - u64 offset, dma_qm_reg, tpc_qm_reg, tpc_cmdq_reg, tpc_cfg_reg; + const char *fmt = "%-5d%-9s%#-14x%#-16x%#x\n"; + const char *dma_fmt = "%-5d%-9s%#-14x%#x\n"; + u32 qm_glbl_sts0, cmdq_glbl_sts0, dma_core_sts0, tpc_cfg_sts, + mme_arch_sts; + bool is_idle = true, is_eng_idle; + u64 offset; int i; + if (s) + seq_puts(s, "\nDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0\n" + "--- ------- ------------ -------------\n"); + offset = mmDMA_QM_1_GLBL_STS0 - mmDMA_QM_0_GLBL_STS0; for (i = 0 ; i < DMA_MAX_NUM ; i++) { - dma_qm_reg = mmDMA_QM_0_GLBL_STS0 + i * offset; + qm_glbl_sts0 = RREG32(mmDMA_QM_0_GLBL_STS0 + i * offset); + dma_core_sts0 = RREG32(mmDMA_CH_0_STS0 + i * offset); + is_eng_idle = IS_DMA_QM_IDLE(qm_glbl_sts0) && + IS_DMA_IDLE(dma_core_sts0); + is_idle &= is_eng_idle; - if ((RREG32(dma_qm_reg) & DMA_QM_IDLE_MASK) != - DMA_QM_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "DMA%d_QM", i); + if (mask) + *mask |= !is_eng_idle << (GOYA_ENGINE_ID_DMA_0 + i); + if (s) + seq_printf(s, dma_fmt, i, is_eng_idle ? "Y" : "N", + qm_glbl_sts0, dma_core_sts0); } + if (s) + seq_puts(s, + "\nTPC is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 CFG_STATUS\n" + "--- ------- ------------ -------------- ----------\n"); + offset = mmTPC1_QM_GLBL_STS0 - mmTPC0_QM_GLBL_STS0; for (i = 0 ; i < TPC_MAX_NUM ; i++) { - tpc_qm_reg = mmTPC0_QM_GLBL_STS0 + i * offset; - tpc_cmdq_reg = mmTPC0_CMDQ_GLBL_STS0 + i * offset; - tpc_cfg_reg = mmTPC0_CFG_STATUS + i * offset; - - if ((RREG32(tpc_qm_reg) & TPC_QM_IDLE_MASK) != - TPC_QM_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "TPC%d_QM", i); - - if ((RREG32(tpc_cmdq_reg) & TPC_CMDQ_IDLE_MASK) != - TPC_CMDQ_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "TPC%d_CMDQ", i); - - if ((RREG32(tpc_cfg_reg) & TPC_CFG_IDLE_MASK) != - TPC_CFG_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "TPC%d_CFG", i); - } - - if ((RREG32(mmMME_QM_GLBL_STS0) & MME_QM_IDLE_MASK) != - MME_QM_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "MME_QM"); - - if ((RREG32(mmMME_CMDQ_GLBL_STS0) & MME_CMDQ_IDLE_MASK) != - MME_CMDQ_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "MME_CMDQ"); - - if ((RREG32(mmMME_ARCH_STATUS) & MME_ARCH_IDLE_MASK) != - MME_ARCH_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "MME_ARCH"); - - if (RREG32(mmMME_SHADOW_0_STATUS) & MME_SHADOW_IDLE_MASK) - return HL_ENG_BUSY(buf, size, "MME"); - - return true; + qm_glbl_sts0 = RREG32(mmTPC0_QM_GLBL_STS0 + i * offset); + cmdq_glbl_sts0 = RREG32(mmTPC0_CMDQ_GLBL_STS0 + i * offset); + tpc_cfg_sts = RREG32(mmTPC0_CFG_STATUS + i * offset); + is_eng_idle = IS_TPC_QM_IDLE(qm_glbl_sts0) && + IS_TPC_CMDQ_IDLE(cmdq_glbl_sts0) && + IS_TPC_IDLE(tpc_cfg_sts); + is_idle &= is_eng_idle; + + if (mask) + *mask |= !is_eng_idle << (GOYA_ENGINE_ID_TPC_0 + i); + if (s) + seq_printf(s, fmt, i, is_eng_idle ? "Y" : "N", + qm_glbl_sts0, cmdq_glbl_sts0, tpc_cfg_sts); + } + + if (s) + seq_puts(s, + "\nMME is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 ARCH_STATUS\n" + "--- ------- ------------ -------------- -----------\n"); + + qm_glbl_sts0 = RREG32(mmMME_QM_GLBL_STS0); + cmdq_glbl_sts0 = RREG32(mmMME_CMDQ_GLBL_STS0); + mme_arch_sts = RREG32(mmMME_ARCH_STATUS); + is_eng_idle = IS_MME_QM_IDLE(qm_glbl_sts0) && + IS_MME_CMDQ_IDLE(cmdq_glbl_sts0) && + IS_MME_IDLE(mme_arch_sts); + is_idle &= is_eng_idle; + + if (mask) + *mask |= !is_eng_idle << GOYA_ENGINE_ID_MME_0; + if (s) { + seq_printf(s, fmt, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0, + cmdq_glbl_sts0, mme_arch_sts); + seq_puts(s, "\n"); + } + + return is_idle; } static void goya_hw_queues_lock(struct hl_device *hdev) diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index c83cab0d641e..f8c611883dc1 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -126,6 +126,12 @@ #define VA_DDR_SPACE_SIZE (VA_DDR_SPACE_END - \ VA_DDR_SPACE_START) /* 128GB */ +#if (HL_CPU_ACCESSIBLE_MEM_SIZE != SZ_2M) +#error "HL_CPU_ACCESSIBLE_MEM_SIZE must be exactly 2MB to enable MMU mapping" +#endif + +#define VA_CPU_ACCESSIBLE_MEM_ADDR 0x8000000000ull + #define DMA_MAX_TRANSFER_SIZE U32_MAX #define HW_CAP_PLL 0x00000001 @@ -157,6 +163,7 @@ struct goya_device { u64 ddr_bar_cur_addr; u32 events_stat[GOYA_ASYNC_EVENT_ID_SIZE]; u32 hw_cap_initialized; + u8 device_cpu_mmu_mappings_done; }; void goya_get_fixed_properties(struct hl_device *hdev); @@ -204,18 +211,14 @@ int goya_armcp_info_get(struct hl_device *hdev); int goya_debug_coresight(struct hl_device *hdev, void *data); void goya_halt_coresight(struct hl_device *hdev); -void goya_mmu_prepare(struct hl_device *hdev, u32 asid); -int goya_mmu_clear_pgt_range(struct hl_device *hdev); -int goya_mmu_set_dram_default_page(struct hl_device *hdev); - int goya_suspend(struct hl_device *hdev); int goya_resume(struct hl_device *hdev); void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry); void *goya_get_events_stat(struct hl_device *hdev, u32 *size); -void goya_add_end_of_cb_packets(u64 kernel_address, u32 len, u64 cq_addr, - u32 cq_val, u32 msix_vec); +void goya_add_end_of_cb_packets(struct hl_device *hdev, u64 kernel_address, + u32 len, u64 cq_addr, u32 cq_val, u32 msix_vec); int goya_cs_parser(struct hl_device *hdev, struct hl_cs_parser *parser); void *goya_get_int_queue_base(struct hl_device *hdev, u32 queue_id, dma_addr_t *dma_handle, u16 *queue_len); @@ -225,5 +228,6 @@ void *goya_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle); void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr); +void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev); #endif /* GOYAP_H_ */ diff --git a/drivers/misc/habanalabs/goya/goya_security.c b/drivers/misc/habanalabs/goya/goya_security.c index d95d1b2f860d..d6ec12b3e692 100644 --- a/drivers/misc/habanalabs/goya/goya_security.c +++ b/drivers/misc/habanalabs/goya/goya_security.c @@ -677,6 +677,17 @@ static void goya_init_tpc_protection_bits(struct hl_device *hdev) goya_pb_set_block(hdev, mmTPC0_RD_REGULATOR_BASE); goya_pb_set_block(hdev, mmTPC0_WR_REGULATOR_BASE); + pb_addr = (mmTPC0_CFG_SEMAPHORE & ~0xFFF) + PROT_BITS_OFFS; + word_offset = ((mmTPC0_CFG_SEMAPHORE & PROT_BITS_OFFS) >> 7) << 2; + + mask = 1 << ((mmTPC0_CFG_SEMAPHORE & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_VFLAGS & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_SFLAGS & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_LFSR_POLYNOM & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_STATUS & 0x7F) >> 2); + + WREG32(pb_addr + word_offset, ~mask); + pb_addr = (mmTPC0_CFG_CFG_BASE_ADDRESS_HIGH & ~0xFFF) + PROT_BITS_OFFS; word_offset = ((mmTPC0_CFG_CFG_BASE_ADDRESS_HIGH & PROT_BITS_OFFS) >> 7) << 2; @@ -684,6 +695,11 @@ static void goya_init_tpc_protection_bits(struct hl_device *hdev) mask |= 1 << ((mmTPC0_CFG_CFG_SUBTRACT_VALUE & 0x7F) >> 2); mask |= 1 << ((mmTPC0_CFG_SM_BASE_ADDRESS_LOW & 0x7F) >> 2); mask |= 1 << ((mmTPC0_CFG_SM_BASE_ADDRESS_HIGH & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_CFG_SUBTRACT_VALUE & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_TPC_STALL & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_MSS_CONFIG & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_TPC_INTR_CAUSE & 0x7F) >> 2); + mask |= 1 << ((mmTPC0_CFG_TPC_INTR_MASK & 0x7F) >> 2); WREG32(pb_addr + word_offset, ~mask); diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index adef7d9d7488..10da9940ee0d 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -34,6 +34,8 @@ #define HL_ARMCP_INFO_TIMEOUT_USEC 10000000 /* 10s */ #define HL_ARMCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ +#define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */ + #define HL_MAX_QUEUES 128 #define HL_MAX_JOBS_PER_CS 64 @@ -123,7 +125,7 @@ enum hl_device_hw_state { /** * struct asic_fixed_properties - ASIC specific immutable properties. * @hw_queues_props: H/W queues properties. - * @armcp_info: received various information from ArmCP regarding the H/W. e.g. + * @armcp_info: received various information from ArmCP regarding the H/W, e.g. * available sensors. * @uboot_ver: F/W U-boot version. * @preboot_ver: F/W Preboot version. @@ -318,18 +320,8 @@ struct hl_cs_job; #define HL_EQ_LENGTH 64 #define HL_EQ_SIZE_IN_BYTES (HL_EQ_LENGTH * HL_EQ_ENTRY_SIZE) -#define HL_CPU_PKT_SHIFT 5 -#define HL_CPU_PKT_SIZE (1 << HL_CPU_PKT_SHIFT) -#define HL_CPU_PKT_MASK (~((1 << HL_CPU_PKT_SHIFT) - 1)) -#define HL_CPU_MAX_PKTS_IN_CB 32 -#define HL_CPU_CB_SIZE (HL_CPU_PKT_SIZE * \ - HL_CPU_MAX_PKTS_IN_CB) -#define HL_CPU_CB_QUEUE_SIZE (HL_QUEUE_LENGTH * HL_CPU_CB_SIZE) - -/* KMD <-> ArmCP shared memory size (EQ + PQ + CPU CB queue) */ -#define HL_CPU_ACCESSIBLE_MEM_SIZE (HL_EQ_SIZE_IN_BYTES + \ - HL_QUEUE_SIZE_IN_BYTES + \ - HL_CPU_CB_QUEUE_SIZE) +/* KMD <-> ArmCP shared memory size */ +#define HL_CPU_ACCESSIBLE_MEM_SIZE SZ_2M /** * struct hl_hw_queue - describes a H/W transport queue. @@ -543,8 +535,9 @@ struct hl_asic_funcs { enum dma_data_direction dir); u32 (*get_dma_desc_list_size)(struct hl_device *hdev, struct sg_table *sgt); - void (*add_end_of_cb_packets)(u64 kernel_address, u32 len, u64 cq_addr, - u32 cq_val, u32 msix_num); + void (*add_end_of_cb_packets)(struct hl_device *hdev, + u64 kernel_address, u32 len, + u64 cq_addr, u32 cq_val, u32 msix_num); void (*update_eq_ci)(struct hl_device *hdev, u32 val); int (*context_switch)(struct hl_device *hdev, u32 asid); void (*restore_phase_topology)(struct hl_device *hdev); @@ -564,7 +557,8 @@ struct hl_asic_funcs { u32 asid, u64 va, u64 size); int (*send_heartbeat)(struct hl_device *hdev); int (*debug_coresight)(struct hl_device *hdev, void *data); - bool (*is_device_idle)(struct hl_device *hdev, char *buf, size_t size); + bool (*is_device_idle)(struct hl_device *hdev, u32 *mask, + struct seq_file *s); int (*soft_reset_late_init)(struct hl_device *hdev); void (*hw_queues_lock)(struct hl_device *hdev); void (*hw_queues_unlock)(struct hl_device *hdev); @@ -1065,12 +1059,59 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); (cond) ? 0 : -ETIMEDOUT; \ }) +/* + * address in this macro points always to a memory location in the + * host's (server's) memory. That location is updated asynchronously + * either by the direct access of the device or by another core + */ +#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us) \ +({ \ + ktime_t __timeout; \ + /* timeout should be longer when working with simulator */ \ + if (hdev->pdev) \ + __timeout = ktime_add_us(ktime_get(), timeout_us); \ + else \ + __timeout = ktime_add_us(ktime_get(), (timeout_us * 10)); \ + might_sleep_if(sleep_us); \ + for (;;) { \ + /* Verify we read updates done by other cores or by device */ \ + mb(); \ + (val) = *((u32 *) (uintptr_t) (addr)); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ + (val) = *((u32 *) (uintptr_t) (addr)); \ + break; \ + } \ + if (sleep_us) \ + usleep_range((sleep_us >> 2) + 1, sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) -#define HL_ENG_BUSY(buf, size, fmt, ...) ({ \ - if (buf) \ - snprintf(buf, size, fmt, ##__VA_ARGS__); \ - false; \ - }) +#define hl_poll_timeout_device_memory(hdev, addr, val, cond, sleep_us, \ + timeout_us) \ +({ \ + ktime_t __timeout; \ + /* timeout should be longer when working with simulator */ \ + if (hdev->pdev) \ + __timeout = ktime_add_us(ktime_get(), timeout_us); \ + else \ + __timeout = ktime_add_us(ktime_get(), (timeout_us * 10)); \ + might_sleep_if(sleep_us); \ + for (;;) { \ + (val) = readl(addr); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ + (val) = readl(addr); \ + break; \ + } \ + if (sleep_us) \ + usleep_range((sleep_us >> 2) + 1, sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) struct hwmon_chip_info; @@ -1117,6 +1158,7 @@ struct hl_device_reset_work { * lock here so we can flush user processes which are opening * the device while we are trying to hard reset it * @send_cpu_message_lock: enforces only one message in KMD <-> ArmCP queue. + * @debug_lock: protects critical section of setting debug mode for device * @asic_prop: ASIC specific immutable properties. * @asic_funcs: ASIC specific functions. * @asic_specific: ASIC specific information to use only from ASIC files. @@ -1159,6 +1201,8 @@ struct hl_device_reset_work { * @mmu_enable: is MMU enabled. * @device_cpu_disabled: is the device CPU disabled (due to timeouts) * @dma_mask: the dma mask that was set for this device + * @in_debug: is device under debug. This, together with fd_open_cnt, enforces + * that only a single user is configuring the debug infrastructure. */ struct hl_device { struct pci_dev *pdev; @@ -1188,6 +1232,7 @@ struct hl_device { /* TODO: remove fd_open_cnt_lock for multiple process support */ struct mutex fd_open_cnt_lock; struct mutex send_cpu_message_lock; + struct mutex debug_lock; struct asic_fixed_properties asic_prop; const struct hl_asic_funcs *asic_funcs; void *asic_specific; @@ -1230,6 +1275,7 @@ struct hl_device { u8 init_done; u8 device_cpu_disabled; u8 dma_mask; + u8 in_debug; /* Parameters for bring-up */ u8 mmu_enable; @@ -1325,13 +1371,10 @@ static inline bool hl_mem_area_crosses_range(u64 address, u32 size, int hl_device_open(struct inode *inode, struct file *filp); bool hl_device_disabled_or_in_reset(struct hl_device *hdev); enum hl_device_status hl_device_status(struct hl_device *hdev); +int hl_device_set_debug_mode(struct hl_device *hdev, bool enable); int create_hdev(struct hl_device **dev, struct pci_dev *pdev, enum hl_asic_type asic_type, int minor); void destroy_hdev(struct hl_device *hdev); -int hl_poll_timeout_memory(struct hl_device *hdev, u64 addr, u32 timeout_us, - u32 *val); -int hl_poll_timeout_device_memory(struct hl_device *hdev, void __iomem *addr, - u32 timeout_us, u32 *val); int hl_hw_queues_create(struct hl_device *hdev); void hl_hw_queues_destroy(struct hl_device *hdev); int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, diff --git a/drivers/misc/habanalabs/habanalabs_drv.c b/drivers/misc/habanalabs/habanalabs_drv.c index 5f4d155be767..6f6dbe93f1df 100644 --- a/drivers/misc/habanalabs/habanalabs_drv.c +++ b/drivers/misc/habanalabs/habanalabs_drv.c @@ -105,9 +105,17 @@ int hl_device_open(struct inode *inode, struct file *filp) return -EPERM; } + if (hdev->in_debug) { + dev_err_ratelimited(hdev->dev, + "Can't open %s because it is being debugged by another user\n", + dev_name(hdev->dev)); + mutex_unlock(&hdev->fd_open_cnt_lock); + return -EPERM; + } + if (atomic_read(&hdev->fd_open_cnt)) { dev_info_ratelimited(hdev->dev, - "Device %s is already attached to application\n", + "Can't open %s because another user is working on it\n", dev_name(hdev->dev)); mutex_unlock(&hdev->fd_open_cnt_lock); return -EBUSY; @@ -164,6 +172,17 @@ close_device: return rc; } +static void set_driver_behavior_per_device(struct hl_device *hdev) +{ + hdev->mmu_enable = 1; + hdev->cpu_enable = 1; + hdev->fw_loading = 1; + hdev->cpu_queues_enable = 1; + hdev->heartbeat = 1; + + hdev->reset_pcilink = 0; +} + /* * create_hdev - create habanalabs device instance * @@ -188,29 +207,25 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, if (!hdev) return -ENOMEM; + /* First, we must find out which ASIC are we handling. This is needed + * to configure the behavior of the driver (kernel parameters) + */ + if (pdev) { + hdev->asic_type = get_asic_type(pdev->device); + if (hdev->asic_type == ASIC_INVALID) { + dev_err(&pdev->dev, "Unsupported ASIC\n"); + rc = -ENODEV; + goto free_hdev; + } + } else { + hdev->asic_type = asic_type; + } + hdev->major = hl_major; hdev->reset_on_lockup = reset_on_lockup; - - /* Parameters for bring-up - set them to defaults */ - hdev->mmu_enable = 1; - hdev->cpu_enable = 1; - hdev->reset_pcilink = 0; - hdev->cpu_queues_enable = 1; - hdev->fw_loading = 1; hdev->pldm = 0; - hdev->heartbeat = 1; - - /* If CPU is disabled, no point in loading FW */ - if (!hdev->cpu_enable) - hdev->fw_loading = 0; - /* If we don't load FW, no need to initialize CPU queues */ - if (!hdev->fw_loading) - hdev->cpu_queues_enable = 0; - - /* If CPU queues not enabled, no way to do heartbeat */ - if (!hdev->cpu_queues_enable) - hdev->heartbeat = 0; + set_driver_behavior_per_device(hdev); if (timeout_locked) hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); @@ -220,17 +235,6 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, hdev->disabled = true; hdev->pdev = pdev; /* can be NULL in case of simulator device */ - if (pdev) { - hdev->asic_type = get_asic_type(pdev->device); - if (hdev->asic_type == ASIC_INVALID) { - dev_err(&pdev->dev, "Unsupported ASIC\n"); - rc = -ENODEV; - goto free_hdev; - } - } else { - hdev->asic_type = asic_type; - } - /* Set default DMA mask to 32 bits */ hdev->dma_mask = 32; diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index b7a0eecf6b6c..07127576b3e8 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -119,7 +119,8 @@ static int hw_idle(struct hl_device *hdev, struct hl_info_args *args) if ((!max_size) || (!out)) return -EINVAL; - hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev, NULL, 0); + hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev, + &hw_idle.busy_engines_mask, NULL); return copy_to_user(out, &hw_idle, min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0; @@ -254,10 +255,18 @@ static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data) case HL_DEBUG_OP_BMON: case HL_DEBUG_OP_SPMU: case HL_DEBUG_OP_TIMESTAMP: + if (!hdev->in_debug) { + dev_err_ratelimited(hdev->dev, + "Rejecting debug configuration request because device not in debug mode\n"); + return -EFAULT; + } args->input_size = min(args->input_size, hl_debug_struct_size[args->op]); rc = debug_coresight(hdev, args); break; + case HL_DEBUG_OP_SET_MODE: + rc = hl_device_set_debug_mode(hdev, (bool) args->enable); + break; default: dev_err(hdev->dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 2894d8975933..e3b5517897ea 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -265,7 +265,7 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job) cq = &hdev->completion_queue[q->hw_queue_id]; cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry); - hdev->asic_funcs->add_end_of_cb_packets(cb->kernel_address, len, + hdev->asic_funcs->add_end_of_cb_packets(hdev, cb->kernel_address, len, cq_addr, __le32_to_cpu(cq_pkt.data), q->hw_queue_id); diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h b/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h new file mode 100644 index 000000000000..028143408401 --- /dev/null +++ b/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h @@ -0,0 +1,418 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2018 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +/************************************ + ** This is an auto-generated file ** + ** DO NOT EDIT BELOW ** + ************************************/ + +#ifndef ASIC_REG_DMA_CH_0_MASKS_H_ +#define ASIC_REG_DMA_CH_0_MASKS_H_ + +/* + ***************************************** + * DMA_CH_0 (Prototype: DMA_CH) + ***************************************** + */ + +/* DMA_CH_0_CFG0 */ +#define DMA_CH_0_CFG0_RD_MAX_OUTSTAND_SHIFT 0 +#define DMA_CH_0_CFG0_RD_MAX_OUTSTAND_MASK 0x3FF +#define DMA_CH_0_CFG0_WR_MAX_OUTSTAND_SHIFT 16 +#define DMA_CH_0_CFG0_WR_MAX_OUTSTAND_MASK 0xFFF0000 + +/* DMA_CH_0_CFG1 */ +#define DMA_CH_0_CFG1_RD_BUF_MAX_SIZE_SHIFT 0 +#define DMA_CH_0_CFG1_RD_BUF_MAX_SIZE_MASK 0x3FF + +/* DMA_CH_0_ERRMSG_ADDR_LO */ +#define DMA_CH_0_ERRMSG_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_ERRMSG_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_ERRMSG_ADDR_HI */ +#define DMA_CH_0_ERRMSG_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_ERRMSG_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_ERRMSG_WDATA */ +#define DMA_CH_0_ERRMSG_WDATA_VAL_SHIFT 0 +#define DMA_CH_0_ERRMSG_WDATA_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_RD_COMP_ADDR_LO */ +#define DMA_CH_0_RD_COMP_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_RD_COMP_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_RD_COMP_ADDR_HI */ +#define DMA_CH_0_RD_COMP_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_RD_COMP_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_RD_COMP_WDATA */ +#define DMA_CH_0_RD_COMP_WDATA_VAL_SHIFT 0 +#define DMA_CH_0_RD_COMP_WDATA_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_WR_COMP_ADDR_LO */ +#define DMA_CH_0_WR_COMP_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_WR_COMP_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_WR_COMP_ADDR_HI */ +#define DMA_CH_0_WR_COMP_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_WR_COMP_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_WR_COMP_WDATA */ +#define DMA_CH_0_WR_COMP_WDATA_VAL_SHIFT 0 +#define DMA_CH_0_WR_COMP_WDATA_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_LDMA_SRC_ADDR_LO */ +#define DMA_CH_0_LDMA_SRC_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_LDMA_SRC_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_LDMA_SRC_ADDR_HI */ +#define DMA_CH_0_LDMA_SRC_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_LDMA_SRC_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_LDMA_DST_ADDR_LO */ +#define DMA_CH_0_LDMA_DST_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_LDMA_DST_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_LDMA_DST_ADDR_HI */ +#define DMA_CH_0_LDMA_DST_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_LDMA_DST_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_LDMA_TSIZE */ +#define DMA_CH_0_LDMA_TSIZE_VAL_SHIFT 0 +#define DMA_CH_0_LDMA_TSIZE_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_COMIT_TRANSFER */ +#define DMA_CH_0_COMIT_TRANSFER_PCI_UPS_WKORDR_SHIFT 0 +#define DMA_CH_0_COMIT_TRANSFER_PCI_UPS_WKORDR_MASK 0x1 +#define DMA_CH_0_COMIT_TRANSFER_RD_COMP_EN_SHIFT 1 +#define DMA_CH_0_COMIT_TRANSFER_RD_COMP_EN_MASK 0x2 +#define DMA_CH_0_COMIT_TRANSFER_WR_COMP_EN_SHIFT 2 +#define DMA_CH_0_COMIT_TRANSFER_WR_COMP_EN_MASK 0x4 +#define DMA_CH_0_COMIT_TRANSFER_NOSNOOP_SHIFT 3 +#define DMA_CH_0_COMIT_TRANSFER_NOSNOOP_MASK 0x8 +#define DMA_CH_0_COMIT_TRANSFER_SRC_ADDR_INC_DIS_SHIFT 4 +#define DMA_CH_0_COMIT_TRANSFER_SRC_ADDR_INC_DIS_MASK 0x10 +#define DMA_CH_0_COMIT_TRANSFER_DST_ADDR_INC_DIS_SHIFT 5 +#define DMA_CH_0_COMIT_TRANSFER_DST_ADDR_INC_DIS_MASK 0x20 +#define DMA_CH_0_COMIT_TRANSFER_MEM_SET_SHIFT 6 +#define DMA_CH_0_COMIT_TRANSFER_MEM_SET_MASK 0x40 +#define DMA_CH_0_COMIT_TRANSFER_MOD_TENSOR_SHIFT 15 +#define DMA_CH_0_COMIT_TRANSFER_MOD_TENSOR_MASK 0x8000 +#define DMA_CH_0_COMIT_TRANSFER_CTL_SHIFT 16 +#define DMA_CH_0_COMIT_TRANSFER_CTL_MASK 0xFFFF0000 + +/* DMA_CH_0_STS0 */ +#define DMA_CH_0_STS0_DMA_BUSY_SHIFT 0 +#define DMA_CH_0_STS0_DMA_BUSY_MASK 0x1 +#define DMA_CH_0_STS0_RD_STS_CTX_FULL_SHIFT 1 +#define DMA_CH_0_STS0_RD_STS_CTX_FULL_MASK 0x2 +#define DMA_CH_0_STS0_WR_STS_CTX_FULL_SHIFT 2 +#define DMA_CH_0_STS0_WR_STS_CTX_FULL_MASK 0x4 + +/* DMA_CH_0_STS1 */ +#define DMA_CH_0_STS1_RD_STS_CTX_CNT_SHIFT 0 +#define DMA_CH_0_STS1_RD_STS_CTX_CNT_MASK 0xFFFFFFFF + +/* DMA_CH_0_STS2 */ +#define DMA_CH_0_STS2_WR_STS_CTX_CNT_SHIFT 0 +#define DMA_CH_0_STS2_WR_STS_CTX_CNT_MASK 0xFFFFFFFF + +/* DMA_CH_0_STS3 */ +#define DMA_CH_0_STS3_RD_STS_TRN_CNT_SHIFT 0 +#define DMA_CH_0_STS3_RD_STS_TRN_CNT_MASK 0xFFFFFFFF + +/* DMA_CH_0_STS4 */ +#define DMA_CH_0_STS4_WR_STS_TRN_CNT_SHIFT 0 +#define DMA_CH_0_STS4_WR_STS_TRN_CNT_MASK 0xFFFFFFFF + +/* DMA_CH_0_SRC_ADDR_LO_STS */ +#define DMA_CH_0_SRC_ADDR_LO_STS_VAL_SHIFT 0 +#define DMA_CH_0_SRC_ADDR_LO_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_SRC_ADDR_HI_STS */ +#define DMA_CH_0_SRC_ADDR_HI_STS_VAL_SHIFT 0 +#define DMA_CH_0_SRC_ADDR_HI_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_SRC_TSIZE_STS */ +#define DMA_CH_0_SRC_TSIZE_STS_VAL_SHIFT 0 +#define DMA_CH_0_SRC_TSIZE_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_DST_ADDR_LO_STS */ +#define DMA_CH_0_DST_ADDR_LO_STS_VAL_SHIFT 0 +#define DMA_CH_0_DST_ADDR_LO_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_DST_ADDR_HI_STS */ +#define DMA_CH_0_DST_ADDR_HI_STS_VAL_SHIFT 0 +#define DMA_CH_0_DST_ADDR_HI_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_DST_TSIZE_STS */ +#define DMA_CH_0_DST_TSIZE_STS_VAL_SHIFT 0 +#define DMA_CH_0_DST_TSIZE_STS_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_RD_RATE_LIM_EN */ +#define DMA_CH_0_RD_RATE_LIM_EN_VAL_SHIFT 0 +#define DMA_CH_0_RD_RATE_LIM_EN_VAL_MASK 0x1 + +/* DMA_CH_0_RD_RATE_LIM_RST_TOKEN */ +#define DMA_CH_0_RD_RATE_LIM_RST_TOKEN_VAL_SHIFT 0 +#define DMA_CH_0_RD_RATE_LIM_RST_TOKEN_VAL_MASK 0xFFFF + +/* DMA_CH_0_RD_RATE_LIM_SAT */ +#define DMA_CH_0_RD_RATE_LIM_SAT_VAL_SHIFT 0 +#define DMA_CH_0_RD_RATE_LIM_SAT_VAL_MASK 0xFFFF + +/* DMA_CH_0_RD_RATE_LIM_TOUT */ +#define DMA_CH_0_RD_RATE_LIM_TOUT_VAL_SHIFT 0 +#define DMA_CH_0_RD_RATE_LIM_TOUT_VAL_MASK 0x7FFFFFFF + +/* DMA_CH_0_WR_RATE_LIM_EN */ +#define DMA_CH_0_WR_RATE_LIM_EN_VAL_SHIFT 0 +#define DMA_CH_0_WR_RATE_LIM_EN_VAL_MASK 0x1 + +/* DMA_CH_0_WR_RATE_LIM_RST_TOKEN */ +#define DMA_CH_0_WR_RATE_LIM_RST_TOKEN_VAL_SHIFT 0 +#define DMA_CH_0_WR_RATE_LIM_RST_TOKEN_VAL_MASK 0xFFFF + +/* DMA_CH_0_WR_RATE_LIM_SAT */ +#define DMA_CH_0_WR_RATE_LIM_SAT_VAL_SHIFT 0 +#define DMA_CH_0_WR_RATE_LIM_SAT_VAL_MASK 0xFFFF + +/* DMA_CH_0_WR_RATE_LIM_TOUT */ +#define DMA_CH_0_WR_RATE_LIM_TOUT_VAL_SHIFT 0 +#define DMA_CH_0_WR_RATE_LIM_TOUT_VAL_MASK 0x7FFFFFFF + +/* DMA_CH_0_CFG2 */ +#define DMA_CH_0_CFG2_FORCE_WORD_SHIFT 0 +#define DMA_CH_0_CFG2_FORCE_WORD_MASK 0x1 + +/* DMA_CH_0_TDMA_CTL */ +#define DMA_CH_0_TDMA_CTL_DTYPE_SHIFT 0 +#define DMA_CH_0_TDMA_CTL_DTYPE_MASK 0x7 + +/* DMA_CH_0_TDMA_SRC_BASE_ADDR_LO */ +#define DMA_CH_0_TDMA_SRC_BASE_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_BASE_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_BASE_ADDR_HI */ +#define DMA_CH_0_TDMA_SRC_BASE_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_BASE_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_BASE_0 */ +#define DMA_CH_0_TDMA_SRC_ROI_BASE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_BASE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_SIZE_0 */ +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_0 */ +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_START_OFFSET_0 */ +#define DMA_CH_0_TDMA_SRC_START_OFFSET_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_START_OFFSET_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_STRIDE_0 */ +#define DMA_CH_0_TDMA_SRC_STRIDE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_STRIDE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_BASE_1 */ +#define DMA_CH_0_TDMA_SRC_ROI_BASE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_BASE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_SIZE_1 */ +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_1 */ +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_START_OFFSET_1 */ +#define DMA_CH_0_TDMA_SRC_START_OFFSET_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_START_OFFSET_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_STRIDE_1 */ +#define DMA_CH_0_TDMA_SRC_STRIDE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_STRIDE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_BASE_2 */ +#define DMA_CH_0_TDMA_SRC_ROI_BASE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_BASE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_SIZE_2 */ +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_2 */ +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_START_OFFSET_2 */ +#define DMA_CH_0_TDMA_SRC_START_OFFSET_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_START_OFFSET_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_STRIDE_2 */ +#define DMA_CH_0_TDMA_SRC_STRIDE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_STRIDE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_BASE_3 */ +#define DMA_CH_0_TDMA_SRC_ROI_BASE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_BASE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_SIZE_3 */ +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_3 */ +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_START_OFFSET_3 */ +#define DMA_CH_0_TDMA_SRC_START_OFFSET_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_START_OFFSET_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_STRIDE_3 */ +#define DMA_CH_0_TDMA_SRC_STRIDE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_STRIDE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_BASE_4 */ +#define DMA_CH_0_TDMA_SRC_ROI_BASE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_BASE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_ROI_SIZE_4 */ +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_ROI_SIZE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_4 */ +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_VALID_ELEMENTS_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_START_OFFSET_4 */ +#define DMA_CH_0_TDMA_SRC_START_OFFSET_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_START_OFFSET_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_SRC_STRIDE_4 */ +#define DMA_CH_0_TDMA_SRC_STRIDE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_SRC_STRIDE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_BASE_ADDR_LO */ +#define DMA_CH_0_TDMA_DST_BASE_ADDR_LO_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_BASE_ADDR_LO_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_BASE_ADDR_HI */ +#define DMA_CH_0_TDMA_DST_BASE_ADDR_HI_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_BASE_ADDR_HI_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_BASE_0 */ +#define DMA_CH_0_TDMA_DST_ROI_BASE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_BASE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_SIZE_0 */ +#define DMA_CH_0_TDMA_DST_ROI_SIZE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_SIZE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_VALID_ELEMENTS_0 */ +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_START_OFFSET_0 */ +#define DMA_CH_0_TDMA_DST_START_OFFSET_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_START_OFFSET_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_STRIDE_0 */ +#define DMA_CH_0_TDMA_DST_STRIDE_0_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_STRIDE_0_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_BASE_1 */ +#define DMA_CH_0_TDMA_DST_ROI_BASE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_BASE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_SIZE_1 */ +#define DMA_CH_0_TDMA_DST_ROI_SIZE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_SIZE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_VALID_ELEMENTS_1 */ +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_START_OFFSET_1 */ +#define DMA_CH_0_TDMA_DST_START_OFFSET_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_START_OFFSET_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_STRIDE_1 */ +#define DMA_CH_0_TDMA_DST_STRIDE_1_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_STRIDE_1_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_BASE_2 */ +#define DMA_CH_0_TDMA_DST_ROI_BASE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_BASE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_SIZE_2 */ +#define DMA_CH_0_TDMA_DST_ROI_SIZE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_SIZE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_VALID_ELEMENTS_2 */ +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_START_OFFSET_2 */ +#define DMA_CH_0_TDMA_DST_START_OFFSET_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_START_OFFSET_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_STRIDE_2 */ +#define DMA_CH_0_TDMA_DST_STRIDE_2_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_STRIDE_2_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_BASE_3 */ +#define DMA_CH_0_TDMA_DST_ROI_BASE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_BASE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_SIZE_3 */ +#define DMA_CH_0_TDMA_DST_ROI_SIZE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_SIZE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_VALID_ELEMENTS_3 */ +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_START_OFFSET_3 */ +#define DMA_CH_0_TDMA_DST_START_OFFSET_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_START_OFFSET_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_STRIDE_3 */ +#define DMA_CH_0_TDMA_DST_STRIDE_3_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_STRIDE_3_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_BASE_4 */ +#define DMA_CH_0_TDMA_DST_ROI_BASE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_BASE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_ROI_SIZE_4 */ +#define DMA_CH_0_TDMA_DST_ROI_SIZE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_ROI_SIZE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_VALID_ELEMENTS_4 */ +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_VALID_ELEMENTS_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_START_OFFSET_4 */ +#define DMA_CH_0_TDMA_DST_START_OFFSET_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_START_OFFSET_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_TDMA_DST_STRIDE_4 */ +#define DMA_CH_0_TDMA_DST_STRIDE_4_VAL_SHIFT 0 +#define DMA_CH_0_TDMA_DST_STRIDE_4_VAL_MASK 0xFFFFFFFF + +/* DMA_CH_0_MEM_INIT_BUSY */ +#define DMA_CH_0_MEM_INIT_BUSY_SBC_DATA_SHIFT 0 +#define DMA_CH_0_MEM_INIT_BUSY_SBC_DATA_MASK 0xFF +#define DMA_CH_0_MEM_INIT_BUSY_SBC_MD_SHIFT 8 +#define DMA_CH_0_MEM_INIT_BUSY_SBC_MD_MASK 0x100 + +#endif /* ASIC_REG_DMA_CH_0_MASKS_H_ */ diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h b/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h index 506e71e201e1..19b0f0ef1d0b 100644 --- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h +++ b/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h @@ -88,6 +88,7 @@ #include "psoc_global_conf_masks.h" #include "dma_macro_masks.h" #include "dma_qm_0_masks.h" +#include "dma_ch_0_masks.h" #include "tpc0_qm_masks.h" #include "tpc0_cmdq_masks.h" #include "mme_qm_masks.h" diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 693877e37fd8..42d237cae1dc 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -1657,17 +1657,10 @@ int hl_vm_init(struct hl_device *hdev) struct hl_vm *vm = &hdev->vm; int rc; - rc = hl_mmu_init(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to init MMU\n"); - return rc; - } - vm->dram_pg_pool = gen_pool_create(__ffs(prop->dram_page_size), -1); if (!vm->dram_pg_pool) { dev_err(hdev->dev, "Failed to create dram page pool\n"); - rc = -ENOMEM; - goto pool_create_err; + return -ENOMEM; } kref_init(&vm->dram_pg_pool_refcount); @@ -1693,8 +1686,6 @@ int hl_vm_init(struct hl_device *hdev) pool_add_err: gen_pool_destroy(vm->dram_pg_pool); -pool_create_err: - hl_mmu_fini(hdev); return rc; } @@ -1724,7 +1715,5 @@ void hl_vm_fini(struct hl_device *hdev) dev_warn(hdev->dev, "dram_pg_pool was not destroyed on %s\n", __func__); - hl_mmu_fini(hdev); - vm->init_done = false; } diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c index 10aee3141444..176c315836f1 100644 --- a/drivers/misc/habanalabs/mmu.c +++ b/drivers/misc/habanalabs/mmu.c @@ -241,8 +241,9 @@ static int dram_default_mapping_init(struct hl_ctx *ctx) hop2_pte_addr, hop3_pte_addr, pte_val; int rc, i, j, hop3_allocated = 0; - if (!hdev->dram_supports_virtual_memory || - !hdev->dram_default_page_mapping) + if ((!hdev->dram_supports_virtual_memory) || + (!hdev->dram_default_page_mapping) || + (ctx->asid == HL_KERNEL_ASID_ID)) return 0; num_of_hop3 = prop->dram_size_for_default_page_mapping; @@ -340,8 +341,9 @@ static void dram_default_mapping_fini(struct hl_ctx *ctx) hop2_pte_addr, hop3_pte_addr; int i, j; - if (!hdev->dram_supports_virtual_memory || - !hdev->dram_default_page_mapping) + if ((!hdev->dram_supports_virtual_memory) || + (!hdev->dram_default_page_mapping) || + (ctx->asid == HL_KERNEL_ASID_ID)) return; num_of_hop3 = prop->dram_size_for_default_page_mapping; @@ -385,12 +387,8 @@ static void dram_default_mapping_fini(struct hl_ctx *ctx) * @hdev: habanalabs device structure. * * This function does the following: - * - Allocate max_asid zeroed hop0 pgts so no mapping is available. - * - Enable MMU in H/W. - * - Invalidate the MMU cache. * - Create a pool of pages for pgt_infos. - * - * This function depends on DMA QMAN to be working! + * - Create a shadow table for pgt * * Return: 0 for success, non-zero for failure. */ @@ -915,6 +913,10 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size) return -EFAULT; } + WARN_ONCE((phys_addr & (real_page_size - 1)), + "Mapping 0x%llx with page size of 0x%x is erroneous! Address must be divisible by page size", + phys_addr, real_page_size); + npages = page_size / real_page_size; real_virt_addr = virt_addr; real_phys_addr = phys_addr; diff --git a/drivers/misc/habanalabs/pci.c b/drivers/misc/habanalabs/pci.c index 0e78a04d63f4..c98d88c7a5c6 100644 --- a/drivers/misc/habanalabs/pci.c +++ b/drivers/misc/habanalabs/pci.c @@ -10,6 +10,8 @@ #include <linux/pci.h> +#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 10) + /** * hl_pci_bars_map() - Map PCI BARs. * @hdev: Pointer to hl_device structure. @@ -88,8 +90,14 @@ static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data) { struct pci_dev *pdev = hdev->pdev; ktime_t timeout; + u64 msec; u32 val; + if (hdev->pldm) + msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC; + else + msec = HL_PCI_ELBI_TIMEOUT_MSEC; + /* Clear previous status */ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0); @@ -98,7 +106,7 @@ static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data) pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL, PCI_CONFIG_ELBI_CTRL_WRITE); - timeout = ktime_add_ms(ktime_get(), 10); + timeout = ktime_add_ms(ktime_get(), msec); for (;;) { pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val); if (val & PCI_CONFIG_ELBI_STS_MASK) diff --git a/drivers/misc/habanalabs/sysfs.c b/drivers/misc/habanalabs/sysfs.c index c900ab15cceb..25eb46d29d88 100644 --- a/drivers/misc/habanalabs/sysfs.c +++ b/drivers/misc/habanalabs/sysfs.c @@ -328,10 +328,6 @@ static ssize_t pci_addr_show(struct device *dev, struct device_attribute *attr, { struct hl_device *hdev = dev_get_drvdata(dev); - /* Use dummy, fixed address for simulator */ - if (!hdev->pdev) - return sprintf(buf, "0000:%02d:00.0\n", hdev->id); - return sprintf(buf, "%04x:%02x:%02x.%x\n", pci_domain_nr(hdev->pdev->bus), hdev->pdev->bus->number, diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 3431a825f24e..c12406f610d5 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -3,7 +3,7 @@ * isl29003.c - Linux kernel module for * Intersil ISL29003 ambient light sensor * - * See file:Documentation/misc-devices/isl29003 + * See file:Documentation/misc-devices/isl29003.rst * * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> * @@ -377,7 +377,7 @@ static int isl29003_init_client(struct i2c_client *client) static int isl29003_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct i2c_adapter *adapter = client->adapter; struct isl29003_data *data; int err = 0; diff --git a/drivers/misc/lis3lv02d/Kconfig b/drivers/misc/lis3lv02d/Kconfig index 4cfad45229c8..bb2fec4b5880 100644 --- a/drivers/misc/lis3lv02d/Kconfig +++ b/drivers/misc/lis3lv02d/Kconfig @@ -7,7 +7,6 @@ config SENSORS_LIS3_SPI tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" depends on !ACPI && SPI_MASTER && INPUT select SENSORS_LIS3LV02D - default n help This driver provides support for the LIS3LV02Dx accelerometer connected via SPI. The accelerometer data is readable via @@ -24,7 +23,6 @@ config SENSORS_LIS3_I2C tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)" depends on I2C && INPUT select SENSORS_LIS3LV02D - default n help This driver provides support for the LIS3LV02Dx accelerometer connected via I2C. The accelerometer data is readable via diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 951c984de61a..fb10eafe9bde 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -15,8 +15,7 @@ KCOV_INSTRUMENT_rodata.o := n OBJCOPYFLAGS := OBJCOPYFLAGS_rodata_objcopy.o := \ - --set-section-flags .text=alloc,readonly \ - --rename-section .text=.rodata + --rename-section .text=.rodata,alloc,readonly,load targets += rodata.o rodata_objcopy.o $(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE $(call if_changed,objcopy) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index d9fcfd3b5af0..1606658b9b7e 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -266,3 +266,69 @@ void lkdtm_STACK_GUARD_PAGE_TRAILING(void) pr_err("FAIL: accessed page after stack!\n"); } + +void lkdtm_UNSET_SMEP(void) +{ +#ifdef CONFIG_X86_64 +#define MOV_CR4_DEPTH 64 + void (*direct_write_cr4)(unsigned long val); + unsigned char *insn; + unsigned long cr4; + int i; + + cr4 = native_read_cr4(); + + if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) { + pr_err("FAIL: SMEP not in use\n"); + return; + } + cr4 &= ~(X86_CR4_SMEP); + + pr_info("trying to clear SMEP normally\n"); + native_write_cr4(cr4); + if (cr4 == native_read_cr4()) { + pr_err("FAIL: pinning SMEP failed!\n"); + cr4 |= X86_CR4_SMEP; + pr_info("restoring SMEP\n"); + native_write_cr4(cr4); + return; + } + pr_info("ok: SMEP did not get cleared\n"); + + /* + * To test the post-write pinning verification we need to call + * directly into the middle of native_write_cr4() where the + * cr4 write happens, skipping any pinning. This searches for + * the cr4 writing instruction. + */ + insn = (unsigned char *)native_write_cr4; + for (i = 0; i < MOV_CR4_DEPTH; i++) { + /* mov %rdi, %cr4 */ + if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7) + break; + /* mov %rdi,%rax; mov %rax, %cr4 */ + if (insn[i] == 0x48 && insn[i+1] == 0x89 && + insn[i+2] == 0xf8 && insn[i+3] == 0x0f && + insn[i+4] == 0x22 && insn[i+5] == 0xe0) + break; + } + if (i >= MOV_CR4_DEPTH) { + pr_info("ok: cannot locate cr4 writing call gadget\n"); + return; + } + direct_write_cr4 = (void *)(insn + i); + + pr_info("trying to clear SMEP with call gadget\n"); + direct_write_cr4(cr4); + if (native_read_cr4() & X86_CR4_SMEP) { + pr_info("ok: SMEP removal was reverted\n"); + } else { + pr_err("FAIL: cleared SMEP not detected!\n"); + cr4 |= X86_CR4_SMEP; + pr_info("restoring SMEP\n"); + native_write_cr4(cr4); + } +#else + pr_err("FAIL: this test is x86_64-only\n"); +#endif +} diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index bba49abb6750..756794ac8fd8 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -114,6 +114,7 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(CORRUPT_USER_DS), CRASHTYPE(STACK_GUARD_PAGE_LEADING), CRASHTYPE(STACK_GUARD_PAGE_TRAILING), + CRASHTYPE(UNSET_SMEP), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(OVERWRITE_ALLOCATION), CRASHTYPE(WRITE_AFTER_FREE), diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 23dc565b4307..bbcd370786d4 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -26,6 +26,7 @@ void lkdtm_CORRUPT_LIST_DEL(void); void lkdtm_CORRUPT_USER_DS(void); void lkdtm_STACK_GUARD_PAGE_LEADING(void); void lkdtm_STACK_GUARD_PAGE_TRAILING(void); +void lkdtm_UNSET_SMEP(void); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 0970142bcace..47cfd5005e1b 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -8,6 +8,7 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/debugfs.h> +#include <linux/seq_file.h> #include <linux/mei.h> @@ -15,104 +16,56 @@ #include "client.h" #include "hw.h" -static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, - size_t cnt, loff_t *ppos) +static int mei_dbgfs_meclients_show(struct seq_file *m, void *unused) { - struct mei_device *dev = fp->private_data; + struct mei_device *dev = m->private; struct mei_me_client *me_cl; - size_t bufsz = 1; - char *buf; int i = 0; - int pos = 0; - int ret; -#define HDR \ -" |id|fix| UUID |con|msg len|sb|refc|\n" + if (!dev) + return -ENODEV; down_read(&dev->me_clients_rwsem); - list_for_each_entry(me_cl, &dev->me_clients, list) - bufsz++; - bufsz *= sizeof(HDR) + 1; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - up_read(&dev->me_clients_rwsem); - return -ENOMEM; - } - - pos += scnprintf(buf + pos, bufsz - pos, HDR); -#undef HDR + seq_puts(m, " |id|fix| UUID |con|msg len|sb|refc|\n"); /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) goto out; list_for_each_entry(me_cl, &dev->me_clients, list) { - - if (mei_me_cl_get(me_cl)) { - pos += scnprintf(buf + pos, bufsz - pos, - "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", - i++, me_cl->client_id, - me_cl->props.fixed_address, - &me_cl->props.protocol_name, - me_cl->props.max_number_of_connections, - me_cl->props.max_msg_length, - me_cl->props.single_recv_buf, - kref_read(&me_cl->refcnt)); - - mei_me_cl_put(me_cl); - } + if (!mei_me_cl_get(me_cl)) + continue; + + seq_printf(m, "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", + i++, me_cl->client_id, + me_cl->props.fixed_address, + &me_cl->props.protocol_name, + me_cl->props.max_number_of_connections, + me_cl->props.max_msg_length, + me_cl->props.single_recv_buf, + kref_read(&me_cl->refcnt)); + mei_me_cl_put(me_cl); } out: up_read(&dev->me_clients_rwsem); - ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); - kfree(buf); - return ret; + return 0; } +DEFINE_SHOW_ATTRIBUTE(mei_dbgfs_meclients); -static const struct file_operations mei_dbgfs_fops_meclients = { - .open = simple_open, - .read = mei_dbgfs_read_meclients, - .llseek = generic_file_llseek, -}; - -static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, - size_t cnt, loff_t *ppos) +static int mei_dbgfs_active_show(struct seq_file *m, void *unused) { - struct mei_device *dev = fp->private_data; + struct mei_device *dev = m->private; struct mei_cl *cl; - size_t bufsz = 1; - char *buf; int i = 0; - int pos = 0; - int ret; - -#define HDR " |me|host|state|rd|wr|wrq\n" if (!dev) return -ENODEV; mutex_lock(&dev->device_lock); - /* - * if the driver is not enabled the list won't be consistent, - * we output empty table - */ - if (dev->dev_state == MEI_DEV_ENABLED) - list_for_each_entry(cl, &dev->file_list, link) - bufsz++; - - bufsz *= sizeof(HDR) + 1; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - mutex_unlock(&dev->device_lock); - return -ENOMEM; - } - - pos += scnprintf(buf + pos, bufsz - pos, HDR); -#undef HDR + seq_puts(m, " |me|host|state|rd|wr|wrq\n"); /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) @@ -120,76 +73,44 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, list_for_each_entry(cl, &dev->file_list, link) { - pos += scnprintf(buf + pos, bufsz - pos, - "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n", - i, mei_cl_me_id(cl), cl->host_client_id, cl->state, - !list_empty(&cl->rd_completed), cl->writing_state, - cl->tx_cb_queued); + seq_printf(m, "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n", + i, mei_cl_me_id(cl), cl->host_client_id, cl->state, + !list_empty(&cl->rd_completed), cl->writing_state, + cl->tx_cb_queued); i++; } out: mutex_unlock(&dev->device_lock); - ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); - kfree(buf); - return ret; + return 0; } +DEFINE_SHOW_ATTRIBUTE(mei_dbgfs_active); -static const struct file_operations mei_dbgfs_fops_active = { - .open = simple_open, - .read = mei_dbgfs_read_active, - .llseek = generic_file_llseek, -}; - -static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, - size_t cnt, loff_t *ppos) +static int mei_dbgfs_devstate_show(struct seq_file *m, void *unused) { - struct mei_device *dev = fp->private_data; - const size_t bufsz = 1024; - char *buf = kzalloc(bufsz, GFP_KERNEL); - int pos = 0; - int ret; - - if (!buf) - return -ENOMEM; + struct mei_device *dev = m->private; - pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", - mei_dev_state_str(dev->dev_state)); - pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", - mei_hbm_state_str(dev->hbm_state)); + seq_printf(m, "dev: %s\n", mei_dev_state_str(dev->dev_state)); + seq_printf(m, "hbm: %s\n", mei_hbm_state_str(dev->hbm_state)); if (dev->hbm_state >= MEI_HBM_ENUM_CLIENTS && dev->hbm_state <= MEI_HBM_STARTED) { - pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n", - dev->hbm_f_pg_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", - dev->hbm_f_dc_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tIE: %01d\n", - dev->hbm_f_ie_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", - dev->hbm_f_dot_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", - dev->hbm_f_ev_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n", - dev->hbm_f_fa_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tOS: %01d\n", - dev->hbm_f_os_supported); - pos += scnprintf(buf + pos, bufsz - pos, "\tDR: %01d\n", - dev->hbm_f_dr_supported); + seq_puts(m, "hbm features:\n"); + seq_printf(m, "\tPG: %01d\n", dev->hbm_f_pg_supported); + seq_printf(m, "\tDC: %01d\n", dev->hbm_f_dc_supported); + seq_printf(m, "\tIE: %01d\n", dev->hbm_f_ie_supported); + seq_printf(m, "\tDOT: %01d\n", dev->hbm_f_dot_supported); + seq_printf(m, "\tEV: %01d\n", dev->hbm_f_ev_supported); + seq_printf(m, "\tFA: %01d\n", dev->hbm_f_fa_supported); + seq_printf(m, "\tOS: %01d\n", dev->hbm_f_os_supported); + seq_printf(m, "\tDR: %01d\n", dev->hbm_f_dr_supported); } - pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", - mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", - mei_pg_state_str(mei_pg_state(dev))); - ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); - kfree(buf); - return ret; + seq_printf(m, "pg: %s, %s\n", + mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", + mei_pg_state_str(mei_pg_state(dev))); + return 0; } -static const struct file_operations mei_dbgfs_fops_devstate = { - .open = simple_open, - .read = mei_dbgfs_read_devstate, - .llseek = generic_file_llseek, -}; +DEFINE_SHOW_ATTRIBUTE(mei_dbgfs_devstate); static ssize_t mei_dbgfs_write_allow_fa(struct file *file, const char __user *user_buf, @@ -208,7 +129,7 @@ static ssize_t mei_dbgfs_write_allow_fa(struct file *file, return ret; } -static const struct file_operations mei_dbgfs_fops_allow_fa = { +static const struct file_operations mei_dbgfs_allow_fa_fops = { .open = simple_open, .read = debugfs_read_file_bool, .write = mei_dbgfs_write_allow_fa, @@ -247,26 +168,26 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) dev->dbgfs_dir = dir; f = debugfs_create_file("meclients", S_IRUSR, dir, - dev, &mei_dbgfs_fops_meclients); + dev, &mei_dbgfs_meclients_fops); if (!f) { dev_err(dev->dev, "meclients: registration failed\n"); goto err; } f = debugfs_create_file("active", S_IRUSR, dir, - dev, &mei_dbgfs_fops_active); + dev, &mei_dbgfs_active_fops); if (!f) { dev_err(dev->dev, "active: registration failed\n"); goto err; } f = debugfs_create_file("devstate", S_IRUSR, dir, - dev, &mei_dbgfs_fops_devstate); + dev, &mei_dbgfs_devstate_fops); if (!f) { dev_err(dev->dev, "devstate: registration failed\n"); goto err; } f = debugfs_create_file("allow_fixed_address", S_IRUSR | S_IWUSR, dir, &dev->allow_fixed_address, - &mei_dbgfs_fops_allow_fa); + &mei_dbgfs_allow_fa_fops); if (!f) { dev_err(dev->dev, "allow_fixed_address: registration failed\n"); goto err; @@ -276,4 +197,3 @@ err: mei_dbgfs_deregister(dev); return -ENODEV; } - diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index b07000202d4a..ed816939fb32 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -2,7 +2,7 @@ /* * Copyright © 2019 Intel Corporation * - * Mei_hdcp.c: HDCP client driver for mei bus + * mei_hdcp.c: HDCP client driver for mei bus * * Author: * Ramalingam C <ramalingam.c@intel.com> @@ -11,12 +11,9 @@ /** * DOC: MEI_HDCP Client Driver * - * This is a client driver to the mei_bus to make the HDCP2.2 services of - * ME FW available for the interested consumers like I915. - * - * This module will act as a translation layer between HDCP protocol - * implementor(I915) and ME FW by translating HDCP2.2 authentication - * messages to ME FW command payloads and vice versa. + * The mei_hdcp driver acts as a translation layer between HDCP 2.2 + * protocol implementer (I915) and ME FW by translating HDCP2.2 + * negotiation messages to ME FW command payloads and vice versa. */ #include <linux/module.h> diff --git a/drivers/misc/mic/scif/scif_main.c b/drivers/misc/mic/scif/scif_main.c index 490e3bdc1941..e2278bf9f11d 100644 --- a/drivers/misc/mic/scif/scif_main.c +++ b/drivers/misc/mic/scif/scif_main.c @@ -133,6 +133,7 @@ static int scif_setup_scifdev(void) static void scif_destroy_scifdev(void) { kfree(scif_dev); + scif_dev = NULL; } static int scif_probe(struct scif_hw_dev *sdev) diff --git a/drivers/misc/ocxl/Kconfig b/drivers/misc/ocxl/Kconfig index 7fb6d39d4c5a..1916fa65f2f2 100644 --- a/drivers/misc/ocxl/Kconfig +++ b/drivers/misc/ocxl/Kconfig @@ -5,7 +5,6 @@ config OCXL_BASE bool - default n select PPC_COPRO_BASE config OCXL diff --git a/drivers/misc/ocxl/context.c b/drivers/misc/ocxl/context.c index bab9c9364184..994563a078eb 100644 --- a/drivers/misc/ocxl/context.c +++ b/drivers/misc/ocxl/context.c @@ -69,6 +69,7 @@ static void xsl_fault_error(void *data, u64 addr, u64 dsisr) int ocxl_context_attach(struct ocxl_context *ctx, u64 amr, struct mm_struct *mm) { int rc; + unsigned long pidr = 0; // Locks both status & tidr mutex_lock(&ctx->status_mutex); @@ -77,9 +78,11 @@ int ocxl_context_attach(struct ocxl_context *ctx, u64 amr, struct mm_struct *mm) goto out; } - rc = ocxl_link_add_pe(ctx->afu->fn->link, ctx->pasid, - mm->context.id, ctx->tidr, amr, mm, - xsl_fault_error, ctx); + if (mm) + pidr = mm->context.id; + + rc = ocxl_link_add_pe(ctx->afu->fn->link, ctx->pasid, pidr, ctx->tidr, + amr, mm, xsl_fault_error, ctx); if (rc) goto out; diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c index cce5b0d64505..58d111afd9f6 100644 --- a/drivers/misc/ocxl/link.c +++ b/drivers/misc/ocxl/link.c @@ -224,6 +224,17 @@ static irqreturn_t xsl_fault_handler(int irq, void *data) ack_irq(spa, ADDRESS_ERROR); return IRQ_HANDLED; } + + if (!pe_data->mm) { + /* + * translation fault from a kernel context - an OpenCAPI + * device tried to access a bad kernel address + */ + rcu_read_unlock(); + pr_warn("Unresolved OpenCAPI xsl fault in kernel context\n"); + ack_irq(spa, ADDRESS_ERROR); + return IRQ_HANDLED; + } WARN_ON(pe_data->mm->context.id != pid); if (mmget_not_zero(pe_data->mm)) { @@ -523,7 +534,13 @@ int ocxl_link_add_pe(void *link_handle, int pasid, u32 pidr, u32 tidr, pe->amr = cpu_to_be64(amr); pe->software_state = cpu_to_be32(SPA_PE_VALID); - mm_context_add_copro(mm); + /* + * For user contexts, register a copro so that TLBIs are seen + * by the nest MMU. If we have a kernel context, TLBIs are + * already global. + */ + if (mm) + mm_context_add_copro(mm); /* * Barrier is to make sure PE is visible in the SPA before it * is used by the device. It also helps with the global TLBI @@ -546,7 +563,8 @@ int ocxl_link_add_pe(void *link_handle, int pasid, u32 pidr, u32 tidr, * have a reference on mm_users. Incrementing mm_count solves * the problem. */ - mmgrab(mm); + if (mm) + mmgrab(mm); trace_ocxl_context_add(current->pid, spa->spa_mem, pasid, pidr, tidr); unlock: mutex_unlock(&spa->spa_lock); @@ -652,8 +670,10 @@ int ocxl_link_remove_pe(void *link_handle, int pasid) if (!pe_data) { WARN(1, "Couldn't find pe data when removing PE\n"); } else { - mm_context_remove_copro(pe_data->mm); - mmdrop(pe_data->mm); + if (pe_data->mm) { + mm_context_remove_copro(pe_data->mm); + mmdrop(pe_data->mm); + } kfree_rcu(pe_data, rcu); } unlock: diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 3eba1c420cc0..782ce95d3f17 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -70,7 +70,7 @@ xpc_get_rsvd_page_pa(int nasid) unsigned long rp_pa = nasid; /* seed with nasid */ size_t len = 0; size_t buf_len = 0; - void *buf = buf; + void *buf = NULL; void *buf_base = NULL; enum xp_retval (*get_partition_rsvd_page_pa) (void *, u64 *, unsigned long *, size_t *) = diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 5b7afd6190fe..09db397df287 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -336,7 +336,7 @@ static struct i2c_driver tsl2550_driver; static int tsl2550_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct i2c_adapter *adapter = client->adapter; struct tsl2550_data *data; int *opmode, err = 0; diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index ad807d5a3141..043eed845246 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -28,6 +28,8 @@ #include <linux/rwsem.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/mount.h> +#include <linux/balloon_compaction.h> #include <linux/vmw_vmci_defs.h> #include <linux/vmw_vmci_api.h> #include <asm/hypervisor.h> @@ -38,25 +40,20 @@ MODULE_ALIAS("dmi:*:svnVMware*:*"); MODULE_ALIAS("vmware_vmmemctl"); MODULE_LICENSE("GPL"); -/* - * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We don't allow wait - * (__GFP_RECLAIM) for huge page allocations. Use __GFP_NOWARN, to suppress page - * allocation failure warnings. Disallow access to emergency low-memory pools. - */ -#define VMW_HUGE_PAGE_ALLOC_FLAGS (__GFP_HIGHMEM|__GFP_NOWARN| \ - __GFP_NOMEMALLOC) +static bool __read_mostly vmwballoon_shrinker_enable; +module_param(vmwballoon_shrinker_enable, bool, 0444); +MODULE_PARM_DESC(vmwballoon_shrinker_enable, + "Enable non-cooperative out-of-memory protection. Disabled by default as it may degrade performance."); -/* - * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We allow lightweight - * reclamation (__GFP_NORETRY). Use __GFP_NOWARN, to suppress page allocation - * failure warnings. Disallow access to emergency low-memory pools. - */ -#define VMW_PAGE_ALLOC_FLAGS (__GFP_HIGHMEM|__GFP_NOWARN| \ - __GFP_NOMEMALLOC|__GFP_NORETRY) +/* Delay in seconds after shrink before inflation. */ +#define VMBALLOON_SHRINK_DELAY (5) /* Maximum number of refused pages we accumulate during inflation cycle */ #define VMW_BALLOON_MAX_REFUSED 16 +/* Magic number for the balloon mount-point */ +#define BALLOON_VMW_MAGIC 0x0ba11007 + /* * Hypervisor communication port definitions. */ @@ -229,29 +226,26 @@ enum vmballoon_stat_general { VMW_BALLOON_STAT_TIMER, VMW_BALLOON_STAT_DOORBELL, VMW_BALLOON_STAT_RESET, - VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_RESET + VMW_BALLOON_STAT_SHRINK, + VMW_BALLOON_STAT_SHRINK_FREE, + VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_SHRINK_FREE }; #define VMW_BALLOON_STAT_NUM (VMW_BALLOON_STAT_LAST + 1) - static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching); static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled); struct vmballoon_ctl { struct list_head pages; struct list_head refused_pages; + struct list_head prealloc_pages; unsigned int n_refused_pages; unsigned int n_pages; enum vmballoon_page_size_type page_size; enum vmballoon_op op; }; -struct vmballoon_page_size { - /* list of reserved physical pages */ - struct list_head pages; -}; - /** * struct vmballoon_batch_entry - a batch entry for lock or unlock. * @@ -266,8 +260,6 @@ struct vmballoon_batch_entry { } __packed; struct vmballoon { - struct vmballoon_page_size page_sizes[VMW_BALLOON_NUM_PAGE_SIZES]; - /** * @max_page_size: maximum supported page size for ballooning. * @@ -340,6 +332,15 @@ struct vmballoon { */ struct page *page; + /** + * @shrink_timeout: timeout until the next inflation. + * + * After an shrink event, indicates the time in jiffies after which + * inflation is allowed again. Can be written concurrently with reads, + * so must use READ_ONCE/WRITE_ONCE when accessing. + */ + unsigned long shrink_timeout; + /* statistics */ struct vmballoon_stats *stats; @@ -348,9 +349,21 @@ struct vmballoon { struct dentry *dbg_entry; #endif + /** + * @b_dev_info: balloon device information descriptor. + */ + struct balloon_dev_info b_dev_info; + struct delayed_work dwork; /** + * @huge_pages - list of the inflated 2MB pages. + * + * Protected by @b_dev_info.pages_lock . + */ + struct list_head huge_pages; + + /** * @vmci_doorbell. * * Protected by @conf_sem. @@ -368,6 +381,20 @@ struct vmballoon { * Lock ordering: @conf_sem -> @comm_lock . */ spinlock_t comm_lock; + + /** + * @shrinker: shrinker interface that is used to avoid over-inflation. + */ + struct shrinker shrinker; + + /** + * @shrinker_registered: whether the shrinker was registered. + * + * The shrinker interface does not handle gracefully the removal of + * shrinker that was not registered before. This indication allows to + * simplify the unregistration process. + */ + bool shrinker_registered; }; static struct vmballoon balloon; @@ -642,15 +669,25 @@ static int vmballoon_alloc_page_list(struct vmballoon *b, unsigned int i; for (i = 0; i < req_n_pages; i++) { - if (ctl->page_size == VMW_BALLOON_2M_PAGE) - page = alloc_pages(VMW_HUGE_PAGE_ALLOC_FLAGS, - VMW_BALLOON_2M_ORDER); - else - page = alloc_page(VMW_PAGE_ALLOC_FLAGS); - - /* Update statistics */ - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC, - ctl->page_size); + /* + * First check if we happen to have pages that were allocated + * before. This happens when 2MB page rejected during inflation + * by the hypervisor, and then split into 4KB pages. + */ + if (!list_empty(&ctl->prealloc_pages)) { + page = list_first_entry(&ctl->prealloc_pages, + struct page, lru); + list_del(&page->lru); + } else { + if (ctl->page_size == VMW_BALLOON_2M_PAGE) + page = alloc_pages(__GFP_HIGHMEM|__GFP_NOWARN| + __GFP_NOMEMALLOC, VMW_BALLOON_2M_ORDER); + else + page = balloon_page_alloc(); + + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC, + ctl->page_size); + } if (page) { vmballoon_mark_page_offline(page, ctl->page_size); @@ -896,7 +933,8 @@ static void vmballoon_release_page_list(struct list_head *page_list, __free_pages(page, vmballoon_page_order(page_size)); } - *n_pages = 0; + if (n_pages) + *n_pages = 0; } @@ -942,6 +980,10 @@ static int64_t vmballoon_change(struct vmballoon *b) size - target < vmballoon_page_in_frames(VMW_BALLOON_2M_PAGE)) return 0; + /* If an out-of-memory recently occurred, inflation is disallowed. */ + if (target > size && time_before(jiffies, READ_ONCE(b->shrink_timeout))) + return 0; + return target - size; } @@ -961,9 +1003,22 @@ static void vmballoon_enqueue_page_list(struct vmballoon *b, unsigned int *n_pages, enum vmballoon_page_size_type page_size) { - struct vmballoon_page_size *page_size_info = &b->page_sizes[page_size]; + unsigned long flags; + + if (page_size == VMW_BALLOON_4K_PAGE) { + balloon_page_list_enqueue(&b->b_dev_info, pages); + } else { + /* + * Keep the huge pages in a local list which is not available + * for the balloon compaction mechanism. + */ + spin_lock_irqsave(&b->b_dev_info.pages_lock, flags); + list_splice_init(pages, &b->huge_pages); + __count_vm_events(BALLOON_INFLATE, *n_pages * + vmballoon_page_in_frames(VMW_BALLOON_2M_PAGE)); + spin_unlock_irqrestore(&b->b_dev_info.pages_lock, flags); + } - list_splice_init(pages, &page_size_info->pages); *n_pages = 0; } @@ -986,19 +1041,58 @@ static void vmballoon_dequeue_page_list(struct vmballoon *b, enum vmballoon_page_size_type page_size, unsigned int n_req_pages) { - struct vmballoon_page_size *page_size_info = &b->page_sizes[page_size]; struct page *page, *tmp; unsigned int i = 0; + unsigned long flags; + + /* In the case of 4k pages, use the compaction infrastructure */ + if (page_size == VMW_BALLOON_4K_PAGE) { + *n_pages = balloon_page_list_dequeue(&b->b_dev_info, pages, + n_req_pages); + return; + } - list_for_each_entry_safe(page, tmp, &page_size_info->pages, lru) { + /* 2MB pages */ + spin_lock_irqsave(&b->b_dev_info.pages_lock, flags); + list_for_each_entry_safe(page, tmp, &b->huge_pages, lru) { list_move(&page->lru, pages); if (++i == n_req_pages) break; } + + __count_vm_events(BALLOON_DEFLATE, + i * vmballoon_page_in_frames(VMW_BALLOON_2M_PAGE)); + spin_unlock_irqrestore(&b->b_dev_info.pages_lock, flags); *n_pages = i; } /** + * vmballoon_split_refused_pages() - Split the 2MB refused pages to 4k. + * + * If inflation of 2MB pages was denied by the hypervisor, it is likely to be + * due to one or few 4KB pages. These 2MB pages may keep being allocated and + * then being refused. To prevent this case, this function splits the refused + * pages into 4KB pages and adds them into @prealloc_pages list. + * + * @ctl: pointer for the %struct vmballoon_ctl, which defines the operation. + */ +static void vmballoon_split_refused_pages(struct vmballoon_ctl *ctl) +{ + struct page *page, *tmp; + unsigned int i, order; + + order = vmballoon_page_order(ctl->page_size); + + list_for_each_entry_safe(page, tmp, &ctl->refused_pages, lru) { + list_del(&page->lru); + split_page(page, order); + for (i = 0; i < (1 << order); i++) + list_add(&page[i].lru, &ctl->prealloc_pages); + } + ctl->n_refused_pages = 0; +} + +/** * vmballoon_inflate() - Inflate the balloon towards its target size. * * @b: pointer to the balloon. @@ -1009,6 +1103,7 @@ static void vmballoon_inflate(struct vmballoon *b) struct vmballoon_ctl ctl = { .pages = LIST_HEAD_INIT(ctl.pages), .refused_pages = LIST_HEAD_INIT(ctl.refused_pages), + .prealloc_pages = LIST_HEAD_INIT(ctl.prealloc_pages), .page_size = b->max_page_size, .op = VMW_BALLOON_INFLATE }; @@ -1056,10 +1151,10 @@ static void vmballoon_inflate(struct vmballoon *b) break; /* - * Ignore errors from locking as we now switch to 4k - * pages and we might get different errors. + * Split the refused pages to 4k. This will also empty + * the refused pages list. */ - vmballoon_release_refused_pages(b, &ctl); + vmballoon_split_refused_pages(&ctl); ctl.page_size--; } @@ -1073,6 +1168,8 @@ static void vmballoon_inflate(struct vmballoon *b) */ if (ctl.n_refused_pages != 0) vmballoon_release_refused_pages(b, &ctl); + + vmballoon_release_page_list(&ctl.prealloc_pages, NULL, ctl.page_size); } /** @@ -1411,6 +1508,90 @@ static void vmballoon_work(struct work_struct *work) } +/** + * vmballoon_shrinker_scan() - deflate the balloon due to memory pressure. + * @shrinker: pointer to the balloon shrinker. + * @sc: page reclaim information. + * + * Returns: number of pages that were freed during deflation. + */ +static unsigned long vmballoon_shrinker_scan(struct shrinker *shrinker, + struct shrink_control *sc) +{ + struct vmballoon *b = &balloon; + unsigned long deflated_frames; + + pr_debug("%s - size: %llu", __func__, atomic64_read(&b->size)); + + vmballoon_stats_gen_inc(b, VMW_BALLOON_STAT_SHRINK); + + /* + * If the lock is also contended for read, we cannot easily reclaim and + * we bail out. + */ + if (!down_read_trylock(&b->conf_sem)) + return 0; + + deflated_frames = vmballoon_deflate(b, sc->nr_to_scan, true); + + vmballoon_stats_gen_add(b, VMW_BALLOON_STAT_SHRINK_FREE, + deflated_frames); + + /* + * Delay future inflation for some time to mitigate the situations in + * which balloon continuously grows and shrinks. Use WRITE_ONCE() since + * the access is asynchronous. + */ + WRITE_ONCE(b->shrink_timeout, jiffies + HZ * VMBALLOON_SHRINK_DELAY); + + up_read(&b->conf_sem); + + return deflated_frames; +} + +/** + * vmballoon_shrinker_count() - return the number of ballooned pages. + * @shrinker: pointer to the balloon shrinker. + * @sc: page reclaim information. + * + * Returns: number of 4k pages that are allocated for the balloon and can + * therefore be reclaimed under pressure. + */ +static unsigned long vmballoon_shrinker_count(struct shrinker *shrinker, + struct shrink_control *sc) +{ + struct vmballoon *b = &balloon; + + return atomic64_read(&b->size); +} + +static void vmballoon_unregister_shrinker(struct vmballoon *b) +{ + if (b->shrinker_registered) + unregister_shrinker(&b->shrinker); + b->shrinker_registered = false; +} + +static int vmballoon_register_shrinker(struct vmballoon *b) +{ + int r; + + /* Do nothing if the shrinker is not enabled */ + if (!vmwballoon_shrinker_enable) + return 0; + + b->shrinker.scan_objects = vmballoon_shrinker_scan; + b->shrinker.count_objects = vmballoon_shrinker_count; + b->shrinker.seeks = DEFAULT_SEEKS; + + r = register_shrinker(&b->shrinker); + + if (r == 0) + b->shrinker_registered = true; + + return r; +} + /* * DEBUGFS Interface */ @@ -1428,6 +1609,8 @@ static const char * const vmballoon_stat_names[] = { [VMW_BALLOON_STAT_TIMER] = "timer", [VMW_BALLOON_STAT_DOORBELL] = "doorbell", [VMW_BALLOON_STAT_RESET] = "reset", + [VMW_BALLOON_STAT_SHRINK] = "shrink", + [VMW_BALLOON_STAT_SHRINK_FREE] = "shrinkFree" }; static int vmballoon_enable_stats(struct vmballoon *b) @@ -1552,9 +1735,204 @@ static inline void vmballoon_debugfs_exit(struct vmballoon *b) #endif /* CONFIG_DEBUG_FS */ + +#ifdef CONFIG_BALLOON_COMPACTION + +static struct dentry *vmballoon_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + static const struct dentry_operations ops = { + .d_dname = simple_dname, + }; + + return mount_pseudo(fs_type, "balloon-vmware:", NULL, &ops, + BALLOON_VMW_MAGIC); +} + +static struct file_system_type vmballoon_fs = { + .name = "balloon-vmware", + .mount = vmballoon_mount, + .kill_sb = kill_anon_super, +}; + +static struct vfsmount *vmballoon_mnt; + +/** + * vmballoon_migratepage() - migrates a balloon page. + * @b_dev_info: balloon device information descriptor. + * @newpage: the page to which @page should be migrated. + * @page: a ballooned page that should be migrated. + * @mode: migration mode, ignored. + * + * This function is really open-coded, but that is according to the interface + * that balloon_compaction provides. + * + * Return: zero on success, -EAGAIN when migration cannot be performed + * momentarily, and -EBUSY if migration failed and should be retried + * with that specific page. + */ +static int vmballoon_migratepage(struct balloon_dev_info *b_dev_info, + struct page *newpage, struct page *page, + enum migrate_mode mode) +{ + unsigned long status, flags; + struct vmballoon *b; + int ret; + + b = container_of(b_dev_info, struct vmballoon, b_dev_info); + + /* + * If the semaphore is taken, there is ongoing configuration change + * (i.e., balloon reset), so try again. + */ + if (!down_read_trylock(&b->conf_sem)) + return -EAGAIN; + + spin_lock(&b->comm_lock); + /* + * We must start by deflating and not inflating, as otherwise the + * hypervisor may tell us that it has enough memory and the new page is + * not needed. Since the old page is isolated, we cannot use the list + * interface to unlock it, as the LRU field is used for isolation. + * Instead, we use the native interface directly. + */ + vmballoon_add_page(b, 0, page); + status = vmballoon_lock_op(b, 1, VMW_BALLOON_4K_PAGE, + VMW_BALLOON_DEFLATE); + + if (status == VMW_BALLOON_SUCCESS) + status = vmballoon_status_page(b, 0, &page); + + /* + * If a failure happened, let the migration mechanism know that it + * should not retry. + */ + if (status != VMW_BALLOON_SUCCESS) { + spin_unlock(&b->comm_lock); + ret = -EBUSY; + goto out_unlock; + } + + /* + * The page is isolated, so it is safe to delete it without holding + * @pages_lock . We keep holding @comm_lock since we will need it in a + * second. + */ + balloon_page_delete(page); + + put_page(page); + + /* Inflate */ + vmballoon_add_page(b, 0, newpage); + status = vmballoon_lock_op(b, 1, VMW_BALLOON_4K_PAGE, + VMW_BALLOON_INFLATE); + + if (status == VMW_BALLOON_SUCCESS) + status = vmballoon_status_page(b, 0, &newpage); + + spin_unlock(&b->comm_lock); + + if (status != VMW_BALLOON_SUCCESS) { + /* + * A failure happened. While we can deflate the page we just + * inflated, this deflation can also encounter an error. Instead + * we will decrease the size of the balloon to reflect the + * change and report failure. + */ + atomic64_dec(&b->size); + ret = -EBUSY; + } else { + /* + * Success. Take a reference for the page, and we will add it to + * the list after acquiring the lock. + */ + get_page(newpage); + ret = MIGRATEPAGE_SUCCESS; + } + + /* Update the balloon list under the @pages_lock */ + spin_lock_irqsave(&b->b_dev_info.pages_lock, flags); + + /* + * On inflation success, we already took a reference for the @newpage. + * If we succeed just insert it to the list and update the statistics + * under the lock. + */ + if (ret == MIGRATEPAGE_SUCCESS) { + balloon_page_insert(&b->b_dev_info, newpage); + __count_vm_event(BALLOON_MIGRATE); + } + + /* + * We deflated successfully, so regardless to the inflation success, we + * need to reduce the number of isolated_pages. + */ + b->b_dev_info.isolated_pages--; + spin_unlock_irqrestore(&b->b_dev_info.pages_lock, flags); + +out_unlock: + up_read(&b->conf_sem); + return ret; +} + +/** + * vmballoon_compaction_deinit() - removes compaction related data. + * + * @b: pointer to the balloon. + */ +static void vmballoon_compaction_deinit(struct vmballoon *b) +{ + if (!IS_ERR(b->b_dev_info.inode)) + iput(b->b_dev_info.inode); + + b->b_dev_info.inode = NULL; + kern_unmount(vmballoon_mnt); + vmballoon_mnt = NULL; +} + +/** + * vmballoon_compaction_init() - initialized compaction for the balloon. + * + * @b: pointer to the balloon. + * + * If during the initialization a failure occurred, this function does not + * perform cleanup. The caller must call vmballoon_compaction_deinit() in this + * case. + * + * Return: zero on success or error code on failure. + */ +static __init int vmballoon_compaction_init(struct vmballoon *b) +{ + vmballoon_mnt = kern_mount(&vmballoon_fs); + if (IS_ERR(vmballoon_mnt)) + return PTR_ERR(vmballoon_mnt); + + b->b_dev_info.migratepage = vmballoon_migratepage; + b->b_dev_info.inode = alloc_anon_inode(vmballoon_mnt->mnt_sb); + + if (IS_ERR(b->b_dev_info.inode)) + return PTR_ERR(b->b_dev_info.inode); + + b->b_dev_info.inode->i_mapping->a_ops = &balloon_aops; + return 0; +} + +#else /* CONFIG_BALLOON_COMPACTION */ + +static void vmballoon_compaction_deinit(struct vmballoon *b) +{ +} + +static int vmballoon_compaction_init(struct vmballoon *b) +{ + return 0; +} + +#endif /* CONFIG_BALLOON_COMPACTION */ + static int __init vmballoon_init(void) { - enum vmballoon_page_size_type page_size; int error; /* @@ -1564,17 +1942,26 @@ static int __init vmballoon_init(void) if (x86_hyper_type != X86_HYPER_VMWARE) return -ENODEV; - for (page_size = VMW_BALLOON_4K_PAGE; - page_size <= VMW_BALLOON_LAST_SIZE; page_size++) - INIT_LIST_HEAD(&balloon.page_sizes[page_size].pages); - - INIT_DELAYED_WORK(&balloon.dwork, vmballoon_work); + error = vmballoon_register_shrinker(&balloon); + if (error) + goto fail; + error = vmballoon_debugfs_init(&balloon); if (error) - return error; + goto fail; + + /* + * Initialization of compaction must be done after the call to + * balloon_devinfo_init() . + */ + balloon_devinfo_init(&balloon.b_dev_info); + error = vmballoon_compaction_init(&balloon); + if (error) + goto fail; + INIT_LIST_HEAD(&balloon.huge_pages); spin_lock_init(&balloon.comm_lock); init_rwsem(&balloon.conf_sem); balloon.vmci_doorbell = VMCI_INVALID_HANDLE; @@ -1585,6 +1972,10 @@ static int __init vmballoon_init(void) queue_delayed_work(system_freezable_wq, &balloon.dwork, 0); return 0; +fail: + vmballoon_unregister_shrinker(&balloon); + vmballoon_compaction_deinit(&balloon); + return error; } /* @@ -1597,6 +1988,7 @@ late_initcall(vmballoon_init); static void __exit vmballoon_exit(void) { + vmballoon_unregister_shrinker(&balloon); vmballoon_vmci_cleanup(&balloon); cancel_delayed_work_sync(&balloon.dwork); @@ -1609,5 +2001,8 @@ static void __exit vmballoon_exit(void) */ vmballoon_send_start(&balloon, 0); vmballoon_pop(&balloon); + + /* Only once we popped the balloon, compaction can be deinit */ + vmballoon_compaction_deinit(&balloon); } module_exit(vmballoon_exit); diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index 300ed69fe2c7..16695366ec92 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -21,6 +21,9 @@ #include "vmci_driver.h" #include "vmci_event.h" +/* Use a wide upper bound for the maximum contexts. */ +#define VMCI_MAX_CONTEXTS 2000 + /* * List of current VMCI contexts. Contexts can be added by * vmci_ctx_create() and removed via vmci_ctx_destroy(). @@ -117,19 +120,22 @@ struct vmci_ctx *vmci_ctx_create(u32 cid, u32 priv_flags, /* Initialize host-specific VMCI context. */ init_waitqueue_head(&context->host_context.wait_queue); - context->queue_pair_array = vmci_handle_arr_create(0); + context->queue_pair_array = + vmci_handle_arr_create(0, VMCI_MAX_GUEST_QP_COUNT); if (!context->queue_pair_array) { error = -ENOMEM; goto err_free_ctx; } - context->doorbell_array = vmci_handle_arr_create(0); + context->doorbell_array = + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); if (!context->doorbell_array) { error = -ENOMEM; goto err_free_qp_array; } - context->pending_doorbell_array = vmci_handle_arr_create(0); + context->pending_doorbell_array = + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); if (!context->pending_doorbell_array) { error = -ENOMEM; goto err_free_db_array; @@ -204,7 +210,7 @@ static int ctx_fire_notification(u32 context_id, u32 priv_flags) * We create an array to hold the subscribers we find when * scanning through all contexts. */ - subscriber_array = vmci_handle_arr_create(0); + subscriber_array = vmci_handle_arr_create(0, VMCI_MAX_CONTEXTS); if (subscriber_array == NULL) return VMCI_ERROR_NO_MEM; @@ -623,20 +629,26 @@ int vmci_ctx_add_notification(u32 context_id, u32 remote_cid) spin_lock(&context->lock); - list_for_each_entry(n, &context->notifier_list, node) { - if (vmci_handle_is_equal(n->handle, notifier->handle)) { - exists = true; - break; + if (context->n_notifiers < VMCI_MAX_CONTEXTS) { + list_for_each_entry(n, &context->notifier_list, node) { + if (vmci_handle_is_equal(n->handle, notifier->handle)) { + exists = true; + break; + } } - } - if (exists) { - kfree(notifier); - result = VMCI_ERROR_ALREADY_EXISTS; + if (exists) { + kfree(notifier); + result = VMCI_ERROR_ALREADY_EXISTS; + } else { + list_add_tail_rcu(¬ifier->node, + &context->notifier_list); + context->n_notifiers++; + result = VMCI_SUCCESS; + } } else { - list_add_tail_rcu(¬ifier->node, &context->notifier_list); - context->n_notifiers++; - result = VMCI_SUCCESS; + kfree(notifier); + result = VMCI_ERROR_NO_MEM; } spin_unlock(&context->lock); @@ -721,8 +733,7 @@ static int vmci_ctx_get_chkpt_doorbells(struct vmci_ctx *context, u32 *buf_size, void **pbuf) { struct dbell_cpt_state *dbells; - size_t n_doorbells; - int i; + u32 i, n_doorbells; n_doorbells = vmci_handle_arr_get_size(context->doorbell_array); if (n_doorbells > 0) { @@ -860,7 +871,8 @@ int vmci_ctx_rcv_notifications_get(u32 context_id, spin_lock(&context->lock); *db_handle_array = context->pending_doorbell_array; - context->pending_doorbell_array = vmci_handle_arr_create(0); + context->pending_doorbell_array = + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); if (!context->pending_doorbell_array) { context->pending_doorbell_array = *db_handle_array; *db_handle_array = NULL; @@ -942,12 +954,11 @@ int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle) return VMCI_ERROR_NOT_FOUND; spin_lock(&context->lock); - if (!vmci_handle_arr_has_entry(context->doorbell_array, handle)) { - vmci_handle_arr_append_entry(&context->doorbell_array, handle); - result = VMCI_SUCCESS; - } else { + if (!vmci_handle_arr_has_entry(context->doorbell_array, handle)) + result = vmci_handle_arr_append_entry(&context->doorbell_array, + handle); + else result = VMCI_ERROR_DUPLICATE_ENTRY; - } spin_unlock(&context->lock); vmci_ctx_put(context); @@ -1083,15 +1094,16 @@ int vmci_ctx_notify_dbell(u32 src_cid, if (!vmci_handle_arr_has_entry( dst_context->pending_doorbell_array, handle)) { - vmci_handle_arr_append_entry( + result = vmci_handle_arr_append_entry( &dst_context->pending_doorbell_array, handle); - - ctx_signal_notify(dst_context); - wake_up(&dst_context->host_context.wait_queue); - + if (result == VMCI_SUCCESS) { + ctx_signal_notify(dst_context); + wake_up(&dst_context->host_context.wait_queue); + } + } else { + result = VMCI_SUCCESS; } - result = VMCI_SUCCESS; } spin_unlock(&dst_context->lock); } @@ -1118,13 +1130,11 @@ int vmci_ctx_qp_create(struct vmci_ctx *context, struct vmci_handle handle) if (context == NULL || vmci_handle_is_invalid(handle)) return VMCI_ERROR_INVALID_ARGS; - if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle)) { - vmci_handle_arr_append_entry(&context->queue_pair_array, - handle); - result = VMCI_SUCCESS; - } else { + if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle)) + result = vmci_handle_arr_append_entry( + &context->queue_pair_array, handle); + else result = VMCI_ERROR_DUPLICATE_ENTRY; - } return result; } diff --git a/drivers/misc/vmw_vmci/vmci_handle_array.c b/drivers/misc/vmw_vmci/vmci_handle_array.c index c527388f5d7b..de7fee7ead1b 100644 --- a/drivers/misc/vmw_vmci/vmci_handle_array.c +++ b/drivers/misc/vmw_vmci/vmci_handle_array.c @@ -8,24 +8,29 @@ #include <linux/slab.h> #include "vmci_handle_array.h" -static size_t handle_arr_calc_size(size_t capacity) +static size_t handle_arr_calc_size(u32 capacity) { - return sizeof(struct vmci_handle_arr) + + return VMCI_HANDLE_ARRAY_HEADER_SIZE + capacity * sizeof(struct vmci_handle); } -struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity) +struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity) { struct vmci_handle_arr *array; + if (max_capacity == 0 || capacity > max_capacity) + return NULL; + if (capacity == 0) - capacity = VMCI_HANDLE_ARRAY_DEFAULT_SIZE; + capacity = min((u32)VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY, + max_capacity); array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC); if (!array) return NULL; array->capacity = capacity; + array->max_capacity = max_capacity; array->size = 0; return array; @@ -36,27 +41,34 @@ void vmci_handle_arr_destroy(struct vmci_handle_arr *array) kfree(array); } -void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, - struct vmci_handle handle) +int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, + struct vmci_handle handle) { struct vmci_handle_arr *array = *array_ptr; if (unlikely(array->size >= array->capacity)) { /* reallocate. */ struct vmci_handle_arr *new_array; - size_t new_capacity = array->capacity * VMCI_ARR_CAP_MULT; - size_t new_size = handle_arr_calc_size(new_capacity); + u32 capacity_bump = min(array->max_capacity - array->capacity, + array->capacity); + size_t new_size = handle_arr_calc_size(array->capacity + + capacity_bump); + + if (array->size >= array->max_capacity) + return VMCI_ERROR_NO_MEM; new_array = krealloc(array, new_size, GFP_ATOMIC); if (!new_array) - return; + return VMCI_ERROR_NO_MEM; - new_array->capacity = new_capacity; + new_array->capacity += capacity_bump; *array_ptr = array = new_array; } array->entries[array->size] = handle; array->size++; + + return VMCI_SUCCESS; } /* @@ -66,7 +78,7 @@ struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array, struct vmci_handle entry_handle) { struct vmci_handle handle = VMCI_INVALID_HANDLE; - size_t i; + u32 i; for (i = 0; i < array->size; i++) { if (vmci_handle_is_equal(array->entries[i], entry_handle)) { @@ -101,7 +113,7 @@ struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array) * Handle at given index, VMCI_INVALID_HANDLE if invalid index. */ struct vmci_handle -vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index) +vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index) { if (unlikely(index >= array->size)) return VMCI_INVALID_HANDLE; @@ -112,7 +124,7 @@ vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index) bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array, struct vmci_handle entry_handle) { - size_t i; + u32 i; for (i = 0; i < array->size; i++) if (vmci_handle_is_equal(array->entries[i], entry_handle)) diff --git a/drivers/misc/vmw_vmci/vmci_handle_array.h b/drivers/misc/vmw_vmci/vmci_handle_array.h index bd1559a548e9..96193f85be5b 100644 --- a/drivers/misc/vmw_vmci/vmci_handle_array.h +++ b/drivers/misc/vmw_vmci/vmci_handle_array.h @@ -9,32 +9,41 @@ #define _VMCI_HANDLE_ARRAY_H_ #include <linux/vmw_vmci_defs.h> +#include <linux/limits.h> #include <linux/types.h> -#define VMCI_HANDLE_ARRAY_DEFAULT_SIZE 4 -#define VMCI_ARR_CAP_MULT 2 /* Array capacity multiplier */ - struct vmci_handle_arr { - size_t capacity; - size_t size; + u32 capacity; + u32 max_capacity; + u32 size; + u32 pad; struct vmci_handle entries[]; }; -struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity); +#define VMCI_HANDLE_ARRAY_HEADER_SIZE \ + offsetof(struct vmci_handle_arr, entries) +/* Select a default capacity that results in a 64 byte sized array */ +#define VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY 6 +/* Make sure that the max array size can be expressed by a u32 */ +#define VMCI_HANDLE_ARRAY_MAX_CAPACITY \ + ((U32_MAX - VMCI_HANDLE_ARRAY_HEADER_SIZE - 1) / \ + sizeof(struct vmci_handle)) + +struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity); void vmci_handle_arr_destroy(struct vmci_handle_arr *array); -void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, - struct vmci_handle handle); +int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, + struct vmci_handle handle); struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array, struct vmci_handle entry_handle); struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array); struct vmci_handle -vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index); +vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index); bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array, struct vmci_handle entry_handle); struct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array); -static inline size_t vmci_handle_arr_get_size( +static inline u32 vmci_handle_arr_get_size( const struct vmci_handle_arr *array) { return array->size; diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c new file mode 100644 index 000000000000..f257d3812110 --- /dev/null +++ b/drivers/misc/xilinx_sdfec.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx SDFEC + * + * Copyright (C) 2019 Xilinx, Inc. + * + * Description: + * This driver is developed for SDFEC16 (Soft Decision FEC 16nm) + * IP. It exposes a char device which supports file operations + * like open(), close() and ioctl(). + */ + +#include <linux/miscdevice.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/clk.h> + +#define DEV_NAME_LEN 12 + +static struct idr dev_idr; +static struct mutex dev_idr_lock; + +/** + * struct xsdfec_clks - For managing SD-FEC clocks + * @core_clk: Main processing clock for core + * @axi_clk: AXI4-Lite memory-mapped clock + * @din_words_clk: DIN Words AXI4-Stream Slave clock + * @din_clk: DIN AXI4-Stream Slave clock + * @dout_clk: DOUT Words AXI4-Stream Slave clock + * @dout_words_clk: DOUT AXI4-Stream Slave clock + * @ctrl_clk: Control AXI4-Stream Slave clock + * @status_clk: Status AXI4-Stream Slave clock + */ +struct xsdfec_clks { + struct clk *core_clk; + struct clk *axi_clk; + struct clk *din_words_clk; + struct clk *din_clk; + struct clk *dout_clk; + struct clk *dout_words_clk; + struct clk *ctrl_clk; + struct clk *status_clk; +}; + +/** + * struct xsdfec_dev - Driver data for SDFEC + * @regs: device physical base address + * @dev: pointer to device struct + * @miscdev: Misc device handle + * @error_data_lock: Error counter and states spinlock + * @clks: Clocks managed by the SDFEC driver + * @dev_name: Device name + * @dev_id: Device ID + * + * This structure contains necessary state for SDFEC driver to operate + */ +struct xsdfec_dev { + void __iomem *regs; + struct device *dev; + struct miscdevice miscdev; + /* Spinlock to protect state_updated and stats_updated */ + spinlock_t error_data_lock; + struct xsdfec_clks clks; + char dev_name[DEV_NAME_LEN]; + int dev_id; +}; + +static const struct file_operations xsdfec_fops = { + .owner = THIS_MODULE, +}; + +static int xsdfec_clk_init(struct platform_device *pdev, + struct xsdfec_clks *clks) +{ + int err; + + clks->core_clk = devm_clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(clks->core_clk)) { + dev_err(&pdev->dev, "failed to get core_clk"); + return PTR_ERR(clks->core_clk); + } + + clks->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(clks->axi_clk)) { + dev_err(&pdev->dev, "failed to get axi_clk"); + return PTR_ERR(clks->axi_clk); + } + + clks->din_words_clk = devm_clk_get(&pdev->dev, "s_axis_din_words_aclk"); + if (IS_ERR(clks->din_words_clk)) { + if (PTR_ERR(clks->din_words_clk) != -ENOENT) { + err = PTR_ERR(clks->din_words_clk); + return err; + } + clks->din_words_clk = NULL; + } + + clks->din_clk = devm_clk_get(&pdev->dev, "s_axis_din_aclk"); + if (IS_ERR(clks->din_clk)) { + if (PTR_ERR(clks->din_clk) != -ENOENT) { + err = PTR_ERR(clks->din_clk); + return err; + } + clks->din_clk = NULL; + } + + clks->dout_clk = devm_clk_get(&pdev->dev, "m_axis_dout_aclk"); + if (IS_ERR(clks->dout_clk)) { + if (PTR_ERR(clks->dout_clk) != -ENOENT) { + err = PTR_ERR(clks->dout_clk); + return err; + } + clks->dout_clk = NULL; + } + + clks->dout_words_clk = + devm_clk_get(&pdev->dev, "s_axis_dout_words_aclk"); + if (IS_ERR(clks->dout_words_clk)) { + if (PTR_ERR(clks->dout_words_clk) != -ENOENT) { + err = PTR_ERR(clks->dout_words_clk); + return err; + } + clks->dout_words_clk = NULL; + } + + clks->ctrl_clk = devm_clk_get(&pdev->dev, "s_axis_ctrl_aclk"); + if (IS_ERR(clks->ctrl_clk)) { + if (PTR_ERR(clks->ctrl_clk) != -ENOENT) { + err = PTR_ERR(clks->ctrl_clk); + return err; + } + clks->ctrl_clk = NULL; + } + + clks->status_clk = devm_clk_get(&pdev->dev, "m_axis_status_aclk"); + if (IS_ERR(clks->status_clk)) { + if (PTR_ERR(clks->status_clk) != -ENOENT) { + err = PTR_ERR(clks->status_clk); + return err; + } + clks->status_clk = NULL; + } + + err = clk_prepare_enable(clks->core_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable core_clk (%d)", err); + return err; + } + + err = clk_prepare_enable(clks->axi_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable axi_clk (%d)", err); + goto err_disable_core_clk; + } + + err = clk_prepare_enable(clks->din_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable din_clk (%d)", err); + goto err_disable_axi_clk; + } + + err = clk_prepare_enable(clks->din_words_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable din_words_clk (%d)", err); + goto err_disable_din_clk; + } + + err = clk_prepare_enable(clks->dout_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable dout_clk (%d)", err); + goto err_disable_din_words_clk; + } + + err = clk_prepare_enable(clks->dout_words_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable dout_words_clk (%d)", + err); + goto err_disable_dout_clk; + } + + err = clk_prepare_enable(clks->ctrl_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable ctrl_clk (%d)", err); + goto err_disable_dout_words_clk; + } + + err = clk_prepare_enable(clks->status_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable status_clk (%d)\n", err); + goto err_disable_ctrl_clk; + } + + return err; + +err_disable_ctrl_clk: + clk_disable_unprepare(clks->ctrl_clk); +err_disable_dout_words_clk: + clk_disable_unprepare(clks->dout_words_clk); +err_disable_dout_clk: + clk_disable_unprepare(clks->dout_clk); +err_disable_din_words_clk: + clk_disable_unprepare(clks->din_words_clk); +err_disable_din_clk: + clk_disable_unprepare(clks->din_clk); +err_disable_axi_clk: + clk_disable_unprepare(clks->axi_clk); +err_disable_core_clk: + clk_disable_unprepare(clks->core_clk); + + return err; +} + +static void xsdfec_disable_all_clks(struct xsdfec_clks *clks) +{ + clk_disable_unprepare(clks->status_clk); + clk_disable_unprepare(clks->ctrl_clk); + clk_disable_unprepare(clks->dout_words_clk); + clk_disable_unprepare(clks->dout_clk); + clk_disable_unprepare(clks->din_words_clk); + clk_disable_unprepare(clks->din_clk); + clk_disable_unprepare(clks->core_clk); + clk_disable_unprepare(clks->axi_clk); +} + +static void xsdfec_idr_remove(struct xsdfec_dev *xsdfec) +{ + mutex_lock(&dev_idr_lock); + idr_remove(&dev_idr, xsdfec->dev_id); + mutex_unlock(&dev_idr_lock); +} + +static int xsdfec_probe(struct platform_device *pdev) +{ + struct xsdfec_dev *xsdfec; + struct device *dev; + struct resource *res; + int err; + + xsdfec = devm_kzalloc(&pdev->dev, sizeof(*xsdfec), GFP_KERNEL); + if (!xsdfec) + return -ENOMEM; + + xsdfec->dev = &pdev->dev; + spin_lock_init(&xsdfec->error_data_lock); + + err = xsdfec_clk_init(pdev, &xsdfec->clks); + if (err) + return err; + + dev = xsdfec->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xsdfec->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(xsdfec->regs)) { + err = PTR_ERR(xsdfec->regs); + goto err_xsdfec_dev; + } + + /* Save driver private data */ + platform_set_drvdata(pdev, xsdfec); + + mutex_lock(&dev_idr_lock); + err = idr_alloc(&dev_idr, xsdfec->dev_name, 0, 0, GFP_KERNEL); + mutex_unlock(&dev_idr_lock); + if (err < 0) + goto err_xsdfec_dev; + xsdfec->dev_id = err; + + snprintf(xsdfec->dev_name, DEV_NAME_LEN, "xsdfec%d", xsdfec->dev_id); + xsdfec->miscdev.minor = MISC_DYNAMIC_MINOR; + xsdfec->miscdev.name = xsdfec->dev_name; + xsdfec->miscdev.fops = &xsdfec_fops; + xsdfec->miscdev.parent = dev; + err = misc_register(&xsdfec->miscdev); + if (err) { + dev_err(dev, "error:%d. Unable to register device", err); + goto err_xsdfec_idr; + } + return 0; + +err_xsdfec_idr: + xsdfec_idr_remove(xsdfec); +err_xsdfec_dev: + xsdfec_disable_all_clks(&xsdfec->clks); + return err; +} + +static int xsdfec_remove(struct platform_device *pdev) +{ + struct xsdfec_dev *xsdfec; + + xsdfec = platform_get_drvdata(pdev); + misc_deregister(&xsdfec->miscdev); + xsdfec_idr_remove(xsdfec); + xsdfec_disable_all_clks(&xsdfec->clks); + return 0; +} + +static const struct of_device_id xsdfec_of_match[] = { + { + .compatible = "xlnx,sd-fec-1.1", + }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, xsdfec_of_match); + +static struct platform_driver xsdfec_driver = { + .driver = { + .name = "xilinx-sdfec", + .of_match_table = xsdfec_of_match, + }, + .probe = xsdfec_probe, + .remove = xsdfec_remove, +}; + +static int __init xsdfec_init(void) +{ + int err; + + mutex_init(&dev_idr_lock); + idr_init(&dev_idr); + err = platform_driver_register(&xsdfec_driver); + if (err < 0) { + pr_err("%s Unabled to register SDFEC driver", __func__); + return err; + } + return 0; +} + +static void __exit xsdfec_exit(void) +{ + platform_driver_unregister(&xsdfec_driver); + idr_destroy(&dev_idr); +} + +module_init(xsdfec_init); +module_exit(xsdfec_exit); + +MODULE_AUTHOR("Xilinx, Inc"); +MODULE_DESCRIPTION("Xilinx SD-FEC16 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 7659d6c5f718..e5c571fd232c 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -46,14 +46,14 @@ config MUX_GPIO be called mux-gpio. config MUX_MMIO - tristate "MMIO register bitfield-controlled Multiplexer" - depends on (OF && MFD_SYSCON) || COMPILE_TEST + tristate "MMIO/Regmap register bitfield-controlled Multiplexer" + depends on OF || COMPILE_TEST help - MMIO register bitfield-controlled Multiplexer controller. + MMIO/Regmap register bitfield-controlled Multiplexer controller. - The driver builds multiplexer controllers for bitfields in a syscon - register. For N bit wide bitfields, there will be 2^N possible - multiplexer states. + The driver builds multiplexer controllers for bitfields in either + a syscon register or a driver regmap register. For N bit wide + bitfields, there will be 2^N possible multiplexer states. To compile the driver as a module, choose M here: the module will be called mux-mmio. diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c index 935ac44aa209..44a7a0e885b8 100644 --- a/drivers/mux/mmio.c +++ b/drivers/mux/mmio.c @@ -28,6 +28,7 @@ static const struct mux_control_ops mux_mmio_ops = { static const struct of_device_id mux_mmio_dt_ids[] = { { .compatible = "mmio-mux", }, + { .compatible = "reg-mux", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mux_mmio_dt_ids); @@ -43,7 +44,10 @@ static int mux_mmio_probe(struct platform_device *pdev) int ret; int i; - regmap = syscon_node_to_regmap(np->parent); + if (of_device_is_compatible(np, "mmio-mux")) + regmap = syscon_node_to_regmap(np->parent); + else + regmap = dev_get_regmap(dev->parent, NULL) ?: ERR_PTR(-ENODEV); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); dev_err(dev, "failed to get regmap: %d\n", ret); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index afa4335e0a20..c2ec750cae6e 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -47,6 +47,13 @@ config NVMEM_IMX_OCOTP This driver can also be built as a module. If so, the module will be called nvmem-imx-ocotp. +config NVMEM_IMX_OCOTP_SCU + tristate "i.MX8 SCU On-Chip OTP Controller support" + depends on IMX_SCU + help + This is a driver for the SCU On-Chip OTP Controller (OCOTP) + available on i.MX8 SoCs. + config NVMEM_LPC18XX_EEPROM tristate "NXP LPC18XX EEPROM Memory Support" depends on ARCH_LPC18XX || COMPILE_TEST @@ -188,7 +195,7 @@ config MESON_MX_EFUSE config NVMEM_SNVS_LPGPR tristate "Support for Low Power General Purpose Register" - depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST + depends on ARCH_MXC || COMPILE_TEST help This is a driver for Low Power General Purpose Register (LPGPR) available on i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index c1fe4768dfef..e5c153d99a67 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -16,6 +16,8 @@ obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o nvmem-imx-iim-y := imx-iim.o obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o nvmem-imx-ocotp-y := imx-ocotp.o +obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o +nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c new file mode 100644 index 000000000000..d9dc482ecb2f --- /dev/null +++ b/drivers/nvmem/imx-ocotp-scu.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i.MX8 OCOTP fusebox driver + * + * Copyright 2019 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <linux/firmware/imx/sci.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +enum ocotp_devtype { + IMX8QXP, +}; + +struct ocotp_devtype_data { + int devtype; + int nregs; +}; + +struct ocotp_priv { + struct device *dev; + const struct ocotp_devtype_data *data; + struct imx_sc_ipc *nvmem_ipc; +}; + +struct imx_sc_msg_misc_fuse_read { + struct imx_sc_rpc_msg hdr; + u32 word; +} __packed; + +static struct ocotp_devtype_data imx8qxp_data = { + .devtype = IMX8QXP, + .nregs = 800, +}; + +static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, + u32 *val) +{ + struct imx_sc_msg_misc_fuse_read msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_MISC; + hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ; + hdr->size = 2; + + msg.word = word; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + *val = msg.word; + + return 0; +} + +static int imx_scu_ocotp_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct ocotp_priv *priv = context; + u32 count, index, num_bytes; + u32 *buf; + void *p; + int i, ret; + + index = offset >> 2; + num_bytes = round_up((offset % 4) + bytes, 4); + count = num_bytes >> 2; + + if (count > (priv->data->nregs - index)) + count = priv->data->nregs - index; + + p = kzalloc(num_bytes, GFP_KERNEL); + if (!p) + return -ENOMEM; + + buf = p; + + for (i = index; i < (index + count); i++) { + if (priv->data->devtype == IMX8QXP) { + if ((i > 271) && (i < 544)) { + *buf++ = 0; + continue; + } + } + + ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); + if (ret) { + kfree(p); + return ret; + } + buf++; + } + + memcpy(val, (u8 *)p + offset % 4, bytes); + + kfree(p); + + return 0; +} + +static struct nvmem_config imx_scu_ocotp_nvmem_config = { + .name = "imx-scu-ocotp", + .read_only = true, + .word_size = 4, + .stride = 1, + .owner = THIS_MODULE, + .reg_read = imx_scu_ocotp_read, +}; + +static const struct of_device_id imx_scu_ocotp_dt_ids[] = { + { .compatible = "fsl,imx8qxp-scu-ocotp", (void *)&imx8qxp_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, imx_scu_ocotp_dt_ids); + +static int imx_scu_ocotp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ocotp_priv *priv; + struct nvmem_device *nvmem; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = imx_scu_get_handle(&priv->nvmem_ipc); + if (ret) + return ret; + + priv->data = of_device_get_match_data(dev); + priv->dev = dev; + imx_scu_ocotp_nvmem_config.size = 4 * priv->data->nregs; + imx_scu_ocotp_nvmem_config.dev = dev; + imx_scu_ocotp_nvmem_config.priv = priv; + nvmem = devm_nvmem_register(dev, &imx_scu_ocotp_nvmem_config); + + return PTR_ERR_OR_ZERO(nvmem); +} + +static struct platform_driver imx_scu_ocotp_driver = { + .probe = imx_scu_ocotp_probe, + .driver = { + .name = "imx_scu_ocotp", + .of_match_table = imx_scu_ocotp_dt_ids, + }, +}; +module_platform_driver(imx_scu_ocotp_driver); + +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); +MODULE_DESCRIPTION("i.MX8 SCU OCOTP fuse box driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index bd016b928589..42d4451e7d67 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -39,12 +39,14 @@ #define IMX_OCOTP_ADDR_DATA2 0x0040 #define IMX_OCOTP_ADDR_DATA3 0x0050 -#define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F +#define IMX_OCOTP_BM_CTRL_ADDR 0x000000FF #define IMX_OCOTP_BM_CTRL_BUSY 0x00000100 #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 -#define DEF_RELAX 20 /* > 16.5ns */ +#define TIMING_STROBE_PROG_US 10 /* Min time to blow a fuse */ +#define TIMING_STROBE_READ_NS 37 /* Min time before read */ +#define TIMING_RELAX_NS 17 #define DEF_FSOURCE 1001 /* > 1000 ns */ #define DEF_STROBE_PROG 10000 /* IPG clocks */ #define IMX_OCOTP_WR_UNLOCK 0x3E770000 @@ -176,14 +178,41 @@ static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) * fields with timing values to match the current frequency of the * ipg_clk. OTP writes will work at maximum bus frequencies as long * as the HW_OCOTP_TIMING parameters are set correctly. + * + * Note: there are minimum timings required to ensure an OTP fuse burns + * correctly that are independent of the ipg_clk. Those values are not + * formally documented anywhere however, working from the minimum + * timings given in u-boot we can say: + * + * - Minimum STROBE_PROG time is 10 microseconds. Intuitively 10 + * microseconds feels about right as representative of a minimum time + * to physically burn out a fuse. + * + * - Minimum STROBE_READ i.e. the time to wait post OTP fuse burn before + * performing another read is 37 nanoseconds + * + * - Minimum RELAX timing is 17 nanoseconds. This final RELAX minimum + * timing is not entirely clear the documentation says "This + * count value specifies the time to add to all default timing + * parameters other than the Tpgm and Trd. It is given in number + * of ipg_clk periods." where Tpgm and Trd refer to STROBE_PROG + * and STROBE_READ respectively. What the other timing parameters + * are though, is not specified. Experience shows a zero RELAX + * value will mess up a re-load of the shadow registers post OTP + * burn. */ clk_rate = clk_get_rate(priv->clk); - relax = clk_rate / (1000000000 / DEF_RELAX) - 1; - strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; - strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + relax = DIV_ROUND_UP(clk_rate * TIMING_RELAX_NS, 1000000000) - 1; + strobe_read = DIV_ROUND_UP(clk_rate * TIMING_STROBE_READ_NS, + 1000000000); + strobe_read += 2 * (relax + 1) - 1; + strobe_prog = DIV_ROUND_CLOSEST(clk_rate * TIMING_STROBE_PROG_US, + 1000000); + strobe_prog += 2 * (relax + 1) - 1; - timing = strobe_prog & 0x00000FFF; + timing = readl(priv->base + IMX_OCOTP_ADDR_TIMING) & 0x0FC00000; + timing |= strobe_prog & 0x00000FFF; timing |= (relax << 12) & 0x0000F000; timing |= (strobe_read << 16) & 0x003F0000; @@ -440,8 +469,14 @@ static const struct ocotp_params imx7ulp_params = { static const struct ocotp_params imx8mq_params = { .nregs = 256, - .bank_address_words = 4, - .set_timing = imx_ocotp_set_imx7_timing, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + +static const struct ocotp_params imx8mm_params = { + .nregs = 256, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, }; static const struct of_device_id imx_ocotp_dt_ids[] = { @@ -454,6 +489,7 @@ static const struct of_device_id imx_ocotp_dt_ids[] = { { .compatible = "fsl,imx6sll-ocotp", .data = &imx6sll_params }, { .compatible = "fsl,imx7ulp-ocotp", .data = &imx7ulp_params }, { .compatible = "fsl,imx8mq-ocotp", .data = &imx8mq_params }, + { .compatible = "fsl,imx8mm-ocotp", .data = &imx8mm_params }, { }, }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b7e5cee2aa26..6053d0158b3b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -341,7 +341,7 @@ config HP_ACCEL Support for a led indicating disk protection will be provided as hp::hddprotect. For more information on the feature, refer to - Documentation/misc-devices/lis3lv02d. + Documentation/misc-devices/lis3lv02d.rst. To compile this driver as a module, choose M here: the module will be called hp_accel. diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index b2f07d2043eb..526e3215d8fe 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -98,11 +98,6 @@ static int slim_device_remove(struct device *dev) static int slim_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct slim_device *sbdev = to_slim_device(dev); - int ret; - - ret = of_device_uevent_modalias(dev, env); - if (ret != -ENODEV) - return ret; return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev)); } diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c index ad3e2e23f56e..a444badd8df5 100644 --- a/drivers/slimbus/qcom-ctrl.c +++ b/drivers/slimbus/qcom-ctrl.c @@ -528,10 +528,8 @@ static int qcom_slim_probe(struct platform_device *pdev) slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); ctrl->base = devm_ioremap_resource(ctrl->dev, slim_mem); - if (IS_ERR(ctrl->base)) { - dev_err(&pdev->dev, "IOremap failed\n"); + if (IS_ERR(ctrl->base)) return PTR_ERR(ctrl->base); - } sctrl->set_laddr = qcom_set_laddr; sctrl->xfer_msg = qcom_xfer_msg; diff --git a/drivers/slimbus/stream.c b/drivers/slimbus/stream.c index 2fa05324ed07..75f87b3d8b95 100644 --- a/drivers/slimbus/stream.c +++ b/drivers/slimbus/stream.c @@ -84,7 +84,7 @@ static const int slim_presence_rate_table[] = { 512000, }; -/* +/** * slim_stream_allocate() - Allocate a new SLIMbus Stream * @dev:Slim device to be associated with * @name: name of the stream @@ -189,7 +189,7 @@ static int slim_get_prate_code(int rate) return -EINVAL; } -/* +/** * slim_stream_prepare() - Prepare a SLIMbus Stream * * @rt: instance of slim stream runtime to configure @@ -336,7 +336,7 @@ static int slim_activate_channel(struct slim_stream_runtime *stream, return slim_do_transfer(sdev->ctrl, &txn); } -/* +/** * slim_stream_enable() - Enable a prepared SLIMbus Stream * * @stream: instance of slim stream runtime to enable @@ -389,7 +389,7 @@ int slim_stream_enable(struct slim_stream_runtime *stream) } EXPORT_SYMBOL_GPL(slim_stream_enable); -/* +/** * slim_stream_disable() - Disable a SLIMbus Stream * * @stream: instance of slim stream runtime to disable @@ -423,7 +423,7 @@ int slim_stream_disable(struct slim_stream_runtime *stream) } EXPORT_SYMBOL_GPL(slim_stream_disable); -/* +/** * slim_stream_unprepare() - Un-prepare a SLIMbus Stream * * @stream: instance of slim stream runtime to unprepare @@ -449,7 +449,7 @@ int slim_stream_unprepare(struct slim_stream_runtime *stream) } EXPORT_SYMBOL_GPL(slim_stream_unprepare); -/* +/** * slim_stream_free() - Free a SLIMbus Stream * * @stream: instance of slim stream runtime to free diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index aac35fc3cf22..fe745830a261 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -87,7 +87,7 @@ int sdw_add_bus_master(struct sdw_bus *bus) /* * Initialize clock values based on Master properties. The max - * frequency is read from max_freq property. Current assumption + * frequency is read from max_clk_freq property. Current assumption * is that the bus will start at highest clock frequency when * powered on. * @@ -95,7 +95,7 @@ int sdw_add_bus_master(struct sdw_bus *bus) * to start with bank 0 (Table 40 of Spec) */ prop = &bus->prop; - bus->params.max_dr_freq = prop->max_freq * SDW_DOUBLE_RATE_FACTOR; + bus->params.max_dr_freq = prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; bus->params.curr_dr_freq = bus->params.max_dr_freq; bus->params.curr_bank = SDW_BANK0; bus->params.next_bank = SDW_BANK1; @@ -648,7 +648,7 @@ static int sdw_initialize_slave(struct sdw_slave *slave) return 0; /* Enable DP0 interrupts */ - val = prop->dp0_prop->device_interrupts; + val = prop->dp0_prop->imp_def_interrupts; val |= SDW_DP0_INT_PORT_READY | SDW_DP0_INT_BRA_FAILURE; ret = sdw_update(slave, SDW_DP0_INTMASK, val, val); diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 682789bb8ab3..ff4badc9b3de 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/soundwire/sdw_registers.h> @@ -236,19 +237,19 @@ cdns_fill_msg_resp(struct sdw_cdns *cdns, for (i = 0; i < count; i++) { if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { no_ack = 1; - dev_dbg(cdns->dev, "Msg Ack not received\n"); + dev_dbg_ratelimited(cdns->dev, "Msg Ack not received\n"); if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { nack = 1; - dev_err(cdns->dev, "Msg NACK received\n"); + dev_err_ratelimited(cdns->dev, "Msg NACK received\n"); } } } if (nack) { - dev_err(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); + dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } else if (no_ack) { - dev_dbg(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); + dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } @@ -356,12 +357,12 @@ cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg) /* For NACK, NO ack, don't return err if we are in Broadcast mode */ if (nack) { - dev_err(cdns->dev, - "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); + dev_err_ratelimited(cdns->dev, + "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } else if (no_ack) { - dev_dbg(cdns->dev, - "SCP_addrpage ignored for Slave %d\n", msg->dev_num); + dev_dbg_ratelimited(cdns->dev, + "SCP_addrpage ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } @@ -486,7 +487,8 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, { enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; bool is_slave = false; - u64 slave, mask; + u64 slave; + u32 mask; int i, set_status; /* combine the two status */ @@ -524,9 +526,9 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, /* first check if Slave reported multiple status */ if (set_status > 1) { - dev_warn(cdns->dev, - "Slave reported multiple Status: %d\n", - status[i]); + dev_warn_ratelimited(cdns->dev, + "Slave reported multiple Status: %d\n", + mask); /* * TODO: we need to reread the status here by * issuing a PING cmd @@ -612,7 +614,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id) struct sdw_cdns *cdns = dev_id; u32 slave0, slave1; - dev_dbg(cdns->dev, "Slave status change\n"); + dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); @@ -716,6 +718,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, stream = &cdns->pcm; /* First two PDIs are reserved for bulk transfers */ + if (stream->num_bd < CDNS_PCM_PDI_OFFSET) + return -EINVAL; stream->num_bd -= CDNS_PCM_PDI_OFFSET; offset = CDNS_PCM_PDI_OFFSET; diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 60293a00a14e..317873bc0555 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -263,6 +263,9 @@ static void intel_pdi_init(struct sdw_intel *sdw, config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >> SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS); + dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", + config->pcm_bd, config->pcm_in, config->pcm_out); + /* PDM Stream Capability */ pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); @@ -272,6 +275,9 @@ static void intel_pdi_init(struct sdw_intel *sdw, SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS); config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >> SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS); + + dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n", + config->pdm_bd, config->pdm_in, config->pdm_out); } static int @@ -796,13 +802,14 @@ static int intel_prop_read(struct sdw_bus *bus) sdw_master_read_prop(bus); /* BIOS is not giving some values correctly. So, lets override them */ - bus->prop.num_freq = 1; - bus->prop.freq = devm_kcalloc(bus->dev, bus->prop.num_freq, - sizeof(*bus->prop.freq), GFP_KERNEL); - if (!bus->prop.freq) + bus->prop.num_clk_freq = 1; + bus->prop.clk_freq = devm_kcalloc(bus->dev, bus->prop.num_clk_freq, + sizeof(*bus->prop.clk_freq), + GFP_KERNEL); + if (!bus->prop.clk_freq) return -ENOMEM; - bus->prop.freq[0] = bus->prop.max_freq; + bus->prop.clk_freq[0] = bus->prop.max_clk_freq; bus->prop.err_threshold = 5; return 0; diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 71050e5f643d..d923b6262330 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -5,7 +5,7 @@ #define __SDW_INTEL_LOCAL_H /** - * struct sdw_intel_res - Soundwire link resources + * struct sdw_intel_link_res - Soundwire link resources * @registers: Link IO registers base * @shim: Audio shim pointer * @alh: ALH (Audio Link Hub) pointer diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d3d6b54c5791..70637a0383d2 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -14,6 +14,7 @@ #include <linux/soundwire/sdw_intel.h> #include "intel.h" +#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ #define SDW_MAX_LINKS 4 #define SDW_SHIM_LCAP 0x0 #define SDW_SHIM_BASE 0x2C000 @@ -80,6 +81,7 @@ static struct sdw_intel_ctx /* Check SNDWLCAP.LCOUNT */ caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); + caps &= GENMASK(2, 0); /* Check HW supported vs property value and use min of two */ count = min_t(u8, caps, count); @@ -89,6 +91,9 @@ static struct sdw_intel_ctx dev_err(&adev->dev, "Link count %d exceeds max %d\n", count, SDW_MAX_LINKS); return NULL; + } else if (!count) { + dev_warn(&adev->dev, "No SoundWire links detected\n"); + return NULL; } dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); @@ -150,6 +155,12 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, { struct sdw_intel_res *res = cdata; struct acpi_device *adev; + acpi_status status; + u64 adr; + + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) + return AE_OK; /* keep going */ if (acpi_bus_get_device(handle, &adev)) { pr_err("%s: Couldn't find ACPI handle\n", __func__); @@ -157,7 +168,19 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, } res->handle = handle; - return AE_OK; + + /* + * On some Intel platforms, multiple children of the HDAS + * device can be found, but only one of them is the SoundWire + * controller. The SNDW device is always exposed with + * Name(_ADR, 0x40000000), with bits 31..28 representing the + * SoundWire link so filter accordingly + */ + if ((adr & GENMASK(31, 28)) >> 28 != SDW_LINK_TYPE) + return AE_OK; /* keep going */ + + /* device found, stop namespace walk */ + return AE_CTRL_TERMINATE; } /** diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index c1f51d6a23d2..79fee1b21ab6 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -40,7 +40,7 @@ int sdw_master_read_prop(struct sdw_bus *bus) /* Find master handle */ snprintf(name, sizeof(name), - "mipi-sdw-master-%d-subproperties", bus->link_id); + "mipi-sdw-link-%d-subproperties", bus->link_id); link = device_get_named_child_node(bus->dev, name); if (!link) { @@ -50,39 +50,40 @@ int sdw_master_read_prop(struct sdw_bus *bus) if (fwnode_property_read_bool(link, "mipi-sdw-clock-stop-mode0-supported")) - prop->clk_stop_mode = SDW_CLK_STOP_MODE0; + prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE0); if (fwnode_property_read_bool(link, "mipi-sdw-clock-stop-mode1-supported")) - prop->clk_stop_mode |= SDW_CLK_STOP_MODE1; + prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE1); fwnode_property_read_u32(link, "mipi-sdw-max-clock-frequency", - &prop->max_freq); + &prop->max_clk_freq); nval = fwnode_property_read_u32_array(link, "mipi-sdw-clock-frequencies-supported", NULL, 0); if (nval > 0) { - prop->num_freq = nval; - prop->freq = devm_kcalloc(bus->dev, prop->num_freq, - sizeof(*prop->freq), GFP_KERNEL); - if (!prop->freq) + prop->num_clk_freq = nval; + prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq, + sizeof(*prop->clk_freq), + GFP_KERNEL); + if (!prop->clk_freq) return -ENOMEM; fwnode_property_read_u32_array(link, "mipi-sdw-clock-frequencies-supported", - prop->freq, prop->num_freq); + prop->clk_freq, prop->num_clk_freq); } /* * Check the frequencies supported. If FW doesn't provide max * freq, then populate here by checking values. */ - if (!prop->max_freq && prop->freq) { - prop->max_freq = prop->freq[0]; - for (i = 1; i < prop->num_freq; i++) { - if (prop->freq[i] > prop->max_freq) - prop->max_freq = prop->freq[i]; + if (!prop->max_clk_freq && prop->clk_freq) { + prop->max_clk_freq = prop->clk_freq[0]; + for (i = 1; i < prop->num_clk_freq; i++) { + if (prop->clk_freq[i] > prop->max_clk_freq) + prop->max_clk_freq = prop->clk_freq[i]; } } @@ -149,13 +150,13 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave, dp0->words, dp0->num_words); } - dp0->flow_controlled = fwnode_property_read_bool(port, + dp0->BRA_flow_controlled = fwnode_property_read_bool(port, "mipi-sdw-bra-flow-controlled"); dp0->simple_ch_prep_sm = fwnode_property_read_bool(port, "mipi-sdw-simplified-channel-prepare-sm"); - dp0->device_interrupts = fwnode_property_read_bool(port, + dp0->imp_def_interrupts = fwnode_property_read_bool(port, "mipi-sdw-imp-def-dp0-interrupts-supported"); return 0; @@ -224,7 +225,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, fwnode_property_read_u32(node, "mipi-sdw-imp-def-dpn-interrupts-supported", - &dpn[i].device_interrupts); + &dpn[i].imp_def_interrupts); fwnode_property_read_u32(node, "mipi-sdw-min-channel-number", &dpn[i].min_ch); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 1d5294b8783b..a0476755a459 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -439,7 +439,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, prep_ch.bank = bus->params.next_bank; - if (dpn_prop->device_interrupts || !dpn_prop->simple_ch_prep_sm) + if (dpn_prop->imp_def_interrupts || !dpn_prop->simple_ch_prep_sm) intr = true; /* @@ -449,7 +449,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, */ if (prep && intr) { ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, - dpn_prop->device_interrupts); + dpn_prop->imp_def_interrupts); if (ret < 0) return ret; } @@ -493,7 +493,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, /* Disable interrupt after Port de-prepare */ if (!prep && intr) ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, - dpn_prop->device_interrupts); + dpn_prop->imp_def_interrupts); return ret; } @@ -1473,7 +1473,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) memcpy(¶ms, &bus->params, sizeof(params)); /* TODO: Support Asynchronous mode */ - if ((prop->max_freq % stream->params.rate) != 0) { + if ((prop->max_clk_freq % stream->params.rate) != 0) { dev_err(bus->dev, "Async mode not supported\n"); return -EINVAL; } diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index 5ae74d5545e6..f1fb18afbcea 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -22,12 +22,17 @@ #define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 #define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A #define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA +#define W1_F3A_INVALID_PIO_STATE 0xFF static ssize_t state_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); + unsigned int retries = W1_F3A_RETRIES; + ssize_t bytes_read = -EIO; + u8 state; + dev_dbg(&sl->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", bin_attr->attr.name, kobj, (unsigned int)off, count, buf); @@ -40,22 +45,37 @@ static ssize_t state_read(struct file *filp, struct kobject *kobj, mutex_lock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex locked"); - if (w1_reset_select_slave(sl)) { - mutex_unlock(&sl->master->bus_mutex); - return -EIO; - } +next: + if (w1_reset_select_slave(sl)) + goto out; + + while (retries--) { + w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); + + state = w1_read_8(sl->master); + if ((state & 0x0F) == ((~state >> 4) & 0x0F)) { + /* complement is correct */ + *buf = state; + bytes_read = 1; + goto out; + } else if (state == W1_F3A_INVALID_PIO_STATE) { + /* slave didn't respond, try to select it again */ + dev_warn(&sl->dev, "slave device did not respond to PIO_ACCESS_READ, " \ + "reselecting, retries left: %d\n", retries); + goto next; + } - w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); - *buf = w1_read_8(sl->master); + if (w1_reset_resume_command(sl->master)) + goto out; /* unrecoverable error */ - mutex_unlock(&sl->master->bus_mutex); - dev_dbg(&sl->dev, "mutex unlocked"); + dev_warn(&sl->dev, "PIO_ACCESS_READ error, retries left: %d\n", retries); + } - /* check for correct complement */ - if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) - return -EIO; - else - return 1; +out: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n", + (bytes_read > 0) ? "succeeded" : "error", retries); + return bytes_read; } static BIN_ATTR_RO(state, 1); @@ -67,6 +87,7 @@ static ssize_t output_write(struct file *filp, struct kobject *kobj, struct w1_slave *sl = kobj_to_w1_slave(kobj); u8 w1_buf[3]; unsigned int retries = W1_F3A_RETRIES; + ssize_t bytes_written = -EIO; if (count != 1 || off != 0) return -EFAULT; @@ -76,7 +97,7 @@ static ssize_t output_write(struct file *filp, struct kobject *kobj, dev_dbg(&sl->dev, "mutex locked"); if (w1_reset_select_slave(sl)) - goto error; + goto out; /* according to the DS2413 datasheet the most significant 6 bits should be set to "1"s, so do it now */ @@ -89,18 +110,20 @@ static ssize_t output_write(struct file *filp, struct kobject *kobj, w1_write_block(sl->master, w1_buf, 3); if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { - mutex_unlock(&sl->master->bus_mutex); - dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); - return 1; + bytes_written = 1; + goto out; } if (w1_reset_resume_command(sl->master)) - goto error; + goto out; /* unrecoverable error */ + + dev_warn(&sl->dev, "PIO_ACCESS_WRITE error, retries left: %d\n", retries); } -error: +out: mutex_unlock(&sl->master->bus_mutex); - dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); - return -EIO; + dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n", + (bytes_written > 0) ? "succeeded" : "error", retries); + return bytes_written; } static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1); diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c index ee1ec9867a78..ccb753a474b1 100644 --- a/drivers/w1/slaves/w1_ds2805.c +++ b/drivers/w1/slaves/w1_ds2805.c @@ -286,7 +286,7 @@ static struct w1_family_ops w1_f0d_fops = { .remove_slave = w1_f0d_remove_slave, }; -static struct w1_family w1_family_2d = { +static struct w1_family w1_family_0d = { .fid = W1_EEPROM_DS2805, .fops = &w1_f0d_fops, }; @@ -294,13 +294,13 @@ static struct w1_family w1_family_2d = { static int __init w1_f0d_init(void) { pr_info("%s()\n", __func__); - return w1_register_family(&w1_family_2d); + return w1_register_family(&w1_family_0d); } static void __exit w1_f0d_fini(void) { pr_info("%s()\n", __func__); - w1_unregister_family(&w1_family_2d); + w1_unregister_family(&w1_family_0d); } module_init(w1_f0d_init); diff --git a/fs/char_dev.c b/fs/char_dev.c index d18cad28c1c3..00dfe17871ac 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -98,7 +98,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { struct char_device_struct *cd, *curr, *prev = NULL; - int ret = -EBUSY; + int ret; int i; if (major >= CHRDEV_MAJOR_MAX) { @@ -129,6 +129,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, major = ret; } + ret = -EBUSY; i = major_to_index(major); for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) { if (curr->major < major) diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h index f31521dcb09a..338aa27e4773 100644 --- a/include/linux/balloon_compaction.h +++ b/include/linux/balloon_compaction.h @@ -64,6 +64,10 @@ extern struct page *balloon_page_alloc(void); extern void balloon_page_enqueue(struct balloon_dev_info *b_dev_info, struct page *page); extern struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info); +extern size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info, + struct list_head *pages); +extern size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info, + struct list_head *pages, size_t n_req_pages); static inline void balloon_devinfo_init(struct balloon_dev_info *balloon) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 62a520df8add..a2b68823717b 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -91,15 +91,11 @@ union coresight_dev_subtype { /** * struct coresight_platform_data - data harvested from the DT specification - * @cpu: the CPU a source belongs to. Only applicable for ETM/PTMs. - * @name: name of the component as shown under sysfs. * @nr_inport: number of input ports for this component. * @nr_outport: number of output ports for this component. * @conns: Array of nr_outport connections from this component */ struct coresight_platform_data { - int cpu; - const char *name; int nr_inport; int nr_outport; struct coresight_connection *conns; @@ -110,11 +106,12 @@ struct coresight_platform_data { * @type: as defined by @coresight_dev_type. * @subtype: as defined by @coresight_dev_subtype. * @ops: generic operations for this component, as defined - by @coresight_ops. + * by @coresight_ops. * @pdata: platform data collected from DT. * @dev: The device entity associated to this component. * @groups: operations specific to this component. These will end up - in the component's sysfs sub-directory. + * in the component's sysfs sub-directory. + * @name: name for the coresight device, also shown under sysfs. */ struct coresight_desc { enum coresight_dev_type type; @@ -123,28 +120,27 @@ struct coresight_desc { struct coresight_platform_data *pdata; struct device *dev; const struct attribute_group **groups; + const char *name; }; /** * struct coresight_connection - representation of a single connection * @outport: a connection's output port number. - * @chid_name: remote component's name. * @child_port: remote component's port number @output is connected to. + * @chid_fwnode: remote component's fwnode handle. * @child_dev: a @coresight_device representation of the component connected to @outport. */ struct coresight_connection { int outport; - const char *child_name; int child_port; + struct fwnode_handle *child_fwnode; struct coresight_device *child_dev; }; /** * struct coresight_device - representation of a device as used by the framework - * @conns: array of coresight_connections associated to this component. - * @nr_inport: number of input port associated to this component. - * @nr_outport: number of output port associated to this component. + * @pdata: Platform data with device connections associated to this device. * @type: as defined by @coresight_dev_type. * @subtype: as defined by @coresight_dev_subtype. * @ops: generic operations for this component, as defined @@ -159,9 +155,7 @@ struct coresight_connection { * @ea: Device attribute for sink representation under PMU directory. */ struct coresight_device { - struct coresight_connection *conns; - int nr_inport; - int nr_outport; + struct coresight_platform_data *pdata; enum coresight_dev_type type; union coresight_dev_subtype subtype; const struct coresight_ops *ops; @@ -174,6 +168,28 @@ struct coresight_device { struct dev_ext_attribute *ea; }; +/* + * coresight_dev_list - Mapping for devices to "name" index for device + * names. + * + * @nr_idx: Number of entries already allocated. + * @pfx: Prefix pattern for device name. + * @fwnode_list: Array of fwnode_handles associated with each allocated + * index, upto nr_idx entries. + */ +struct coresight_dev_list { + int nr_idx; + const char *pfx; + struct fwnode_handle **fwnode_list; +}; + +#define DEFINE_CORESIGHT_DEVLIST(var, dev_pfx) \ +static struct coresight_dev_list (var) = { \ + .pfx = dev_pfx, \ + .nr_idx = 0, \ + .fwnode_list = NULL, \ +} + #define to_coresight_device(d) container_of(d, struct coresight_device, dev) #define source_ops(csdev) csdev->ops->source_ops @@ -267,7 +283,8 @@ extern int coresight_claim_device_unlocked(void __iomem *base); extern void coresight_disclaim_device(void __iomem *base); extern void coresight_disclaim_device_unlocked(void __iomem *base); - +extern char *coresight_alloc_device_name(struct coresight_dev_list *devs, + struct device *dev); #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } @@ -292,16 +309,8 @@ static inline void coresight_disclaim_device_unlocked(void __iomem *base) {} #endif -#ifdef CONFIG_OF -extern int of_coresight_get_cpu(const struct device_node *node); -extern struct coresight_platform_data * -of_get_coresight_platform_data(struct device *dev, - const struct device_node *node); -#else -static inline int of_coresight_get_cpu(const struct device_node *node) -{ return 0; } -static inline struct coresight_platform_data *of_get_coresight_platform_data( - struct device *dev, const struct device_node *node) { return NULL; } -#endif +extern int coresight_get_cpu(struct device *dev); + +struct coresight_platform_data *coresight_get_platform_data(struct device *dev); #endif diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 1262ea6a1f4b..778abbbc7d94 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -46,7 +46,6 @@ #define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U #define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U #define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U -#define ZYNQMP_PM_CAPABILITY_POWER 0x8U /* * Firmware FPGA Manager flags diff --git a/include/linux/platform_data/fsa9480.h b/include/linux/platform_data/fsa9480.h deleted file mode 100644 index dea8d84448ec..000000000000 --- a/include/linux/platform_data/fsa9480.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2010 Samsung Electronics - * Minkyu Kang <mk7.kang@samsung.com> - */ - -#ifndef _FSA9480_H_ -#define _FSA9480_H_ - -#define FSA9480_ATTACHED 1 -#define FSA9480_DETACHED 0 - -struct fsa9480_platform_data { - void (*cfg_gpio) (void); - void (*usb_cb) (u8 attached); - void (*uart_cb) (u8 attached); - void (*charger_cb) (u8 attached); - void (*jig_cb) (u8 attached); - void (*reset_cb) (void); - void (*usb_power) (u8 on); - int wakeup; -}; - -#endif /* _FSA9480_H_ */ diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 35662d9c2c62..bea46bd8b6ce 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -41,6 +41,31 @@ struct sdw_slave; #define SDW_DAI_ID_RANGE_START 100 #define SDW_DAI_ID_RANGE_END 200 +enum { + SDW_PORT_DIRN_SINK = 0, + SDW_PORT_DIRN_SOURCE, + SDW_PORT_DIRN_MAX, +}; + +/* + * constants for flow control, ports and transport + * + * these are bit masks as devices can have multiple capabilities + */ + +/* + * flow modes for SDW port. These can be isochronous, tx controlled, + * rx controlled or async + */ +#define SDW_PORT_FLOW_MODE_ISOCH 0 +#define SDW_PORT_FLOW_MODE_TX_CNTRL BIT(0) +#define SDW_PORT_FLOW_MODE_RX_CNTRL BIT(1) +#define SDW_PORT_FLOW_MODE_ASYNC GENMASK(1, 0) + +/* sample packaging for block. It can be per port or per channel */ +#define SDW_BLOCK_PACKG_PER_PORT BIT(0) +#define SDW_BLOCK_PACKG_PER_CH BIT(1) + /** * enum sdw_slave_status - Slave status * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus. @@ -76,6 +101,14 @@ enum sdw_command_response { SDW_CMD_FAIL_OTHER = 4, }; +/* block group count enum */ +enum sdw_dpn_grouping { + SDW_BLK_GRP_CNT_1 = 0, + SDW_BLK_GRP_CNT_2 = 1, + SDW_BLK_GRP_CNT_3 = 2, + SDW_BLK_GRP_CNT_4 = 3, +}; + /** * enum sdw_stream_type: data stream type * @@ -100,6 +133,26 @@ enum sdw_data_direction { SDW_DATA_DIR_TX = 1, }; +/** + * enum sdw_port_data_mode: Data Port mode + * + * @SDW_PORT_DATA_MODE_NORMAL: Normal data mode where audio data is received + * and transmitted. + * @SDW_PORT_DATA_MODE_STATIC_1: Simple test mode which uses static value of + * logic 1. The encoding will result in signal transitions at every bitslot + * owned by this Port + * @SDW_PORT_DATA_MODE_STATIC_0: Simple test mode which uses static value of + * logic 0. The encoding will result in no signal transitions + * @SDW_PORT_DATA_MODE_PRBS: Test mode which uses a PRBS generator to produce + * a pseudo random data pattern that is transferred + */ +enum sdw_port_data_mode { + SDW_PORT_DATA_MODE_NORMAL = 0, + SDW_PORT_DATA_MODE_STATIC_1 = 1, + SDW_PORT_DATA_MODE_STATIC_0 = 2, + SDW_PORT_DATA_MODE_PRBS = 3, +}; + /* * SDW properties, defined in MIPI DisCo spec v1.0 */ @@ -153,10 +206,11 @@ enum sdw_clk_stop_mode { * (inclusive) * @num_words: number of wordlengths supported * @words: wordlengths supported - * @flow_controlled: Slave implementation results in an OK_NotReady + * @BRA_flow_controlled: Slave implementation results in an OK_NotReady * response * @simple_ch_prep_sm: If channel prepare sequence is required - * @device_interrupts: If implementation-defined interrupts are supported + * @imp_def_interrupts: If set, each bit corresponds to support for + * implementation-defined interrupts * * The wordlengths are specified by Spec as max, min AND number of * discrete values, implementation can define based on the wordlengths they @@ -167,9 +221,9 @@ struct sdw_dp0_prop { u32 min_word; u32 num_words; u32 *words; - bool flow_controlled; + bool BRA_flow_controlled; bool simple_ch_prep_sm; - bool device_interrupts; + bool imp_def_interrupts; }; /** @@ -219,7 +273,7 @@ struct sdw_dpn_audio_mode { * @simple_ch_prep_sm: If the port supports simplified channel prepare state * machine * @ch_prep_timeout: Port-specific timeout value, in milliseconds - * @device_interrupts: If set, each bit corresponds to support for + * @imp_def_interrupts: If set, each bit corresponds to support for * implementation-defined interrupts * @max_ch: Maximum channels supported * @min_ch: Minimum channels supported @@ -244,7 +298,7 @@ struct sdw_dpn_prop { u32 max_grouping; bool simple_ch_prep_sm; u32 ch_prep_timeout; - u32 device_interrupts; + u32 imp_def_interrupts; u32 max_ch; u32 min_ch; u32 num_ch; @@ -311,36 +365,32 @@ struct sdw_slave_prop { /** * struct sdw_master_prop - Master properties * @revision: MIPI spec version of the implementation - * @master_count: Number of masters - * @clk_stop_mode: Bitmap for Clock Stop modes supported - * @max_freq: Maximum Bus clock frequency, in Hz + * @clk_stop_modes: Bitmap, bit N set when clock-stop-modeN supported + * @max_clk_freq: Maximum Bus clock frequency, in Hz * @num_clk_gears: Number of clock gears supported * @clk_gears: Clock gears supported - * @num_freq: Number of clock frequencies supported, in Hz - * @freq: Clock frequencies supported, in Hz + * @num_clk_freq: Number of clock frequencies supported, in Hz + * @clk_freq: Clock frequencies supported, in Hz * @default_frame_rate: Controller default Frame rate, in Hz * @default_row: Number of rows * @default_col: Number of columns - * @dynamic_frame: Dynamic frame supported + * @dynamic_frame: Dynamic frame shape supported * @err_threshold: Number of times that software may retry sending a single * command - * @dpn_prop: Data Port N properties */ struct sdw_master_prop { u32 revision; - u32 master_count; - enum sdw_clk_stop_mode clk_stop_mode; - u32 max_freq; + u32 clk_stop_modes; + u32 max_clk_freq; u32 num_clk_gears; u32 *clk_gears; - u32 num_freq; - u32 *freq; + u32 num_clk_freq; + u32 *clk_freq; u32 default_frame_rate; u32 default_row; u32 default_col; bool dynamic_frame; u32 err_threshold; - struct sdw_dpn_prop *dpn_prop; }; int sdw_master_read_prop(struct sdw_bus *bus); diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index 9c756b5a0dfe..aaa7f4267c14 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -16,4 +16,15 @@ void sdw_unregister_driver(struct sdw_driver *drv); int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); +/** + * module_sdw_driver() - Helper macro for registering a Soundwire driver + * @__sdw_driver: soundwire slave driver struct + * + * Helper macro for Soundwire drivers which do not do anything special in + * module init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_sdw_driver(__sdw_driver) \ + module_driver(__sdw_driver, sdw_register_driver, \ + sdw_unregister_driver) #endif /* __SOUNDWIRE_TYPES_H */ diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index 77ac9c7b9483..fefb5292403b 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -62,9 +62,18 @@ enum { /* * A single VMCI device has an upper limit of 128MB on the amount of - * memory that can be used for queue pairs. + * memory that can be used for queue pairs. Since each queue pair + * consists of at least two pages, the memory limit also dictates the + * number of queue pairs a guest can create. */ #define VMCI_MAX_GUEST_QP_MEMORY (128 * 1024 * 1024) +#define VMCI_MAX_GUEST_QP_COUNT (VMCI_MAX_GUEST_QP_MEMORY / PAGE_SIZE / 2) + +/* + * There can be at most PAGE_SIZE doorbells since there is one doorbell + * per byte in the doorbell bitmap page. + */ +#define VMCI_MAX_GUEST_DOORBELL_COUNT PAGE_SIZE /* * Queues with pre-mapped data pages must be small, so that we don't pin @@ -430,8 +439,8 @@ enum { struct vmci_queue_header { /* All fields are 64bit and aligned. */ struct vmci_handle handle; /* Identifier. */ - atomic64_t producer_tail; /* Offset in this queue. */ - atomic64_t consumer_head; /* Offset in peer queue. */ + u64 producer_tail; /* Offset in this queue. */ + u64 consumer_head; /* Offset in peer queue. */ }; /* @@ -732,13 +741,9 @@ static inline void *vmci_event_data_payload(struct vmci_event_data *ev_data) * prefix will be used, so correctness isn't an issue, but using a * 64bit operation still adds unnecessary overhead. */ -static inline u64 vmci_q_read_pointer(atomic64_t *var) +static inline u64 vmci_q_read_pointer(u64 *var) { -#if defined(CONFIG_X86_32) - return atomic_read((atomic_t *)var); -#else - return atomic64_read(var); -#endif + return READ_ONCE(*(unsigned long *)var); } /* @@ -747,23 +752,17 @@ static inline u64 vmci_q_read_pointer(atomic64_t *var) * never exceeds a 32bit value in this case. On 32bit SMP, using a * locked cmpxchg8b adds unnecessary overhead. */ -static inline void vmci_q_set_pointer(atomic64_t *var, - u64 new_val) +static inline void vmci_q_set_pointer(u64 *var, u64 new_val) { -#if defined(CONFIG_X86_32) - return atomic_set((atomic_t *)var, (u32)new_val); -#else - return atomic64_set(var, new_val); -#endif + /* XXX buggered on big-endian */ + WRITE_ONCE(*(unsigned long *)var, (unsigned long)new_val); } /* * Helper to add a given offset to a head or tail pointer. Wraps the * value of the pointer around the max size of the queue. */ -static inline void vmci_qp_add_pointer(atomic64_t *var, - size_t add, - u64 size) +static inline void vmci_qp_add_pointer(u64 *var, size_t add, u64 size) { u64 new_val = vmci_q_read_pointer(var); @@ -840,8 +839,8 @@ static inline void vmci_q_header_init(struct vmci_queue_header *q_header, const struct vmci_handle handle) { q_header->handle = handle; - atomic64_set(&q_header->producer_tail, 0); - atomic64_set(&q_header->consumer_head, 0); + q_header->producer_tail = 0; + q_header->consumer_head = 0; } /* diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 204ab9b4ae67..3956c226ca35 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -45,6 +45,30 @@ enum goya_queue_id { GOYA_QUEUE_ID_SIZE }; +/* + * Engine Numbering + * + * Used in the "busy_engines_mask" field in `struct hl_info_hw_idle' + */ + +enum goya_engine_id { + GOYA_ENGINE_ID_DMA_0 = 0, + GOYA_ENGINE_ID_DMA_1, + GOYA_ENGINE_ID_DMA_2, + GOYA_ENGINE_ID_DMA_3, + GOYA_ENGINE_ID_DMA_4, + GOYA_ENGINE_ID_MME_0, + GOYA_ENGINE_ID_TPC_0, + GOYA_ENGINE_ID_TPC_1, + GOYA_ENGINE_ID_TPC_2, + GOYA_ENGINE_ID_TPC_3, + GOYA_ENGINE_ID_TPC_4, + GOYA_ENGINE_ID_TPC_5, + GOYA_ENGINE_ID_TPC_6, + GOYA_ENGINE_ID_TPC_7, + GOYA_ENGINE_ID_SIZE +}; + enum hl_device_status { HL_DEVICE_STATUS_OPERATIONAL, HL_DEVICE_STATUS_IN_RESET, @@ -86,7 +110,11 @@ struct hl_info_dram_usage { struct hl_info_hw_idle { __u32 is_idle; - __u32 pad; + /* + * Bitmask of busy engines. + * Bits definition is according to `enum <chip>_enging_id'. + */ + __u32 busy_engines_mask; }; struct hl_info_device_status { diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index 9969358a7af5..e7258d8c252b 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -20,56 +20,42 @@ #endif #include <linux/font.h> -#define NO_FONTS - static const struct font_desc *fonts[] = { #ifdef CONFIG_FONT_8x8 -#undef NO_FONTS - &font_vga_8x8, + &font_vga_8x8, #endif #ifdef CONFIG_FONT_8x16 -#undef NO_FONTS - &font_vga_8x16, + &font_vga_8x16, #endif #ifdef CONFIG_FONT_6x11 -#undef NO_FONTS - &font_vga_6x11, + &font_vga_6x11, #endif #ifdef CONFIG_FONT_7x14 -#undef NO_FONTS - &font_7x14, + &font_7x14, #endif #ifdef CONFIG_FONT_SUN8x16 -#undef NO_FONTS - &font_sun_8x16, + &font_sun_8x16, #endif #ifdef CONFIG_FONT_SUN12x22 -#undef NO_FONTS - &font_sun_12x22, + &font_sun_12x22, #endif #ifdef CONFIG_FONT_10x18 -#undef NO_FONTS - &font_10x18, + &font_10x18, #endif #ifdef CONFIG_FONT_ACORN_8x8 -#undef NO_FONTS - &font_acorn_8x8, + &font_acorn_8x8, #endif #ifdef CONFIG_FONT_PEARL_8x8 -#undef NO_FONTS - &font_pearl_8x8, + &font_pearl_8x8, #endif #ifdef CONFIG_FONT_MINI_4x6 -#undef NO_FONTS - &font_mini_4x6, + &font_mini_4x6, #endif #ifdef CONFIG_FONT_6x10 -#undef NO_FONTS - &font_6x10, + &font_6x10, #endif #ifdef CONFIG_FONT_TER16x32 -#undef NO_FONTS - &font_ter_16x32, + &font_ter_16x32, #endif }; @@ -90,16 +76,17 @@ static const struct font_desc *fonts[] = { * specified font. * */ - const struct font_desc *find_font(const char *name) { - unsigned int i; + unsigned int i; - for (i = 0; i < num_fonts; i++) - if (!strcmp(fonts[i]->name, name)) - return fonts[i]; - return NULL; + BUILD_BUG_ON(!num_fonts); + for (i = 0; i < num_fonts; i++) + if (!strcmp(fonts[i]->name, name)) + return fonts[i]; + return NULL; } +EXPORT_SYMBOL(find_font); /** @@ -116,44 +103,46 @@ const struct font_desc *find_font(const char *name) * chosen font. * */ - const struct font_desc *get_default_font(int xres, int yres, u32 font_w, u32 font_h) { - int i, c, cc; - const struct font_desc *f, *g; - - g = NULL; - cc = -10000; - for(i=0; i<num_fonts; i++) { - f = fonts[i]; - c = f->pref; + int i, c, cc, res; + const struct font_desc *f, *g; + + g = NULL; + cc = -10000; + for (i = 0; i < num_fonts; i++) { + f = fonts[i]; + c = f->pref; #if defined(__mc68000__) #ifdef CONFIG_FONT_PEARL_8x8 - if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) - c = 100; + if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) + c = 100; #endif #ifdef CONFIG_FONT_6x11 - if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) - c = 100; + if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) + c = 100; #endif #endif - if ((yres < 400) == (f->height <= 8)) - c += 1000; + if ((yres < 400) == (f->height <= 8)) + c += 1000; + + /* prefer a bigger font for high resolution */ + res = (xres / f->width) * (yres / f->height) / 1000; + if (res > 20) + c += 20 - res; - if ((font_w & (1 << (f->width - 1))) && - (font_h & (1 << (f->height - 1)))) - c += 1000; + if ((font_w & (1 << (f->width - 1))) && + (font_h & (1 << (f->height - 1)))) + c += 1000; - if (c > cc) { - cc = c; - g = f; + if (c > cc) { + cc = c; + g = f; + } } - } - return g; + return g; } - -EXPORT_SYMBOL(find_font); EXPORT_SYMBOL(get_default_font); MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); diff --git a/mm/balloon_compaction.c b/mm/balloon_compaction.c index ba739b76e6c5..83a7b614061f 100644 --- a/mm/balloon_compaction.c +++ b/mm/balloon_compaction.c @@ -11,6 +11,105 @@ #include <linux/export.h> #include <linux/balloon_compaction.h> +static void balloon_page_enqueue_one(struct balloon_dev_info *b_dev_info, + struct page *page) +{ + /* + * Block others from accessing the 'page' when we get around to + * establishing additional references. We should be the only one + * holding a reference to the 'page' at this point. If we are not, then + * memory corruption is possible and we should stop execution. + */ + BUG_ON(!trylock_page(page)); + list_del(&page->lru); + balloon_page_insert(b_dev_info, page); + unlock_page(page); + __count_vm_event(BALLOON_INFLATE); +} + +/** + * balloon_page_list_enqueue() - inserts a list of pages into the balloon page + * list. + * @b_dev_info: balloon device descriptor where we will insert a new page to + * @pages: pages to enqueue - allocated using balloon_page_alloc. + * + * Driver must call it to properly enqueue a balloon pages before definitively + * removing it from the guest system. + * + * Return: number of pages that were enqueued. + */ +size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info, + struct list_head *pages) +{ + struct page *page, *tmp; + unsigned long flags; + size_t n_pages = 0; + + spin_lock_irqsave(&b_dev_info->pages_lock, flags); + list_for_each_entry_safe(page, tmp, pages, lru) { + balloon_page_enqueue_one(b_dev_info, page); + n_pages++; + } + spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); + return n_pages; +} +EXPORT_SYMBOL_GPL(balloon_page_list_enqueue); + +/** + * balloon_page_list_dequeue() - removes pages from balloon's page list and + * returns a list of the pages. + * @b_dev_info: balloon device decriptor where we will grab a page from. + * @pages: pointer to the list of pages that would be returned to the caller. + * @n_req_pages: number of requested pages. + * + * Driver must call this function to properly de-allocate a previous enlisted + * balloon pages before definetively releasing it back to the guest system. + * This function tries to remove @n_req_pages from the ballooned pages and + * return them to the caller in the @pages list. + * + * Note that this function may fail to dequeue some pages temporarily empty due + * to compaction isolated pages. + * + * Return: number of pages that were added to the @pages list. + */ +size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info, + struct list_head *pages, size_t n_req_pages) +{ + struct page *page, *tmp; + unsigned long flags; + size_t n_pages = 0; + + spin_lock_irqsave(&b_dev_info->pages_lock, flags); + list_for_each_entry_safe(page, tmp, &b_dev_info->pages, lru) { + if (n_pages == n_req_pages) + break; + + /* + * Block others from accessing the 'page' while we get around to + * establishing additional references and preparing the 'page' + * to be released by the balloon driver. + */ + if (!trylock_page(page)) + continue; + + if (IS_ENABLED(CONFIG_BALLOON_COMPACTION) && + PageIsolated(page)) { + /* raced with isolation */ + unlock_page(page); + continue; + } + balloon_page_delete(page); + __count_vm_event(BALLOON_DEFLATE); + list_add(&page->lru, pages); + unlock_page(page); + n_pages++; + } + spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); + + return n_pages; +} +EXPORT_SYMBOL_GPL(balloon_page_list_dequeue); + /* * balloon_page_alloc - allocates a new page for insertion into the balloon * page list. @@ -44,17 +143,9 @@ void balloon_page_enqueue(struct balloon_dev_info *b_dev_info, { unsigned long flags; - /* - * Block others from accessing the 'page' when we get around to - * establishing additional references. We should be the only one - * holding a reference to the 'page' at this point. - */ - BUG_ON(!trylock_page(page)); spin_lock_irqsave(&b_dev_info->pages_lock, flags); - balloon_page_insert(b_dev_info, page); - __count_vm_event(BALLOON_INFLATE); + balloon_page_enqueue_one(b_dev_info, page); spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); - unlock_page(page); } EXPORT_SYMBOL_GPL(balloon_page_enqueue); @@ -71,36 +162,13 @@ EXPORT_SYMBOL_GPL(balloon_page_enqueue); */ struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info) { - struct page *page, *tmp; unsigned long flags; - bool dequeued_page; + LIST_HEAD(pages); + int n_pages; - dequeued_page = false; - spin_lock_irqsave(&b_dev_info->pages_lock, flags); - list_for_each_entry_safe(page, tmp, &b_dev_info->pages, lru) { - /* - * Block others from accessing the 'page' while we get around - * establishing additional references and preparing the 'page' - * to be released by the balloon driver. - */ - if (trylock_page(page)) { -#ifdef CONFIG_BALLOON_COMPACTION - if (PageIsolated(page)) { - /* raced with isolation */ - unlock_page(page); - continue; - } -#endif - balloon_page_delete(page); - __count_vm_event(BALLOON_DEFLATE); - unlock_page(page); - dequeued_page = true; - break; - } - } - spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); + n_pages = balloon_page_list_dequeue(b_dev_info, &pages, 1); - if (!dequeued_page) { + if (n_pages != 1) { /* * If we are unable to dequeue a balloon page because the page * list is empty and there is no isolated pages, then something @@ -113,9 +181,9 @@ struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info) !b_dev_info->isolated_pages)) BUG(); spin_unlock_irqrestore(&b_dev_info->pages_lock, flags); - page = NULL; + return NULL; } - return page; + return list_first_entry(&pages, struct page, lru); } EXPORT_SYMBOL_GPL(balloon_page_dequeue); |