diff options
683 files changed, 32476 insertions, 9712 deletions
diff --git a/.clang-format b/.clang-format index 9b87ea1fc16e..1247d54f9e49 100644 --- a/.clang-format +++ b/.clang-format @@ -516,6 +516,7 @@ ForEachMacros: - 'of_property_for_each_string' - 'of_property_for_each_u32' - 'pci_bus_for_each_resource' + - 'pci_doe_for_each_off' - 'pcl_for_each_chunk' - 'pcl_for_each_segment' - 'pcm_for_each_format' diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index 7c2b846521f3..8494ef27e8d2 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -7,6 +7,7 @@ Description: all descendant memdevs for unbind. Writing '1' to this attribute flushes that work. + What: /sys/bus/cxl/devices/memX/firmware_version Date: December, 2020 KernelVersion: v5.12 @@ -16,6 +17,7 @@ Description: Memory Device Output Payload in the CXL-2.0 specification. + What: /sys/bus/cxl/devices/memX/ram/size Date: December, 2020 KernelVersion: v5.12 @@ -25,6 +27,7 @@ Description: identically named field in the Identify Memory Device Output Payload in the CXL-2.0 specification. + What: /sys/bus/cxl/devices/memX/pmem/size Date: December, 2020 KernelVersion: v5.12 @@ -34,6 +37,7 @@ Description: identically named field in the Identify Memory Device Output Payload in the CXL-2.0 specification. + What: /sys/bus/cxl/devices/memX/serial Date: January, 2022 KernelVersion: v5.18 @@ -43,6 +47,7 @@ Description: capability. Mandatory for CXL devices, see CXL 2.0 8.1.12.2 Memory Device PCIe Capabilities and Extended Capabilities. + What: /sys/bus/cxl/devices/memX/numa_node Date: January, 2022 KernelVersion: v5.18 @@ -52,114 +57,334 @@ Description: host PCI device for this memory device, emit the CPU node affinity for this device. + What: /sys/bus/cxl/devices/*/devtype Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - CXL device objects export the devtype attribute which mirrors - the same value communicated in the DEVTYPE environment variable - for uevents for devices on the "cxl" bus. + (RO) CXL device objects export the devtype attribute which + mirrors the same value communicated in the DEVTYPE environment + variable for uevents for devices on the "cxl" bus. + What: /sys/bus/cxl/devices/*/modalias Date: December, 2021 KernelVersion: v5.18 Contact: linux-cxl@vger.kernel.org Description: - CXL device objects export the modalias attribute which mirrors - the same value communicated in the MODALIAS environment variable - for uevents for devices on the "cxl" bus. + (RO) CXL device objects export the modalias attribute which + mirrors the same value communicated in the MODALIAS environment + variable for uevents for devices on the "cxl" bus. + What: /sys/bus/cxl/devices/portX/uport Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - CXL port objects are enumerated from either a platform firmware - device (ACPI0017 and ACPI0016) or PCIe switch upstream port with - CXL component registers. The 'uport' symlink connects the CXL - portX object to the device that published the CXL port + (RO) CXL port objects are enumerated from either a platform + firmware device (ACPI0017 and ACPI0016) or PCIe switch upstream + port with CXL component registers. The 'uport' symlink connects + the CXL portX object to the device that published the CXL port capability. + What: /sys/bus/cxl/devices/portX/dportY Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - CXL port objects are enumerated from either a platform firmware - device (ACPI0017 and ACPI0016) or PCIe switch upstream port with - CXL component registers. The 'dportY' symlink identifies one or - more downstream ports that the upstream port may target in its - decode of CXL memory resources. The 'Y' integer reflects the - hardware port unique-id used in the hardware decoder target - list. + (RO) CXL port objects are enumerated from either a platform + firmware device (ACPI0017 and ACPI0016) or PCIe switch upstream + port with CXL component registers. The 'dportY' symlink + identifies one or more downstream ports that the upstream port + may target in its decode of CXL memory resources. The 'Y' + integer reflects the hardware port unique-id used in the + hardware decoder target list. + What: /sys/bus/cxl/devices/decoderX.Y Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - CXL decoder objects are enumerated from either a platform + (RO) CXL decoder objects are enumerated from either a platform firmware description, or a CXL HDM decoder register set in a PCIe device (see CXL 2.0 section 8.2.5.12 CXL HDM Decoder Capability Structure). The 'X' in decoderX.Y represents the cxl_port container of this decoder, and 'Y' represents the instance id of a given decoder resource. + What: /sys/bus/cxl/devices/decoderX.Y/{start,size} Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - The 'start' and 'size' attributes together convey the physical - address base and number of bytes mapped in the decoder's decode - window. For decoders of devtype "cxl_decoder_root" the address - range is fixed. For decoders of devtype "cxl_decoder_switch" the - address is bounded by the decode range of the cxl_port ancestor - of the decoder's cxl_port, and dynamically updates based on the - active memory regions in that address space. + (RO) The 'start' and 'size' attributes together convey the + physical address base and number of bytes mapped in the + decoder's decode window. For decoders of devtype + "cxl_decoder_root" the address range is fixed. For decoders of + devtype "cxl_decoder_switch" the address is bounded by the + decode range of the cxl_port ancestor of the decoder's cxl_port, + and dynamically updates based on the active memory regions in + that address space. + What: /sys/bus/cxl/devices/decoderX.Y/locked Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - CXL HDM decoders have the capability to lock the configuration - until the next device reset. For decoders of devtype - "cxl_decoder_root" there is no standard facility to unlock them. - For decoders of devtype "cxl_decoder_switch" a secondary bus - reset, of the PCIe bridge that provides the bus for this - decoders uport, unlocks / resets the decoder. + (RO) CXL HDM decoders have the capability to lock the + configuration until the next device reset. For decoders of + devtype "cxl_decoder_root" there is no standard facility to + unlock them. For decoders of devtype "cxl_decoder_switch" a + secondary bus reset, of the PCIe bridge that provides the bus + for this decoders uport, unlocks / resets the decoder. + What: /sys/bus/cxl/devices/decoderX.Y/target_list Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - Display a comma separated list of the current decoder target - configuration. The list is ordered by the current configured - interleave order of the decoder's dport instances. Each entry in - the list is a dport id. + (RO) Display a comma separated list of the current decoder + target configuration. The list is ordered by the current + configured interleave order of the decoder's dport instances. + Each entry in the list is a dport id. + What: /sys/bus/cxl/devices/decoderX.Y/cap_{pmem,ram,type2,type3} Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - When a CXL decoder is of devtype "cxl_decoder_root", it + (RO) When a CXL decoder is of devtype "cxl_decoder_root", it represents a fixed memory window identified by platform firmware. A fixed window may only support a subset of memory types. The 'cap_*' attributes indicate whether persistent memory, volatile memory, accelerator memory, and / or expander memory may be mapped behind this decoder's memory window. + What: /sys/bus/cxl/devices/decoderX.Y/target_type Date: June, 2021 KernelVersion: v5.14 Contact: linux-cxl@vger.kernel.org Description: - When a CXL decoder is of devtype "cxl_decoder_switch", it can - optionally decode either accelerator memory (type-2) or expander - memory (type-3). The 'target_type' attribute indicates the - current setting which may dynamically change based on what + (RO) When a CXL decoder is of devtype "cxl_decoder_switch", it + can optionally decode either accelerator memory (type-2) or + expander memory (type-3). The 'target_type' attribute indicates + the current setting which may dynamically change based on what memory regions are activated in this decode hierarchy. + + +What: /sys/bus/cxl/devices/endpointX/CDAT +Date: July, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) If this sysfs entry is not present no DOE mailbox was + found to support CDAT data. If it is present and the length of + the data is 0 reading the CDAT data failed. Otherwise the CDAT + data is reported. + + +What: /sys/bus/cxl/devices/decoderX.Y/mode +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) When a CXL decoder is of devtype "cxl_decoder_endpoint" it + translates from a host physical address range, to a device local + address range. Device-local address ranges are further split + into a 'ram' (volatile memory) range and 'pmem' (persistent + memory) range. The 'mode' attribute emits one of 'ram', 'pmem', + 'mixed', or 'none'. The 'mixed' indication is for error cases + when a decoder straddles the volatile/persistent partition + boundary, and 'none' indicates the decoder is not actively + decoding, or no DPA allocation policy has been set. + + 'mode' can be written, when the decoder is in the 'disabled' + state, with either 'ram' or 'pmem' to set the boundaries for the + next allocation. + + +What: /sys/bus/cxl/devices/decoderX.Y/dpa_resource +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) When a CXL decoder is of devtype "cxl_decoder_endpoint", + and its 'dpa_size' attribute is non-zero, this attribute + indicates the device physical address (DPA) base address of the + allocation. + + +What: /sys/bus/cxl/devices/decoderX.Y/dpa_size +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) When a CXL decoder is of devtype "cxl_decoder_endpoint" it + translates from a host physical address range, to a device local + address range. The range, base address plus length in bytes, of + DPA allocated to this decoder is conveyed in these 2 attributes. + Allocations can be mutated as long as the decoder is in the + disabled state. A write to 'dpa_size' releases the previous DPA + allocation and then attempts to allocate from the free capacity + in the device partition referred to by 'decoderX.Y/mode'. + Allocate and free requests can only be performed on the highest + instance number disabled decoder with non-zero size. I.e. + allocations are enforced to occur in increasing 'decoderX.Y/id' + order and frees are enforced to occur in decreasing + 'decoderX.Y/id' order. + + +What: /sys/bus/cxl/devices/decoderX.Y/interleave_ways +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) The number of targets across which this decoder's host + physical address (HPA) memory range is interleaved. The device + maps every Nth block of HPA (of size == + 'interleave_granularity') to consecutive DPA addresses. The + decoder's position in the interleave is determined by the + device's (endpoint or switch) switch ancestry. For root + decoders their interleave is specified by platform firmware and + they only specify a downstream target order for host bridges. + + +What: /sys/bus/cxl/devices/decoderX.Y/interleave_granularity +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) The number of consecutive bytes of host physical address + space this decoder claims at address N before the decode rotates + to the next target in the interleave at address N + + interleave_granularity (assuming N is aligned to + interleave_granularity). + + +What: /sys/bus/cxl/devices/decoderX.Y/create_pmem_region +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Write a string in the form 'regionZ' to start the process + of defining a new persistent memory region (interleave-set) + within the decode range bounded by root decoder 'decoderX.Y'. + The value written must match the current value returned from + reading this attribute. An atomic compare exchange operation is + done on write to assign the requested id to a region and + allocate the region-id for the next creation attempt. EBUSY is + returned if the region name written does not match the current + cached value. + + +What: /sys/bus/cxl/devices/decoderX.Y/delete_region +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (WO) Write a string in the form 'regionZ' to delete that region, + provided it is currently idle / not bound to a driver. + + +What: /sys/bus/cxl/devices/regionZ/uuid +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Write a unique identifier for the region. This field must + be set for persistent regions and it must not conflict with the + UUID of another region. + + +What: /sys/bus/cxl/devices/regionZ/interleave_granularity +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Set the number of consecutive bytes each device in the + interleave set will claim. The possible interleave granularity + values are determined by the CXL spec and the participating + devices. + + +What: /sys/bus/cxl/devices/regionZ/interleave_ways +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Configures the number of devices participating in the + region is set by writing this value. Each device will provide + 1/interleave_ways of storage for the region. + + +What: /sys/bus/cxl/devices/regionZ/size +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) System physical address space to be consumed by the region. + When written trigger the driver to allocate space out of the + parent root decoder's address space. When read the size of the + address space is reported and should match the span of the + region's resource attribute. Size shall be set after the + interleave configuration parameters. Once set it cannot be + changed, only freed by writing 0. The kernel makes no guarantees + that data is maintained over an address space freeing event, and + there is no guarantee that a free followed by an allocate + results in the same address being allocated. + + +What: /sys/bus/cxl/devices/regionZ/resource +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) A region is a contiguous partition of a CXL root decoder + address space. Region capacity is allocated by writing to the + size attribute, the resulting physical address space determined + by the driver is reflected here. It is therefore not useful to + read this before writing a value to the size attribute. + + +What: /sys/bus/cxl/devices/regionZ/target[0..N] +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Write an endpoint decoder object name to 'targetX' where X + is the intended position of the endpoint device in the region + interleave and N is the 'interleave_ways' setting for the + region. ENXIO is returned if the write results in an impossible + to map decode scenario, like the endpoint is unreachable at that + position relative to the root decoder interleave. EBUSY is + returned if the position in the region is already occupied, or + if the region is not in a state to accept interleave + configuration changes. EINVAL is returned if the object name is + not an endpoint decoder. Once all positions have been + successfully written a final validation for decode conflicts is + performed before activating the region. + + +What: /sys/bus/cxl/devices/regionZ/commit +Date: May, 2022 +KernelVersion: v5.20 +Contact: linux-cxl@vger.kernel.org +Description: + (RW) Write a boolean 'true' string value to this attribute to + trigger the region to transition from the software programmed + state to the actively decoding in hardware state. The commit + operation in addition to validating that the region is in proper + configured state, validates that the decoders are being + committed in spec mandated order (last committed decoder id + + 1), and checks that the hardware accepts the commit request. + Reading this value indicates whether the region is committed or + not. diff --git a/Documentation/admin-guide/hw-vuln/spectre.rst b/Documentation/admin-guide/hw-vuln/spectre.rst index 9e9556826450..2ce2a38cdd55 100644 --- a/Documentation/admin-guide/hw-vuln/spectre.rst +++ b/Documentation/admin-guide/hw-vuln/spectre.rst @@ -422,6 +422,14 @@ The possible values in this file are: 'RSB filling' Protection of RSB on context switch enabled ============= =========================================== + - EIBRS Post-barrier Return Stack Buffer (PBRSB) protection status: + + =========================== ======================================================= + 'PBRSB-eIBRS: SW sequence' CPU is affected and protection of RSB on VMEXIT enabled + 'PBRSB-eIBRS: Vulnerable' CPU is vulnerable + 'PBRSB-eIBRS: Not affected' CPU is not affected by PBRSB + =========================== ======================================================= + Full mitigation might require a microcode update from the CPU vendor. When the necessary microcode is not available, the kernel will report vulnerability. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index bab2b0bf5988..db5de5f0b9d3 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1158,8 +1158,12 @@ nopku [X86] Disable Memory Protection Keys CPU feature found in some Intel CPUs. - <module>.async_probe [KNL] - Enable asynchronous probe on this module. + <module>.async_probe[=<bool>] [KNL] + If no <bool> value is specified or if the value + specified is not a valid <bool>, enable asynchronous + probe on this module. Otherwise, enable/disable + asynchronous probe on this module as indicated by the + <bool> value. See also: module.async_probe early_ioremap_debug [KNL] Enable debug messages in early_ioremap support. This @@ -1731,12 +1735,13 @@ hugetlb_free_vmemmap= [KNL] Reguires CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP enabled. + Control if HugeTLB Vmemmap Optimization (HVO) is enabled. Allows heavy hugetlb users to free up some more memory (7 * PAGE_SIZE for each 2MB hugetlb page). - Format: { [oO][Nn]/Y/y/1 | [oO][Ff]/N/n/0 (default) } + Format: { on | off (default) } - [oO][Nn]/Y/y/1: enable the feature - [oO][Ff]/N/n/0: disable the feature + on: enable HVO + off: disable HVO Built with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON=y, the default is on. @@ -3277,6 +3282,15 @@ For details see: Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst + module.async_probe=<bool> + [KNL] When set to true, modules will use async probing + by default. To enable/disable async probing for a + specific module, use the module specific control that + is documented under <module>.async_probe. When both + module.async_probe and <module>.async_probe are + specified, <module>.async_probe takes precedence for + the specific module. + module.sig_enforce [KNL] When CONFIG_MODULE_SIG is set, this means that modules without (valid) signatures will fail to load. diff --git a/Documentation/admin-guide/mm/hugetlbpage.rst b/Documentation/admin-guide/mm/hugetlbpage.rst index a90330d0a837..8e2727dc18d4 100644 --- a/Documentation/admin-guide/mm/hugetlbpage.rst +++ b/Documentation/admin-guide/mm/hugetlbpage.rst @@ -164,8 +164,8 @@ default_hugepagesz will all result in 256 2M huge pages being allocated. Valid default huge page size is architecture dependent. hugetlb_free_vmemmap - When CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP is set, this enables optimizing - unused vmemmap pages associated with each HugeTLB page. + When CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP is set, this enables HugeTLB + Vmemmap Optimization (HVO). When multiple huge page sizes are supported, ``/proc/sys/vm/nr_hugepages`` indicates the current number of pre-allocated huge pages of the default size. diff --git a/Documentation/admin-guide/mm/memory-hotplug.rst b/Documentation/admin-guide/mm/memory-hotplug.rst index 0f56ecd8ac05..a3c9e8ad8fa0 100644 --- a/Documentation/admin-guide/mm/memory-hotplug.rst +++ b/Documentation/admin-guide/mm/memory-hotplug.rst @@ -653,8 +653,8 @@ block might fail: - Concurrent activity that operates on the same physical memory area, such as allocating gigantic pages, can result in temporary offlining failures. -- Out of memory when dissolving huge pages, especially when freeing unused - vmemmap pages associated with each hugetlb page is enabled. +- Out of memory when dissolving huge pages, especially when HugeTLB Vmemmap + Optimization (HVO) is enabled. Offlining code may be able to migrate huge page contents, but may not be able to dissolve the source huge page because it fails allocating (unmovable) pages diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst index f74f722ad702..9b833e439f09 100644 --- a/Documentation/admin-guide/sysctl/vm.rst +++ b/Documentation/admin-guide/sysctl/vm.rst @@ -569,8 +569,7 @@ This knob is not available when the size of 'struct page' (a structure defined in include/linux/mm_types.h) is not power of two (an unusual system config could result in this). -Enable (set to 1) or disable (set to 0) the feature of optimizing vmemmap pages -associated with each HugeTLB page. +Enable (set to 1) or disable (set to 0) HugeTLB Vmemmap Optimization (HVO). Once enabled, the vmemmap pages of subsequent allocation of HugeTLB pages from buddy allocator will be optimized (7 pages per 2MB HugeTLB page and 4095 pages diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst index 437de2a7a5de..a210b8a4df00 100644 --- a/Documentation/bpf/bpf_design_QA.rst +++ b/Documentation/bpf/bpf_design_QA.rst @@ -214,6 +214,12 @@ A: NO. Tracepoints are tied to internal implementation details hence they are subject to change and can break with newer kernels. BPF programs need to change accordingly when this happens. +Q: Are places where kprobes can attach part of the stable ABI? +-------------------------------------------------------------- +A: NO. The places to which kprobes can attach are internal implementation +details, which means that they are subject to change and can break with +newer kernels. BPF programs need to change accordingly when this happens. + Q: How much stack space a BPF program uses? ------------------------------------------- A: Currently all program types are limited to 512 bytes of stack @@ -273,3 +279,22 @@ cc (congestion-control) implementations. If any of these kernel functions has changed, both the in-tree and out-of-tree kernel tcp cc implementations have to be changed. The same goes for the bpf programs and they have to be adjusted accordingly. + +Q: Attaching to arbitrary kernel functions is an ABI? +----------------------------------------------------- +Q: BPF programs can be attached to many kernel functions. Do these +kernel functions become part of the ABI? + +A: NO. + +The kernel function prototypes will change, and BPF programs attaching to +them will need to change. The BPF compile-once-run-everywhere (CO-RE) +should be used in order to make it easier to adapt your BPF programs to +different versions of the kernel. + +Q: Marking a function with BTF_ID makes that function an ABI? +------------------------------------------------------------- +A: NO. + +The BTF_ID macro does not cause a function to become part of the ABI +any more than does the EXPORT_SYMBOL_GPL macro. diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml index 2f1b8b6852a0..24fa3d87a40b 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -25,6 +25,7 @@ properties: - description: v2 of CPUFREQ HW (EPSS) items: - enum: + - qcom,sm6375-cpufreq-epss - qcom,sm8250-cpufreq-epss - const: qcom,cpufreq-epss diff --git a/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml index 10b3a7a4af36..a11e1b867379 100644 --- a/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml +++ b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml @@ -22,6 +22,13 @@ select: compatible: contains: enum: + - qcom,apq8064 + - qcom,apq8096 + - qcom,ipq8064 + - qcom,msm8939 + - qcom,msm8960 + - qcom,msm8974 + - qcom,msm8996 - qcom,qcs404 required: - compatible diff --git a/Documentation/devicetree/bindings/gpio/x-powers,axp209-gpio.yaml b/Documentation/devicetree/bindings/gpio/x-powers,axp209-gpio.yaml index 0f628b088cec..14486aee97b4 100644 --- a/Documentation/devicetree/bindings/gpio/x-powers,axp209-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/x-powers,axp209-gpio.yaml @@ -19,8 +19,14 @@ properties: oneOf: - enum: - x-powers,axp209-gpio + - x-powers,axp221-gpio - x-powers,axp813-gpio - items: + - enum: + - x-powers,axp223-gpio + - x-powers,axp809-gpio + - const: x-powers,axp221-gpio + - items: - const: x-powers,axp803-gpio - const: x-powers,axp813-gpio diff --git a/Documentation/devicetree/bindings/input/adc-joystick.yaml b/Documentation/devicetree/bindings/input/adc-joystick.yaml index 2ee04e03bc22..64d961458ac7 100644 --- a/Documentation/devicetree/bindings/input/adc-joystick.yaml +++ b/Documentation/devicetree/bindings/input/adc-joystick.yaml @@ -45,6 +45,7 @@ additionalProperties: false patternProperties: "^axis@[0-9a-f]+$": type: object + $ref: input.yaml# description: > Represents a joystick axis bound to the given ADC channel. For each entry in the io-channels list, one axis subnode with a matching @@ -57,7 +58,6 @@ patternProperties: description: Index of an io-channels list entry bound to this axis. linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 description: EV_ABS specific event code generated by the axis. abs-range: diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt deleted file mode 100644 index 6c8be6a9ace2..000000000000 --- a/Documentation/devicetree/bindings/input/adc-keys.txt +++ /dev/null @@ -1,67 +0,0 @@ -ADC attached resistor ladder buttons ------------------------------------- - -Required properties: - - compatible: "adc-keys" - - io-channels: Phandle to an ADC channel - - io-channel-names = "buttons"; - - keyup-threshold-microvolt: Voltage above or equal to which all the keys are - considered up. - -Optional properties: - - poll-interval: Poll interval time in milliseconds - - autorepeat: Boolean, Enable auto repeat feature of Linux input - subsystem. - -Each button (key) is represented as a sub-node of "adc-keys": - -Required subnode-properties: - - label: Descriptive name of the key. - - linux,code: Keycode to emit. - - press-threshold-microvolt: voltage above or equal to which this key is - considered pressed. - -No two values of press-threshold-microvolt may be the same. -All values of press-threshold-microvolt must be less than -keyup-threshold-microvolt. - -Example: - -#include <dt-bindings/input/input.h> - - adc-keys { - compatible = "adc-keys"; - io-channels = <&lradc 0>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <2000000>; - - button-up { - label = "Volume Up"; - linux,code = <KEY_VOLUMEUP>; - press-threshold-microvolt = <1500000>; - }; - - button-down { - label = "Volume Down"; - linux,code = <KEY_VOLUMEDOWN>; - press-threshold-microvolt = <1000000>; - }; - - button-enter { - label = "Enter"; - linux,code = <KEY_ENTER>; - press-threshold-microvolt = <500000>; - }; - }; - -+--------------------------------+------------------------+ -| 2.000.000 <= value | no key pressed | -+--------------------------------+------------------------+ -| 1.500.000 <= value < 2.000.000 | KEY_VOLUMEUP pressed | -+--------------------------------+------------------------+ -| 1.000.000 <= value < 1.500.000 | KEY_VOLUMEDOWN pressed | -+--------------------------------+------------------------+ -| 500.000 <= value < 1.000.000 | KEY_ENTER pressed | -+--------------------------------+------------------------+ -| value < 500.000 | no key pressed | -+--------------------------------+------------------------+ diff --git a/Documentation/devicetree/bindings/input/adc-keys.yaml b/Documentation/devicetree/bindings/input/adc-keys.yaml new file mode 100644 index 000000000000..7aa078dead37 --- /dev/null +++ b/Documentation/devicetree/bindings/input/adc-keys.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/adc-keys.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC attached resistor ladder buttons + +maintainers: + - Alexandre Belloni <alexandre.belloni@bootlin.com> + +allOf: + - $ref: input.yaml# + +properties: + compatible: + const: adc-keys + + io-channels: + maxItems: 1 + + io-channel-names: + const: buttons + + keyup-threshold-microvolt: + description: + Voltage above or equal to which all the keys are considered up. + + poll-interval: true + autorepeat: true + +patternProperties: + '^button-': + type: object + $ref: input.yaml# + additionalProperties: false + description: + Each button (key) is represented as a sub-node. + + properties: + label: true + + linux,code: true + + press-threshold-microvolt: + description: + Voltage above or equal to which this key is considered pressed. No + two values of press-threshold-microvolt may be the same. All values + of press-threshold-microvolt must be less than + keyup-threshold-microvolt. + + required: + - linux,code + - press-threshold-microvolt + +required: + - compatible + - io-channels + - io-channel-names + - keyup-threshold-microvolt + +additionalProperties: false + +examples: + - | + #include <dt-bindings/input/input.h> + // +--------------------------------+------------------------+ + // | 2.000.000 <= value | no key pressed | + // +--------------------------------+------------------------+ + // | 1.500.000 <= value < 2.000.000 | KEY_VOLUMEUP pressed | + // +--------------------------------+------------------------+ + // | 1.000.000 <= value < 1.500.000 | KEY_VOLUMEDOWN pressed | + // +--------------------------------+------------------------+ + // | 500.000 <= value < 1.000.000 | KEY_ENTER pressed | + // +--------------------------------+------------------------+ + // | value < 500.000 | no key pressed | + // +--------------------------------+------------------------+ + + adc-keys { + compatible = "adc-keys"; + io-channels = <&lradc 0>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <2000000>; + + button-up { + label = "Volume Up"; + linux,code = <KEY_VOLUMEUP>; + press-threshold-microvolt = <1500000>; + }; + + button-down { + label = "Volume Down"; + linux,code = <KEY_VOLUMEDOWN>; + press-threshold-microvolt = <1000000>; + }; + + button-enter { + label = "Enter"; + linux,code = <KEY_ENTER>; + press-threshold-microvolt = <500000>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml index 3399fc288afb..9700dc468b25 100644 --- a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml +++ b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml @@ -44,14 +44,13 @@ properties: patternProperties: "^button-[0-9]+$": type: object + $ref: input.yaml# properties: label: $ref: /schemas/types.yaml#/definitions/string description: Descriptive name of the key - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Keycode to emit + linux,code: true channel: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml b/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml index b4ad829d7383..442f623bb294 100644 --- a/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml +++ b/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml @@ -17,6 +17,7 @@ description: | allOf: - $ref: input.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml index a3a1e5a65306..02e605fac408 100644 --- a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml +++ b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml @@ -37,10 +37,6 @@ properties: device is temporarily held in hardware reset prior to initialization if this property is present. - azoteq,rf-filt-enable: - type: boolean - description: Enables the device's internal RF filter. - azoteq,max-counts: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3] @@ -421,6 +417,7 @@ patternProperties: patternProperties: "^event-(prox|touch)$": type: object + $ref: input.yaml# description: Represents a proximity or touch event reported by the channel. @@ -467,14 +464,9 @@ patternProperties: The IQS7222B does not feature channel-specific timeouts; the time- out specified for any one channel applies to all channels. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - Numeric key or switch code associated with the event. Specify - KEY_RESERVED (0) to opt out of event reporting. + linux,code: true linux,input-type: - $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 5] default: 1 description: @@ -537,9 +529,8 @@ patternProperties: azoteq,bottom-speed: $ref: /schemas/types.yaml#/definitions/uint32 - multipleOf: 4 minimum: 0 - maximum: 1020 + maximum: 255 description: Specifies the speed of movement after which coordinate filtering is linearly reduced. @@ -575,14 +566,13 @@ patternProperties: patternProperties: "^event-(press|tap|(swipe|flick)-(pos|neg))$": type: object + $ref: input.yaml# description: Represents a press or gesture (IQS7222A only) event reported by the slider. properties: - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key code associated with the event. + linux,code: true azoteq,gesture-max-ms: multipleOf: 4 @@ -616,16 +606,15 @@ patternProperties: azoteq,gpio-select: $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 - maxItems: 1 + maxItems: 3 items: minimum: 0 - maximum: 0 + maximum: 2 description: | - Specifies an individual GPIO mapped to a tap, swipe or flick - gesture as follows: + Specifies one or more GPIO mapped to the event as follows: 0: GPIO0 - 1: GPIO3 (reserved) - 2: GPIO4 (reserved) + 1: GPIO3 (IQS7222C only) + 2: GPIO4 (IQS7222C only) Note that although multiple events can be mapped to a single GPIO, they must all be of the same type (proximity, touch or @@ -710,6 +699,14 @@ allOf: multipleOf: 4 maximum: 1020 + patternProperties: + "^event-(press|tap|(swipe|flick)-(pos|neg))$": + properties: + azoteq,gpio-select: + maxItems: 1 + items: + maximum: 0 + else: patternProperties: "^channel-([0-9]|1[0-9])$": @@ -726,8 +723,6 @@ allOf: azoteq,gesture-dist: false - azoteq,gpio-select: false - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml b/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml index 878464f128dc..5139af287d3e 100644 --- a/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml +++ b/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml @@ -57,7 +57,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - mpr121@5a { + touchkey@5a { compatible = "fsl,mpr121-touchkey"; reg = <0x5a>; interrupt-parent = <&gpio1>; @@ -77,7 +77,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - mpr121@5a { + touchkey@5a { compatible = "fsl,mpr121-touchkey"; reg = <0x5a>; poll-interval = <20>; diff --git a/Documentation/devicetree/bindings/input/gpio-keys.yaml b/Documentation/devicetree/bindings/input/gpio-keys.yaml index 7fe1966ea28a..17ac9dff7972 100644 --- a/Documentation/devicetree/bindings/input/gpio-keys.yaml +++ b/Documentation/devicetree/bindings/input/gpio-keys.yaml @@ -15,107 +15,106 @@ properties: - gpio-keys - gpio-keys-polled + autorepeat: true + + label: + description: Name of entire device + + poll-interval: true + patternProperties: - ".*": - if: - type: object - then: - $ref: input.yaml# + "^(button|event|key|switch|(button|event|key|switch)-[a-z0-9-]+|[a-z0-9-]+-(button|event|key|switch))$": + $ref: input.yaml# - properties: - gpios: - maxItems: 1 + properties: + gpios: + maxItems: 1 + + interrupts: + maxItems: 1 - interrupts: - maxItems: 1 + label: + description: Descriptive name of the key. - label: - description: Descriptive name of the key. + linux,code: + description: Key / Axis code to emit. - linux,code: - description: Key / Axis code to emit. - $ref: /schemas/types.yaml#/definitions/uint32 + linux,input-type: + default: 1 # EV_KEY - linux,input-type: - description: - Specify event type this button/key generates. If not specified defaults to - <1> == EV_KEY. - $ref: /schemas/types.yaml#/definitions/uint32 + linux,input-value: + description: | + If linux,input-type is EV_ABS or EV_REL then this + value is sent for events this button generates when pressed. + EV_ABS/EV_REL axis will generate an event with a value of 0 + when all buttons with linux,input-type == type and + linux,code == axis are released. This value is interpreted + as a signed 32 bit value, e.g. to make a button generate a + value of -1 use: - default: 1 + linux,input-value = <0xffffffff>; /* -1 */ - linux,input-value: - description: | - If linux,input-type is EV_ABS or EV_REL then this - value is sent for events this button generates when pressed. - EV_ABS/EV_REL axis will generate an event with a value of 0 - when all buttons with linux,input-type == type and - linux,code == axis are released. This value is interpreted - as a signed 32 bit value, e.g. to make a button generate a - value of -1 use: + $ref: /schemas/types.yaml#/definitions/uint32 - linux,input-value = <0xffffffff>; /* -1 */ + debounce-interval: + description: + Debouncing interval time in milliseconds. If not specified defaults to 5. + $ref: /schemas/types.yaml#/definitions/uint32 - $ref: /schemas/types.yaml#/definitions/uint32 + default: 5 - debounce-interval: - description: - Debouncing interval time in milliseconds. If not specified defaults to 5. - $ref: /schemas/types.yaml#/definitions/uint32 + wakeup-source: + description: Button can wake-up the system. - default: 5 + wakeup-event-action: + description: | + Specifies whether the key should wake the system when asserted, when + deasserted, or both. This property is only valid for keys that wake up the + system (e.g., when the "wakeup-source" property is also provided). - wakeup-source: - description: Button can wake-up the system. + Supported values are defined in linux-event-codes.h: - wakeup-event-action: - description: | - Specifies whether the key should wake the system when asserted, when - deasserted, or both. This property is only valid for keys that wake up the - system (e.g., when the "wakeup-source" property is also provided). + EV_ACT_ANY - both asserted and deasserted + EV_ACT_ASSERTED - asserted + EV_ACT_DEASSERTED - deasserted + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] - Supported values are defined in linux-event-codes.h: + linux,can-disable: + description: + Indicates that button is connected to dedicated (not shared) interrupt + which can be disabled to suppress events from the button. + type: boolean - EV_ACT_ANY - both asserted and deasserted - EV_ACT_ASSERTED - asserted - EV_ACT_DEASSERTED - deasserted - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2] + required: + - linux,code - linux,can-disable: - description: - Indicates that button is connected to dedicated (not shared) interrupt - which can be disabled to suppress events from the button. - type: boolean + anyOf: + - required: + - interrupts + - required: + - interrupts-extended + - required: + - gpios + dependencies: + wakeup-event-action: [ wakeup-source ] + linux,input-value: [ gpios ] + + unevaluatedProperties: false + +allOf: + - $ref: input.yaml# + - if: + properties: + compatible: + const: gpio-keys-polled + then: required: - - linux,code - - anyOf: - - required: - - interrupts - - required: - - gpios - - dependencies: - wakeup-event-action: [ wakeup-source ] - linux,input-value: [ gpios ] - - unevaluatedProperties: false - -if: - properties: - compatible: - const: gpio-keys-polled -then: - properties: - poll-interval: - description: - Poll interval time in milliseconds - $ref: /schemas/types.yaml#/definitions/uint32 - - required: - - poll-interval + - poll-interval + else: + properties: + poll-interval: false additionalProperties: false @@ -127,13 +126,13 @@ examples: compatible = "gpio-keys"; autorepeat; - up { + key-up { label = "GPIO Key UP"; linux,code = <103>; gpios = <&gpio1 0 1>; }; - down { + key-down { label = "GPIO Key DOWN"; linux,code = <108>; interrupts = <1 IRQ_TYPE_EDGE_FALLING>; diff --git a/Documentation/devicetree/bindings/input/input.yaml b/Documentation/devicetree/bindings/input/input.yaml index d41d8743aad4..17512f4347fd 100644 --- a/Documentation/devicetree/bindings/input/input.yaml +++ b/Documentation/devicetree/bindings/input/input.yaml @@ -21,7 +21,26 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32-array items: minimum: 0 - maximum: 0xff + maximum: 0x2ff + + linux,code: + description: + Specifies a single numeric keycode value to be used for reporting + button/switch events. Specify KEY_RESERVED (0) to opt out of event + reporting. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 0x2ff + + linux,input-type: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 1 # EV_KEY + - 2 # EV_REL + - 3 # EV_ABS + - 5 # EV_SW + description: + Specifies whether the event is to be interpreted as a key, relative, + absolute, or switch. poll-interval: description: Poll interval time in milliseconds. @@ -39,4 +58,7 @@ properties: reset automatically. Device with key pressed reset feature can specify this property. +dependencies: + linux,input-type: [ "linux,code" ] + additionalProperties: true diff --git a/Documentation/devicetree/bindings/input/iqs269a.yaml b/Documentation/devicetree/bindings/input/iqs269a.yaml index 9c154e5e1a91..3c430d38594f 100644 --- a/Documentation/devicetree/bindings/input/iqs269a.yaml +++ b/Documentation/devicetree/bindings/input/iqs269a.yaml @@ -370,6 +370,7 @@ patternProperties: patternProperties: "^event-prox(-alt)?$": type: object + $ref: input.yaml# description: Represents a proximity event reported by the channel in response to a decrease in counts. Node names suffixed with '-alt' instead corre- @@ -396,14 +397,13 @@ patternProperties: default: 10 description: Specifies the threshold for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false "^event-touch(-alt)?$": type: object + $ref: input.yaml# description: Represents a touch event reported by the channel. properties: @@ -421,14 +421,13 @@ patternProperties: default: 4 description: Specifies the hysteresis for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false "^event-deep(-alt)?$": type: object + $ref: input.yaml# description: Represents a deep-touch event reported by the channel. properties: @@ -446,9 +445,7 @@ patternProperties: default: 0 description: Specifies the hysteresis for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false @@ -475,7 +472,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - iqs269a@44 { + touch@44 { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/input/iqs626a.yaml b/Documentation/devicetree/bindings/input/iqs626a.yaml index 0cb736c541c9..7a27502095f3 100644 --- a/Documentation/devicetree/bindings/input/iqs626a.yaml +++ b/Documentation/devicetree/bindings/input/iqs626a.yaml @@ -449,6 +449,7 @@ patternProperties: patternProperties: "^event-(prox|touch|deep)(-alt)?$": type: object + $ref: input.yaml# description: Represents a proximity, touch or deep-touch event reported by the channel in response to a decrease in counts. Node names suffixed with @@ -487,21 +488,15 @@ patternProperties: Specifies the hysteresis for the event (touch and deep-touch events only). - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true linux,input-type: - $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 5] description: Specifies whether the event is to be interpreted as a key (1) or a switch (5). By default, Hall-channel events are interpreted as switches and all others are interpreted as keys. - dependencies: - linux,input-type: ["linux,code"] - additionalProperties: false dependencies: @@ -511,6 +506,7 @@ patternProperties: "^trackpad-3x[2-3]$": type: object + $ref: input.yaml# description: Represents all channels associated with the trackpad. The channels are collectively active if the trackpad is defined and inactive otherwise. @@ -679,7 +675,6 @@ patternProperties: Specifies the raw count filter strength during low-power mode. linux,keycodes: - $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 maxItems: 6 description: | @@ -751,7 +746,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - iqs626a@44 { + touch@44 { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/input/iqs62x-keys.yaml b/Documentation/devicetree/bindings/input/iqs62x-keys.yaml index 77fe3b545b35..0aa951f0ab92 100644 --- a/Documentation/devicetree/bindings/input/iqs62x-keys.yaml +++ b/Documentation/devicetree/bindings/input/iqs62x-keys.yaml @@ -9,6 +9,9 @@ title: Azoteq IQS620A/621/622/624/625 Keys and Switches maintainers: - Jeff LaBundy <jeff@labundy.com> +allOf: + - $ref: input.yaml# + description: | The Azoteq IQS620A, IQS621, IQS622, IQS624 and IQS625 multi-function sensors feature a variety of self-capacitive, mutual-inductive and Hall-effect sens- @@ -30,7 +33,6 @@ properties: - azoteq,iqs625-keys linux,keycodes: - $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 maxItems: 16 description: | @@ -89,15 +91,14 @@ properties: patternProperties: "^hall-switch-(north|south)$": type: object + $ref: input.yaml# description: Represents north/south-field Hall-effect sensor touch or proximity events. Note that north/south-field orientation is reversed on the IQS620AXzCSR device due to its flip-chip package. properties: - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric switch code associated with the event. + linux,code: true azoteq,use-prox: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/input/max77650-onkey.yaml b/Documentation/devicetree/bindings/input/max77650-onkey.yaml index 3a2ad6ec64db..48edc0c8c1dd 100644 --- a/Documentation/devicetree/bindings/input/max77650-onkey.yaml +++ b/Documentation/devicetree/bindings/input/max77650-onkey.yaml @@ -16,15 +16,15 @@ description: | The onkey controller is represented as a sub-node of the PMIC node on the device tree. +allOf: + - $ref: input.yaml# + properties: compatible: const: maxim,max77650-onkey linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - The key-code to be reported when the key is pressed. Defaults - to KEY_POWER. + default: 116 # KEY_POWER maxim,onkey-slide: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml index d5d6bced3148..96358b12f9b2 100644 --- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml +++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml @@ -112,7 +112,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - cap1188@28 { + touch@28 { compatible = "microchip,cap1188"; interrupt-parent = <&gpio1>; interrupts = <0 0>; diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index 2e8da7470513..46bc8c028fe6 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -85,6 +85,14 @@ properties: minimum: 0 maximum: 80 + report-rate-hz: + description: | + Allows setting the scan rate in Hertz. + M06 supports range from 30 to 140 Hz. + M12 supports range from 1 to 255 Hz. + minimum: 1 + maximum: 255 + touchscreen-size-x: true touchscreen-size-y: true touchscreen-fuzz-x: true diff --git a/Documentation/devicetree/bindings/opp/opp-v2-base.yaml b/Documentation/devicetree/bindings/opp/opp-v2-base.yaml index 76c8acd981b3..66d0ec763f0b 100644 --- a/Documentation/devicetree/bindings/opp/opp-v2-base.yaml +++ b/Documentation/devicetree/bindings/opp/opp-v2-base.yaml @@ -50,6 +50,16 @@ patternProperties: property to uniquely identify the OPP nodes exists. Devices like power domains must have another (implementation dependent) property. + Entries for multiple clocks shall be provided in the same field, as + array of frequencies. The OPP binding doesn't provide any provisions + to relate the values to their clocks or the order in which the clocks + need to be configured and that is left for the implementation + specific binding. + minItems: 1 + maxItems: 16 + items: + maxItems: 1 + opp-microvolt: description: | Voltage for the OPP diff --git a/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml b/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml index 30f7b596d609..59663e897dae 100644 --- a/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml +++ b/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml @@ -98,6 +98,8 @@ examples: capacity-dmips-mhz = <1024>; clocks = <&kryocc 0>; operating-points-v2 = <&cluster0_opp>; + power-domains = <&cpr>; + power-domain-names = "cpr"; #cooling-cells = <2>; next-level-cache = <&L2_0>; L2_0: l2-cache { @@ -115,6 +117,8 @@ examples: capacity-dmips-mhz = <1024>; clocks = <&kryocc 0>; operating-points-v2 = <&cluster0_opp>; + power-domains = <&cpr>; + power-domain-names = "cpr"; #cooling-cells = <2>; next-level-cache = <&L2_0>; }; @@ -128,6 +132,8 @@ examples: capacity-dmips-mhz = <1024>; clocks = <&kryocc 1>; operating-points-v2 = <&cluster1_opp>; + power-domains = <&cpr>; + power-domain-names = "cpr"; #cooling-cells = <2>; next-level-cache = <&L2_1>; L2_1: l2-cache { @@ -145,6 +151,8 @@ examples: capacity-dmips-mhz = <1024>; clocks = <&kryocc 1>; operating-points-v2 = <&cluster1_opp>; + power-domains = <&cpr>; + power-domain-names = "cpr"; #cooling-cells = <2>; next-level-cache = <&L2_1>; }; @@ -182,18 +190,21 @@ examples: opp-microvolt = <905000 905000 1140000>; opp-supported-hw = <0x7>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp1>; }; opp-1401600000 { opp-hz = /bits/ 64 <1401600000>; opp-microvolt = <1140000 905000 1140000>; opp-supported-hw = <0x5>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp2>; }; opp-1593600000 { opp-hz = /bits/ 64 <1593600000>; opp-microvolt = <1140000 905000 1140000>; opp-supported-hw = <0x1>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp3>; }; }; @@ -207,24 +218,28 @@ examples: opp-microvolt = <905000 905000 1140000>; opp-supported-hw = <0x7>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp1>; }; opp-1804800000 { opp-hz = /bits/ 64 <1804800000>; opp-microvolt = <1140000 905000 1140000>; opp-supported-hw = <0x6>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp4>; }; opp-1900800000 { opp-hz = /bits/ 64 <1900800000>; opp-microvolt = <1140000 905000 1140000>; opp-supported-hw = <0x4>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp5>; }; opp-2150400000 { opp-hz = /bits/ 64 <2150400000>; opp-microvolt = <1140000 905000 1140000>; opp-supported-hw = <0x1>; clock-latency-ns = <200000>; + required-opps = <&cpr_opp6>; }; }; diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml index 0681b9a3965f..d19d65c870aa 100644 --- a/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml @@ -46,6 +46,7 @@ properties: - allwinner,sun8i-v3s-pinctrl - allwinner,sun9i-a80-pinctrl - allwinner,sun9i-a80-r-pinctrl + - allwinner,sun20i-d1-pinctrl - allwinner,sun50i-a64-pinctrl - allwinner,sun50i-a64-r-pinctrl - allwinner,sun50i-a100-pinctrl @@ -80,9 +81,6 @@ properties: - const: hosc - const: losc - resets: - maxItems: 1 - gpio-controller: true interrupt-controller: true gpio-line-names: true @@ -185,6 +183,18 @@ allOf: properties: compatible: enum: + - allwinner,sun20i-d1-pinctrl + + then: + properties: + interrupts: + minItems: 6 + maxItems: 6 + + - if: + properties: + compatible: + enum: - allwinner,sun9i-a80-pinctrl then: diff --git a/Documentation/devicetree/bindings/pinctrl/nuvoton,wpcm450-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/nuvoton,wpcm450-pinctrl.yaml index 47a56b83a610..7a11beb8f222 100644 --- a/Documentation/devicetree/bindings/pinctrl/nuvoton,wpcm450-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nuvoton,wpcm450-pinctrl.yaml @@ -152,7 +152,7 @@ examples: pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uid>, <&pinmux_uid>; - uid { + button-uid { label = "UID"; linux,code = <102>; gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8186.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8186.yaml index 8a2bb8608291..1eeb885ce0c6 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8186.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8186.yaml @@ -28,6 +28,8 @@ properties: gpio-ranges: maxItems: 1 + gpio-line-names: true + reg: description: | Physical address base for gpio base registers. There are 8 different GPIO @@ -105,31 +107,8 @@ patternProperties: drive-strength: enum: [2, 4, 6, 8, 10, 12, 14, 16] - mediatek,drive-strength-adv: - description: | - Describe the specific driving setup property. - For I2C pins, the existing generic driving setup can only support - 2/4/6/8/10/12/14/16mA driving. But in specific driving setup, they - can support 0.125/0.25/0.5/1mA adjustment. If we enable specific - driving setup, the existing generic setup will be disabled. - The specific driving setup is controlled by E1E0EN. - When E1=0/E0=0, the strength is 0.125mA. - When E1=0/E0=1, the strength is 0.25mA. - When E1=1/E0=0, the strength is 0.5mA. - When E1=1/E0=1, the strength is 1mA. - EN is used to enable or disable the specific driving setup. - Valid arguments are described as below: - 0: (E1, E0, EN) = (0, 0, 0) - 1: (E1, E0, EN) = (0, 0, 1) - 2: (E1, E0, EN) = (0, 1, 0) - 3: (E1, E0, EN) = (0, 1, 1) - 4: (E1, E0, EN) = (1, 0, 0) - 5: (E1, E0, EN) = (1, 0, 1) - 6: (E1, E0, EN) = (1, 1, 0) - 7: (E1, E0, EN) = (1, 1, 1) - So the valid arguments are from 0 to 7. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3, 4, 5, 6, 7] + drive-strength-microamp: + enum: [125, 250, 500, 1000] bias-pull-down: oneOf: @@ -291,7 +270,7 @@ examples: pinmux = <PINMUX_GPIO127__FUNC_SCL0>, <PINMUX_GPIO128__FUNC_SDA0>; bias-pull-up = <MTK_PULL_SET_RSEL_001>; - mediatek,drive-strength-adv = <7>; + drive-strength-microamp = <1000>; }; }; }; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8192.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8192.yaml index c90a132fbc79..e0e943e5b874 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8192.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8192.yaml @@ -80,46 +80,30 @@ patternProperties: dt-bindings/pinctrl/mt65xx.h. It can only support 2/4/6/8/10/12/14/16mA in mt8192. enum: [2, 4, 6, 8, 10, 12, 14, 16] - mediatek,drive-strength-adv: - description: | - Describe the specific driving setup property. - For I2C pins, the existing generic driving setup can only support - 2/4/6/8/10/12/14/16mA driving. But in specific driving setup, they - can support 0.125/0.25/0.5/1mA adjustment. If we enable specific - driving setup, the existing generic setup will be disabled. - The specific driving setup is controlled by E1E0EN. - When E1=0/E0=0, the strength is 0.125mA. - When E1=0/E0=1, the strength is 0.25mA. - When E1=1/E0=0, the strength is 0.5mA. - When E1=1/E0=1, the strength is 1mA. - EN is used to enable or disable the specific driving setup. - Valid arguments are described as below: - 0: (E1, E0, EN) = (0, 0, 0) - 1: (E1, E0, EN) = (0, 0, 1) - 2: (E1, E0, EN) = (0, 1, 0) - 3: (E1, E0, EN) = (0, 1, 1) - 4: (E1, E0, EN) = (1, 0, 0) - 5: (E1, E0, EN) = (1, 0, 1) - 6: (E1, E0, EN) = (1, 1, 0) - 7: (E1, E0, EN) = (1, 1, 1) - So the valid arguments are from 0 to 7. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3, 4, 5, 6, 7] - - mediatek,pull-up-adv: - description: | - Pull up settings for 2 pull resistors, R0 and R1. User can - configure those special pins. Valid arguments are described as below: - 0: (R1, R0) = (0, 0) which means R1 disabled and R0 disabled. - 1: (R1, R0) = (0, 1) which means R1 disabled and R0 enabled. - 2: (R1, R0) = (1, 0) which means R1 enabled and R0 disabled. - 3: (R1, R0) = (1, 1) which means R1 enabled and R0 enabled. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3] - - bias-pull-down: true - - bias-pull-up: true + drive-strength-microamp: + enum: [125, 250, 500, 1000] + + bias-pull-down: + oneOf: + - type: boolean + description: normal pull down. + - enum: [100, 101, 102, 103] + description: PUPD/R1/R0 pull down type. See MTK_PUPD_SET_R1R0_ + defines in dt-bindings/pinctrl/mt65xx.h. + - enum: [200, 201, 202, 203] + description: RSEL pull down type. See MTK_PULL_SET_RSEL_ + defines in dt-bindings/pinctrl/mt65xx.h. + + bias-pull-up: + oneOf: + - type: boolean + description: normal pull up. + - enum: [100, 101, 102, 103] + description: PUPD/R1/R0 pull up type. See MTK_PUPD_SET_R1R0_ + defines in dt-bindings/pinctrl/mt65xx.h. + - enum: [200, 201, 202, 203] + description: RSEL pull up type. See MTK_PULL_SET_RSEL_ + defines in dt-bindings/pinctrl/mt65xx.h. bias-disable: true diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8195.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8195.yaml index c5b755514c46..66fe17e9e4d3 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8195.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt8195.yaml @@ -29,6 +29,8 @@ properties: description: gpio valid number range. maxItems: 1 + gpio-line-names: true + reg: description: | Physical address base for gpio base registers. There are 8 GPIO @@ -49,7 +51,7 @@ properties: description: The interrupt outputs to sysirq. maxItems: 1 - mediatek,rsel_resistance_in_si_unit: + mediatek,rsel-resistance-in-si-unit: type: boolean description: | Identifying i2c pins pull up/down type which is RSEL. It can support @@ -98,31 +100,8 @@ patternProperties: drive-strength: enum: [2, 4, 6, 8, 10, 12, 14, 16] - mediatek,drive-strength-adv: - description: | - Describe the specific driving setup property. - For I2C pins, the existing generic driving setup can only support - 2/4/6/8/10/12/14/16mA driving. But in specific driving setup, they - can support 0.125/0.25/0.5/1mA adjustment. If we enable specific - driving setup, the existing generic setup will be disabled. - The specific driving setup is controlled by E1E0EN. - When E1=0/E0=0, the strength is 0.125mA. - When E1=0/E0=1, the strength is 0.25mA. - When E1=1/E0=0, the strength is 0.5mA. - When E1=1/E0=1, the strength is 1mA. - EN is used to enable or disable the specific driving setup. - Valid arguments are described as below: - 0: (E1, E0, EN) = (0, 0, 0) - 1: (E1, E0, EN) = (0, 0, 1) - 2: (E1, E0, EN) = (0, 1, 0) - 3: (E1, E0, EN) = (0, 1, 1) - 4: (E1, E0, EN) = (1, 0, 0) - 5: (E1, E0, EN) = (1, 0, 1) - 6: (E1, E0, EN) = (1, 1, 0) - 7: (E1, E0, EN) = (1, 1, 1) - So the valid arguments are from 0 to 7. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 3, 4, 5, 6, 7] + drive-strength-microamp: + enum: [125, 250, 500, 1000] bias-pull-down: oneOf: @@ -142,7 +121,7 @@ patternProperties: "MTK_PUPD_SET_R1R0_11" define in mt8195. For pull down type is RSEL, it can add RSEL define & resistance value(ohm) to set different resistance by identifying property - "mediatek,rsel_resistance_in_si_unit". + "mediatek,rsel-resistance-in-si-unit". It can support "MTK_PULL_SET_RSEL_000" & "MTK_PULL_SET_RSEL_001" & "MTK_PULL_SET_RSEL_010" & "MTK_PULL_SET_RSEL_011" & "MTK_PULL_SET_RSEL_100" & "MTK_PULL_SET_RSEL_101" @@ -161,7 +140,7 @@ patternProperties: }; An example of using si unit resistance value(ohm): &pio { - mediatek,rsel_resistance_in_si_unit; + mediatek,rsel-resistance-in-si-unit; } pincontroller { i2c0_pin { @@ -190,7 +169,7 @@ patternProperties: "MTK_PUPD_SET_R1R0_11" define in mt8195. For pull up type is RSEL, it can add RSEL define & resistance value(ohm) to set different resistance by identifying property - "mediatek,rsel_resistance_in_si_unit". + "mediatek,rsel-resistance-in-si-unit". It can support "MTK_PULL_SET_RSEL_000" & "MTK_PULL_SET_RSEL_001" & "MTK_PULL_SET_RSEL_010" & "MTK_PULL_SET_RSEL_011" & "MTK_PULL_SET_RSEL_100" & "MTK_PULL_SET_RSEL_101" @@ -209,7 +188,7 @@ patternProperties: }; An example of using si unit resistance value(ohm): &pio { - mediatek,rsel_resistance_in_si_unit; + mediatek,rsel-resistance-in-si-unit; } pincontroller { i2c0-pins { @@ -302,7 +281,7 @@ examples: pinmux = <PINMUX_GPIO8__FUNC_SDA0>, <PINMUX_GPIO9__FUNC_SCL0>; bias-disable; - mediatek,drive-strength-adv = <7>; + drive-strength-microamp = <1000>; }; }; }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8909-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8909-tlmm.yaml new file mode 100644 index 000000000000..e03530091478 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8909-tlmm.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,msm8909-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. MSM8909 TLMM block + +maintainers: + - Stephan Gerhold <stephan@gerhold.net> + +description: | + This binding describes the Top Level Mode Multiplexer (TLMM) block found + in the MSM8909 platform. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,msm8909-tlmm + + reg: + maxItems: 1 + + interrupts: true + interrupt-controller: true + '#interrupt-cells': true + gpio-controller: true + gpio-reserved-ranges: true + '#gpio-cells': true + gpio-ranges: true + wakeup-parent: true + +required: + - compatible + - reg + +additionalProperties: false + +patternProperties: + '-state$': + oneOf: + - $ref: "#/$defs/qcom-msm8909-tlmm-state" + - patternProperties: + ".*": + $ref: "#/$defs/qcom-msm8909-tlmm-state" + +$defs: + qcom-msm8909-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: "qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state" + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|10[0-9]|11[0-7])$" + - enum: [ sdc1_clk, sdc1_cmd, sdc1_data, sdc2_clk, sdc2_cmd, + sdc2_data, qdsd_clk, qdsd_cmd, qdsd_data0, qdsd_data1, + qdsd_data2, qdsd_data3 ] + minItems: 1 + maxItems: 16 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ adsp_ext, atest_bbrx0, atest_bbrx1, atest_char, atest_char0, + atest_char1, atest_char2, atest_char3, atest_combodac, + atest_gpsadc0, atest_gpsadc1, atest_wlan0, atest_wlan1, + bimc_dte0, bimc_dte1, blsp_i2c1, blsp_i2c2, blsp_i2c3, + blsp_i2c4, blsp_i2c5, blsp_i2c6, blsp_spi1, blsp_spi1_cs1, + blsp_spi1_cs2, blsp_spi1_cs3, blsp_spi2, blsp_spi2_cs1, + blsp_spi2_cs2, blsp_spi2_cs3, blsp_spi3, blsp_spi3_cs1, + blsp_spi3_cs2, blsp_spi3_cs3, blsp_spi4, blsp_spi5, blsp_spi6, + blsp_uart1, blsp_uart2, blsp_uim1, blsp_uim2, cam_mclk, + cci_async, cci_timer0, cci_timer1, cci_timer2, cdc_pdm0, + dbg_out, dmic0_clk, dmic0_data, ebi0_wrcdc, ebi2_a, ebi2_lcd, + ext_lpass, gcc_gp1_clk_a, gcc_gp1_clk_b, gcc_gp2_clk_a, + gcc_gp2_clk_b, gcc_gp3_clk_a, gcc_gp3_clk_b, gcc_plltest, gpio, + gsm0_tx, ldo_en, ldo_update, m_voc, mdp_vsync, modem_tsync, + nav_pps, nav_tsync, pa_indicator, pbs0, pbs1, pbs2, + pri_mi2s_data0_a, pri_mi2s_data0_b, pri_mi2s_data1_a, + pri_mi2s_data1_b, pri_mi2s_mclk_a, pri_mi2s_mclk_b, + pri_mi2s_sck_a, pri_mi2s_sck_b, pri_mi2s_ws_a, pri_mi2s_ws_b, + prng_rosc, pwr_crypto_enabled_a, pwr_crypto_enabled_b, + pwr_modem_enabled_a, pwr_modem_enabled_b, pwr_nav_enabled_a, + pwr_nav_enabled_b, qdss_cti_trig_in_a0, qdss_cti_trig_in_a1, + qdss_cti_trig_in_b0, qdss_cti_trig_in_b1, qdss_cti_trig_out_a0, + qdss_cti_trig_out_a1, qdss_cti_trig_out_b0, + qdss_cti_trig_out_b1, qdss_traceclk_a, qdss_tracectl_a, + qdss_tracedata_a, qdss_tracedata_b, sd_write, sec_mi2s, + smb_int, ssbi0, ssbi1, uim1_clk, uim1_data, uim1_present, + uim1_reset, uim2_clk, uim2_data, uim2_present, uim2_reset, + uim3_clk, uim3_data, uim3_present, uim3_reset, uim_batt, + wcss_bt, wcss_fm, wcss_wlan ] + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-strength: true + input-enable: true + output-high: true + output-low: true + + required: + - pins + - function + + additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pinctrl@1000000 { + compatible = "qcom,msm8909-tlmm"; + reg = <0x1000000 0x300000>; + interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 117>; + interrupt-controller; + #interrupt-cells = <2>; + + gpio-wo-subnode-state { + pins = "gpio1"; + function = "gpio"; + }; + + uart-w-subnodes-state { + rx { + pins = "gpio4"; + function = "blsp_uart1"; + bias-pull-up; + }; + + tx { + pins = "gpio5"; + function = "blsp_uart1"; + bias-disable; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml index 6f2efc3772cb..694898f382be 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml @@ -52,6 +52,7 @@ properties: - qcom,pmi8998-gpio - qcom,pmk8350-gpio - qcom,pmm8155au-gpio + - qcom,pmp8074-gpio - qcom,pmr735a-gpio - qcom,pmr735b-gpio - qcom,pms405-gpio @@ -158,6 +159,7 @@ allOf: compatible: contains: enum: + - qcom,pm8226-gpio - qcom,pm8350b-gpio - qcom,pm8950-gpio then: @@ -233,6 +235,7 @@ allOf: - qcom,pm8150b-gpio - qcom,pm8150l-gpio - qcom,pmc8180c-gpio + - qcom,pmp8074-gpio - qcom,pms405-gpio then: properties: @@ -415,6 +418,7 @@ $defs: - gpio1-gpio10 for pmi8994 - gpio1-gpio4 for pmk8350 - gpio1-gpio10 for pmm8155au + - gpio1-gpio12 for pmp8074 (holes on gpio1 and gpio12) - gpio1-gpio4 for pmr735a - gpio1-gpio4 for pmr735b - gpio1-gpio12 for pms405 (holes on gpio1, gpio9 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sc7280-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sc7280-lpass-lpi-pinctrl.yaml index d32ee32776e8..33d1d37fdf6d 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sc7280-lpass-lpi-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sc7280-lpass-lpi-pinctrl.yaml @@ -19,6 +19,11 @@ properties: compatible: const: qcom,sc7280-lpass-lpi-pinctrl + qcom,adsp-bypass-mode: + description: + Tells ADSP is in bypass mode. + type: boolean + reg: minItems: 2 maxItems: 2 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm6375-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm6375-tlmm.yaml new file mode 100644 index 000000000000..3908807a8339 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm6375-tlmm.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,sm6375-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SM6375 TLMM block + +maintainers: + - Konrad Dybcio <konrad.dybcio@somainline.org> + +description: | + This binding describes the Top Level Mode Multiplexer (TLMM) block found + in the SM6375 platform. + +allOf: + - $ref: "pinctrl.yaml#" + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,sm6375-tlmm + + reg: + maxItems: 1 + + interrupts: true + interrupt-controller: true + '#interrupt-cells': true + gpio-controller: true + gpio-reserved-ranges: true + '#gpio-cells': true + gpio-ranges: true + wakeup-parent: true + +required: + - compatible + - reg + +additionalProperties: false + +patternProperties: + '-state$': + oneOf: + - $ref: "#/$defs/qcom-sm6375-tlmm-state" + - patternProperties: + ".*": + $ref: "#/$defs/qcom-sm6375-tlmm-state" + +$defs: + qcom-sm6375-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: "qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state" + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-4][0-9]|15[0-6])$" + - enum: [ ufs_reset, sdc1_clk, sdc1_cmd, sdc1_data, sdc2_clk, + sdc2_cmd, sdc2_data ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ adsp_ext, agera_pll, atest_char, atest_char0, atest_char1, + atest_char2, atest_char3, atest_tsens, atest_tsens2, + atest_usb1, atest_usb10, atest_usb11, atest_usb12, + atest_usb13, atest_usb2, atest_usb20, atest_usb21, + atest_usb22, atest_usb23, audio_ref, btfm_slimbus, cam_mclk, + cci_async, cci_i2c, cci_timer0, cci_timer1, cci_timer2, + cci_timer3, cci_timer4, cri_trng, dbg_out, ddr_bist, + ddr_pxi0, ddr_pxi1, ddr_pxi2, ddr_pxi3, dp_hot, edp_lcd, + gcc_gp1, gcc_gp2, gcc_gp3, gp_pdm0, gp_pdm1, gp_pdm2, gpio, + gps_tx, ibi_i3c, jitter_bist, ldo_en, ldo_update, lpass_ext, + m_voc, mclk, mdp_vsync, mdp_vsync0, mdp_vsync1, mdp_vsync2, + mdp_vsync3, mi2s_0, mi2s_1, mi2s_2, mss_lte, nav_gpio, + nav_pps, pa_indicator, phase_flag0, phase_flag1, phase_flag10, + phase_flag11, phase_flag12, phase_flag13, phase_flag14, + phase_flag15, phase_flag16, phase_flag17, phase_flag18, + phase_flag19, phase_flag2, phase_flag20, phase_flag21, + phase_flag22, phase_flag23, phase_flag24, phase_flag25, + phase_flag26, phase_flag27, phase_flag28, phase_flag29, + phase_flag3, phase_flag30, phase_flag31, phase_flag4, + phase_flag5, phase_flag6, phase_flag7, phase_flag8, + phase_flag9, pll_bist, pll_bypassnl, pll_clk, pll_reset, + prng_rosc0, prng_rosc1, prng_rosc2, prng_rosc3, qdss_cti, + qdss_gpio, qdss_gpio0, qdss_gpio1, qdss_gpio10, qdss_gpio11, + qdss_gpio12, qdss_gpio13, qdss_gpio14, qdss_gpio15, + qdss_gpio2, qdss_gpio3, qdss_gpio4, qdss_gpio5, qdss_gpio6, + qdss_gpio7, qdss_gpio8, qdss_gpio9, qlink0_enable, + qlink0_request, qlink0_wmss, qlink1_enable, qlink1_request, + qlink1_wmss, qup00, qup01, qup02, qup10, qup11_f1, qup11_f2, + qup12, qup13_f1, qup13_f2, qup14, sd_write, sdc1_tb, sdc2_tb, + sp_cmu, tgu_ch0, tgu_ch1, tgu_ch2, tgu_ch3, tsense_pwm1, + tsense_pwm2, uim1_clk, uim1_data, uim1_present, uim1_reset, + uim2_clk, uim2_data, uim2_present, uim2_reset, usb2phy_ac, + usb_phy, vfr_1, vsense_trigger, wlan1_adc0, wlan1_adc1, + wlan2_adc0, wlan2_adc1 ] + + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-strength: true + input-enable: true + output-high: true + output-low: true + + required: + - pins + - function + + additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + pinctrl@500000 { + compatible = "qcom,sm6375-tlmm"; + reg = <0x00500000 0x800000>; + interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&tlmm 0 0 157>; + + gpio-wo-subnode-state { + pins = "gpio1"; + function = "gpio"; + }; + + uart-w-subnodes-state { + rx { + pins = "gpio18"; + function = "qup13_f2"; + bias-pull-up; + }; + + tx { + pins = "gpio19"; + function = "qup13_f2"; + bias-disable; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml index 2a57df75d832..4fc758fea7e6 100644 --- a/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml +++ b/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml @@ -45,6 +45,7 @@ properties: - renesas,pfc-r8a77995 # R-Car D3 - renesas,pfc-r8a779a0 # R-Car V3U - renesas,pfc-r8a779f0 # R-Car S4-8 + - renesas,pfc-r8a779g0 # R-Car V4H - renesas,pfc-sh73a0 # SH-Mobile AG5 reg: diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml new file mode 100644 index 000000000000..eac6245db7dc --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/renesas,rzv2m-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/V2M combined Pin and GPIO controller + +maintainers: + - Geert Uytterhoeven <geert+renesas@glider.be> + - Phil Edworthy <phil.edworthy@renesas.com> + +description: + The Renesas RZ/V2M SoC features a combined Pin and GPIO controller. + Pin multiplexing and GPIO configuration is performed on a per-pin basis. + Each port features up to 16 pins, each of them configurable for GPIO function + (port mode) or in alternate function mode. + Up to 8 different alternate function modes exist for each single pin. + +properties: + compatible: + const: renesas,r9a09g011-pinctrl # RZ/V2M + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + description: + The first cell contains the global GPIO port index, constructed using the + RZV2M_GPIO() helper macro in <dt-bindings/pinctrl/rzv2m-pinctrl.h> and the + second cell represents consumer flag as mentioned in ../gpio/gpio.txt + E.g. "RZV2M_GPIO(8, 1)" for P8_1. + + gpio-ranges: + maxItems: 1 + + interrupts: + description: INEXINT[0..38] corresponding to individual pin inputs. + maxItems: 39 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +additionalProperties: + anyOf: + - type: object + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + description: + Pin controller client devices use pin configuration subnodes (children + and grandchildren) for desired pin configuration. + Client device subnodes use below standard properties. + + properties: + phandle: true + pinmux: + description: + Values are constructed from GPIO port number, pin number, and + alternate function configuration number using the RZV2M_PORT_PINMUX() + helper macro in <dt-bindings/pinctrl/rzv2m-pinctrl.h>. + pins: true + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-strength-microamp: + # Superset of supported values + enum: [ 1600, 1800, 2000, 3200, 3800, 4000, 6400, 7800, 8000, + 9000, 9600, 11000, 12000, 13000, 18000 ] + slew-rate: + description: 0 is slow slew rate, 1 is fast slew rate + enum: [ 0, 1 ] + gpio-hog: true + gpios: true + output-high: true + output-low: true + line-name: true + + - type: object + properties: + phandle: true + + additionalProperties: + $ref: "#/additionalProperties/anyOf/0" + +allOf: + - $ref: "pinctrl.yaml#" + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - gpio-ranges + - interrupts + - clocks + - power-domains + - resets + +examples: + - | + #include <dt-bindings/pinctrl/rzv2m-pinctrl.h> + #include <dt-bindings/clock/r9a09g011-cpg.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pinctrl: pinctrl@b6250000 { + compatible = "renesas,r9a09g011-pinctrl"; + reg = <0xb6250000 0x800>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 352>; + interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD R9A09G011_PFC_PCLK>; + resets = <&cpg R9A09G011_PFC_PRESETN>; + power-domains = <&cpg>; + + i2c2_pins: i2c2 { + pinmux = <RZV2M_PORT_PINMUX(3, 8, 2)>, /* SDA */ + <RZV2M_PORT_PINMUX(3, 9, 2)>; /* SCL */ + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml index 335ffc1353b5..d35dcc4f0242 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml @@ -59,6 +59,7 @@ properties: patternProperties: '^gpio@[0-9a-f]*$': type: object + additionalProperties: false properties: gpio-controller: true '#gpio-cells': @@ -68,8 +69,7 @@ patternProperties: maxItems: 1 clocks: maxItems: 1 - reset: - minItems: 1 + resets: maxItems: 1 gpio-ranges: minItems: 1 diff --git a/Documentation/devicetree/bindings/pinctrl/sunplus,sp7021-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/sunplus,sp7021-pinctrl.yaml index d8e75b3e64f1..15092fdd4b5b 100644 --- a/Documentation/devicetree/bindings/pinctrl/sunplus,sp7021-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/sunplus,sp7021-pinctrl.yaml @@ -288,11 +288,14 @@ required: additionalProperties: false +allOf: + - $ref: "pinctrl.yaml#" + examples: - | #include <dt-bindings/pinctrl/sppctl-sp7021.h> - pinctl@9c000100 { + pinctrl@9c000100 { compatible = "sunplus,sp7021-pctl"; reg = <0x9c000100 0x100>, <0x9c000300 0x100>, <0x9c0032e4 0x1c>, <0x9c000080 0x20>; diff --git a/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml index 2722dc7bb03d..1e2b9b627b12 100644 --- a/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml @@ -274,6 +274,10 @@ patternProperties: slew-rate: enum: [0, 1] + output-enable: + description: + This will internally disable the tri-state for MIO pins. + drive-strength: description: Selects the drive strength for MIO pins, in mA. diff --git a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml index eec3b9c4c713..7e091eaffc18 100644 --- a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml +++ b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml @@ -18,6 +18,7 @@ properties: enum: - mediatek,mt8183-scp - mediatek,mt8186-scp + - mediatek,mt8188-scp - mediatek,mt8192-scp - mediatek,mt8195-scp @@ -80,6 +81,7 @@ allOf: enum: - mediatek,mt8183-scp - mediatek,mt8186-scp + - mediatek,mt8188-scp then: properties: reg: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml index 947f94548d0e..3072af5f9d79 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml @@ -67,13 +67,28 @@ properties: minItems: 1 maxItems: 8 + interconnects: + maxItems: 1 + interrupts: minItems: 5 - maxItems: 6 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt interrupt-names: minItems: 5 - maxItems: 6 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack resets: minItems: 1 @@ -116,7 +131,6 @@ properties: - description: Stop the modem qcom,smem-state-names: - $ref: /schemas/types.yaml#/definitions/string-array description: The names of the state bits used for SMP2P output items: - const: stop @@ -134,13 +148,13 @@ properties: three offsets within syscon for q6, modem and nc halt registers. smd-edge: - type: object + $ref: /schemas/remoteproc/qcom,smd-edge.yaml# description: Qualcomm Shared Memory subnode which represents communication edge, channels and devices related to the ADSP. glink-edge: - type: object + $ref: /schemas/remoteproc/qcom,glink-edge.yaml# description: Qualcomm G-Link subnode which represents communication edge, channels and devices related to the ADSP. @@ -315,19 +329,9 @@ allOf: then: properties: interrupts: - items: - - description: Watchdog interrupt - - description: Fatal interrupt - - description: Ready interrupt - - description: Handover interrupt - - description: Stop acknowledge interrupt + maxItems: 5 interrupt-names: - items: - - const: wdog - - const: fatal - - const: ready - - const: handover - - const: stop-ack + maxItems: 5 - if: properties: @@ -345,21 +349,9 @@ allOf: then: properties: interrupts: - items: - - description: Watchdog interrupt - - description: Fatal interrupt - - description: Ready interrupt - - description: Handover interrupt - - description: Stop acknowledge interrupt - - description: Shutdown acknowledge interrupt + minItems: 6 interrupt-names: - items: - - const: wdog - - const: fatal - - const: ready - - const: handover - - const: stop-ack - - const: shutdown-ack + minItems: 6 - if: properties: @@ -379,6 +371,8 @@ allOf: - qcom,msm8226-adsp-pil - qcom,msm8996-adsp-pil - qcom,msm8998-adsp-pas + - qcom,sm8150-adsp-pas + - qcom,sm8150-cdsp-pas then: properties: power-domains: @@ -447,19 +441,6 @@ allOf: compatible: contains: enum: - - qcom,sm8150-adsp-pas - - qcom,sm8150-cdsp-pas - then: - properties: - power-domains: - items: - - description: CX power domain - - - if: - properties: - compatible: - contains: - enum: - qcom,sc7280-mpss-pas - qcom,sdx55-mpss-pas - qcom,sm6350-mpss-pas @@ -594,11 +575,12 @@ allOf: examples: - | #include <dt-bindings/clock/qcom,rpmcc.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/irq.h> adsp { compatible = "qcom,msm8974-adsp-pil"; - interrupts-extended = <&intc 0 162 IRQ_TYPE_EDGE_RISING>, + interrupts-extended = <&intc GIC_SPI 162 IRQ_TYPE_EDGE_RISING>, <&adsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, <&adsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, <&adsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, @@ -620,7 +602,7 @@ examples: qcom,smem-state-names = "stop"; smd-edge { - interrupts = <0 156 IRQ_TYPE_EDGE_RISING>; + interrupts = <GIC_SPI 156 IRQ_TYPE_EDGE_RISING>; qcom,ipc = <&apcs 8 8>; qcom,smd-edge = <1>; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml new file mode 100644 index 000000000000..fa69f7b21eed --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,glink-edge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm G-Link Edge communication channel nodes + +maintainers: + - Bjorn Andersson <bjorn.andersson@linaro.org> + +description: + Qualcomm G-Link subnode represents communication edge, channels and devices + related to the remote processor. + +properties: + $nodename: + const: "glink-edge" + + apr: + $ref: /schemas/soc/qcom/qcom,apr.yaml# + description: + Qualcomm APR/GPR (Asynchronous/Generic Packet Router) + + fastrpc: + type: object + description: + See Documentation/devicetree/bindings/misc/qcom,fastrpc.txt + + interrupts: + maxItems: 1 + + label: + description: The names of the state bits used for SMP2P output + + mboxes: + maxItems: 1 + + qcom,remote-pid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + ID of the shared memory used by GLINK for communication with remote + processor. + +required: + - interrupts + - label + - mboxes + - qcom,remote-pid + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/mailbox/qcom-ipcc.h> + + remoteproc@8a00000 { + reg = <0x08a00000 0x10000>; + // ... + + glink-edge { + interrupts-extended = <&ipcc IPCC_CLIENT_WPSS + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_CLIENT_WPSS + IPCC_MPROC_SIGNAL_GLINK_QMP>; + + label = "wpss"; + qcom,remote-pid = <13>; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 658f96fbc4fe..d0ebd16ee0e1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -14,8 +14,6 @@ on the Qualcomm Hexagon core. "qcom,msm8974-mss-pil" "qcom,msm8996-mss-pil" "qcom,msm8998-mss-pil" - "qcom,sc7180-mss-pil" - "qcom,sc7280-mss-pil" "qcom,sdm845-mss-pil" - reg: @@ -47,8 +45,6 @@ on the Qualcomm Hexagon core. must be "wdog", "fatal", "ready", "handover", "stop-ack" qcom,msm8996-mss-pil: qcom,msm8998-mss-pil: - qcom,sc7180-mss-pil: - qcom,sc7280-mss-pil: qcom,sdm845-mss-pil: must be "wdog", "fatal", "ready", "handover", "stop-ack", "shutdown-ack" @@ -86,11 +82,6 @@ on the Qualcomm Hexagon core. qcom,msm8998-mss-pil: must be "iface", "bus", "mem", "xo", "gpll0_mss", "snoc_axi", "mnoc_axi", "qdss" - qcom,sc7180-mss-pil: - must be "iface", "bus", "xo", "snoc_axi", "mnoc_axi", - "nav" - qcom,sc7280-mss-pil: - must be "iface", "xo", "snoc_axi", "offline", "pka" qcom,sdm845-mss-pil: must be "iface", "bus", "mem", "xo", "gpll0_mss", "snoc_axi", "mnoc_axi", "prng" @@ -102,7 +93,7 @@ on the Qualcomm Hexagon core. reference to the list of 3 reset-controllers for the wcss sub-system reference to the list of 2 reset-controllers for the modem - sub-system on SC7180, SC7280, SDM845 SoCs + sub-system on SDM845 SoCs - reset-names: Usage: required @@ -111,7 +102,7 @@ on the Qualcomm Hexagon core. must be "wcss_aon_reset", "wcss_reset", "wcss_q6_reset" for the wcss sub-system must be "mss_restart", "pdc_reset" for the modem - sub-system on SC7180, SC7280, SDM845 SoCs + sub-system on SDM845 SoCs For devices where the mba and mpss sub-nodes are not specified, mba/mpss region should be referenced as follows: @@ -176,10 +167,6 @@ For the compatible string below the following supplies are required: qcom,msm8996-mss-pil: qcom,msm8998-mss-pil: must be "cx", "mx" - qcom,sc7180-mss-pil: - must be "cx", "mx", "mss" - qcom,sc7280-mss-pil: - must be "cx", "mss" qcom,sdm845-mss-pil: must be "cx", "mx", "mss" @@ -205,36 +192,6 @@ For the compatible string below the following supplies are required: Definition: a phandle reference to a syscon representing TCSR followed by the three offsets within syscon for q6, modem and nc halt registers. - a phandle reference to a syscon representing TCSR followed - by the four offsets within syscon for q6, modem, nc and vq6 - halt registers on SC7280 SoCs. - -For the compatible strings below the following phandle references are required: - "qcom,sc7180-mss-pil" -- qcom,spare-regs: - Usage: required - Value type: <prop-encoded-array> - Definition: a phandle reference to a syscon representing TCSR followed - by the offset within syscon for conn_box_spare0 register - used by the modem sub-system running on SC7180 SoC. - -For the compatible strings below the following phandle references are required: - "qcom,sc7280-mss-pil" -- qcom,ext-regs: - Usage: required - Value type: <prop-encoded-array> - Definition: two phandle references to syscons representing TCSR_REG and - TCSR register space followed by the two offsets within the syscon - to force_clk_en/rscc_disable and axim1_clk_off/crypto_clk_off - registers respectively. - -- qcom,qaccept-regs: - Usage: required - Value type: <prop-encoded-array> - Definition: a phandle reference to a syscon representing TCSR followed - by the three offsets within syscon for mdm, cx and axi - qaccept registers used by the modem sub-system running on - SC7280 SoC. The Hexagon node must contain iommus property as described in ../iommu/iommu.txt on platforms which do not have TrustZone. @@ -257,29 +214,23 @@ related to the Hexagon. See ../soc/qcom/qcom,smd.yaml and The following example describes the resources needed to boot control the Hexagon, as it is found on MSM8974 boards. - modem-rproc@fc880000 { - compatible = "qcom,q6v5-pil"; - reg = <0xfc880000 0x100>, - <0xfc820000 0x020>; + remoteproc@fc880000 { + compatible = "qcom,msm8974-mss-pil"; + reg = <0xfc880000 0x100>, <0xfc820000 0x020>; reg-names = "qdsp6", "rmb"; - interrupts-extended = <&intc 0 24 1>, - <&modem_smp2p_in 0 0>, - <&modem_smp2p_in 1 0>, - <&modem_smp2p_in 2 0>, - <&modem_smp2p_in 3 0>; - interrupt-names = "wdog", - "fatal", - "ready", - "handover", - "stop-ack"; + interrupts-extended = <&intc GIC_SPI 24 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; clocks = <&gcc GCC_MSS_Q6_BIMC_AXI_CLK>, <&gcc GCC_MSS_CFG_AHB_CLK>, - <&gcc GCC_BOOT_ROM_AHB_CLK>; - clock-names = "iface", "bus", "mem"; - - qcom,halt-regs = <&tcsr_mutex_block 0x1180 0x1200 0x1280>; + <&gcc GCC_BOOT_ROM_AHB_CLK>, + <&xo_board>; + clock-names = "iface", "bus", "mem", "xo"; resets = <&gcc GCC_MSS_RESTART>; reset-names = "mss_restart"; @@ -289,6 +240,8 @@ Hexagon, as it is found on MSM8974 boards. mx-supply = <&pm8841_s1>; pll-supply = <&pm8941_l12>; + qcom,halt-regs = <&tcsr_mutex_block 0x1180 0x1200 0x1280>; + qcom,smem-states = <&modem_smp2p_out 0>; qcom,smem-state-names = "stop"; @@ -299,4 +252,13 @@ Hexagon, as it is found on MSM8974 boards. mpss { memory-region = <&mpss_region>; }; + + smd-edge { + interrupts = <GIC_SPI 25 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&apcs 8 12>; + qcom,smd-edge = <0>; + + label = "modem"; + }; }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml index 31413cfe10db..06f5f93f62a9 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-cdsp-pil.yaml @@ -90,7 +90,6 @@ properties: - description: Stop the modem qcom,smem-state-names: - $ref: /schemas/types.yaml#/definitions/string description: The names of the state bits used for SMP2P output items: - const: stop diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml new file mode 100644 index 000000000000..e76c861165dd --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml @@ -0,0 +1,245 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,sc7180-mss-pil.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SC7180 MSS Peripheral Image Loader + +maintainers: + - Sibi Sankar <quic_sibis@quicinc.com> + +description: + This document describes the hardware for a component that loads and boots firmware + on the Qualcomm Technology Inc. SC7180 Modem Hexagon Core. + +properties: + compatible: + enum: + - qcom,sc7180-mss-pil + + reg: + items: + - description: MSS QDSP6 registers + - description: RMB registers + + reg-names: + items: + - const: qdsp6 + - const: rmb + + iommus: + items: + - description: MSA Stream 1 + - description: MSA Stream 2 + + interrupts: + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + clocks: + items: + - description: GCC MSS IFACE clock + - description: GCC MSS BUS clock + - description: GCC MSS NAV clock + - description: GCC MSS SNOC_AXI clock + - description: GCC MSS MFAB_AXIS clock + - description: RPMH XO clock + + clock-names: + items: + - const: iface + - const: bus + - const: nav + - const: snoc_axi + - const: mnoc_axi + - const: xo + + power-domains: + items: + - description: CX power domain + - description: MX power domain + - description: MSS power domain + + power-domain-names: + items: + - const: cx + - const: mx + - const: mss + + resets: + items: + - description: AOSS restart + - description: PDC reset + + reset-names: + items: + - const: mss_restart + - const: pdc_reset + + memory-region: + items: + - description: MBA reserved region + - description: modem reserved region + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string-array + items: + - description: Name of MBA firmware + - description: Name of modem firmware + + qcom,halt-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Halt registers are used to halt transactions of various sub-components + within MSS. + items: + - items: + - description: phandle to TCSR_MUTEX registers + - description: offset to the Q6 halt register + - description: offset to the modem halt register + - description: offset to the nc halt register + + qcom,spare-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Spare registers are multipurpose registers used for errata + handling. + items: + - items: + - description: phandle to TCSR_MUTEX registers + - description: offset to the conn_box_spare0 register + + qcom,qmp: + $ref: /schemas/types.yaml#/definitions/phandle + description: Reference to the AOSS side-channel message RAM. + + qcom,smem-states: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: States used by the AP to signal the Hexagon core + items: + - description: Stop the modem + + qcom,smem-state-names: + description: The names of the state bits used for SMP2P output + const: stop + + glink-edge: + $ref: qcom,glink-edge.yaml# + description: + Qualcomm G-Link subnode which represents communication edge, channels + and devices related to the DSP. + + properties: + interrupts: + items: + - description: IRQ from MSS to GLINK + + mboxes: + items: + - description: Mailbox for communication between APPS and MSS + + label: + const: modem + + apr: false + fastrpc: false + +required: + - compatible + - reg + - reg-names + - iommus + - interrupts + - interrupt-names + - clocks + - clock-names + - power-domains + - power-domain-names + - resets + - reset-names + - qcom,halt-regs + - qcom,spare-regs + - memory-region + - qcom,qmp + - qcom,smem-states + - qcom,smem-state-names + - glink-edge + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-sc7180.h> + #include <dt-bindings/clock/qcom,rpmh.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/qcom-rpmpd.h> + #include <dt-bindings/reset/qcom,sdm845-aoss.h> + #include <dt-bindings/reset/qcom,sdm845-pdc.h> + + remoteproc_mpss: remoteproc@4080000 { + compatible = "qcom,sc7180-mss-pil"; + reg = <0x04080000 0x10000>, <0x04180000 0x48>; + reg-names = "qdsp6", "rmb"; + + iommus = <&apps_smmu 0x461 0x0>, <&apps_smmu 0x444 0x3>; + + interrupts-extended = <&intc GIC_SPI 264 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 3 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 7 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "wdog", "fatal", "ready", "handover", + "stop-ack", "shutdown-ack"; + + clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, + <&gcc GCC_MSS_Q6_MEMNOC_AXI_CLK>, + <&gcc GCC_MSS_NAV_AXI_CLK>, + <&gcc GCC_MSS_SNOC_AXI_CLK>, + <&gcc GCC_MSS_MFAB_AXIS_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "bus", "nav", "snoc_axi", + "mnoc_axi", "xo"; + + power-domains = <&rpmhpd SC7180_CX>, + <&rpmhpd SC7180_MX>, + <&rpmhpd SC7180_MSS>; + power-domain-names = "cx", "mx", "mss"; + + memory-region = <&mba_mem>, <&mpss_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&modem_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + resets = <&aoss_reset AOSS_CC_MSS_RESTART>, + <&pdc_reset PDC_MODEM_SYNC_RESET>; + reset-names = "mss_restart", "pdc_reset"; + + qcom,halt-regs = <&tcsr_mutex_regs 0x23000 0x25000 0x24000>; + qcom,spare-regs = <&tcsr_regs 0xb3e4>; + + glink-edge { + interrupts = <GIC_SPI 449 IRQ_TYPE_EDGE_RISING>; + mboxes = <&apss_shared 12>; + qcom,remote-pid = <1>; + label = "modem"; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml new file mode 100644 index 000000000000..da1a5de3d38b --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml @@ -0,0 +1,266 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,sc7280-mss-pil.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SC7280 MSS Peripheral Image Loader + +maintainers: + - Sibi Sankar <quic_sibis@quicinc.com> + +description: + This document describes the hardware for a component that loads and boots firmware + on the Qualcomm Technology Inc. SC7280 Modem Hexagon Core. + +properties: + compatible: + enum: + - qcom,sc7280-mss-pil + + reg: + items: + - description: MSS QDSP6 registers + - description: RMB registers + + reg-names: + items: + - const: qdsp6 + - const: rmb + + iommus: + items: + - description: MSA Stream 1 + - description: MSA Stream 2 + + interconnects: + items: + - description: Path leading to system memory + + interrupts: + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + clocks: + items: + - description: GCC MSS IFACE clock + - description: GCC MSS OFFLINE clock + - description: GCC MSS SNOC_AXI clock + - description: RPMH PKA clock + - description: RPMH XO clock + + clock-names: + items: + - const: iface + - const: offline + - const: snoc_axi + - const: pka + - const: xo + + power-domains: + items: + - description: CX power domain + - description: MSS power domain + + power-domain-names: + items: + - const: cx + - const: mss + + resets: + items: + - description: AOSS restart + - description: PDC reset + + reset-names: + items: + - const: mss_restart + - const: pdc_reset + + memory-region: + items: + - description: MBA reserved region + - description: modem reserved region + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string-array + items: + - description: Name of MBA firmware + - description: Name of modem firmware + + qcom,halt-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Halt registers are used to halt transactions of various sub-components + within MSS. + items: + - items: + - description: phandle to TCSR_MUTEX registers + - description: offset to the Q6 halt register + - description: offset to the modem halt register + - description: offset to the nc halt register + - description: offset to the vq6 halt register + + qcom,ext-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: EXT registers are used for various power related functionality + items: + - items: + - description: phandle to TCSR_REG registers + - description: offset to the force_clk_en register + - description: offset to the rscc_disable register + - items: + - description: phandle to TCSR_MUTEX registers + - description: offset to the axim1_clk_off register + - description: offset to the crypto_clk_off register + + qcom,qaccept-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: QACCEPT registers are used to bring up/down Q-channels + items: + - items: + - description: phandle to TCSR_MUTEX registers + - description: offset to the mdm qaccept register + - description: offset to the cx qaccept register + - description: offset to the axi qaccept register + + qcom,qmp: + $ref: /schemas/types.yaml#/definitions/phandle + description: Reference to the AOSS side-channel message RAM. + + qcom,smem-states: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: States used by the AP to signal the Hexagon core + items: + - description: Stop the modem + + qcom,smem-state-names: + description: The names of the state bits used for SMP2P output + const: stop + + glink-edge: + $ref: qcom,glink-edge.yaml# + description: + Qualcomm G-Link subnode which represents communication edge, channels + and devices related to the DSP. + + properties: + interrupts: + items: + - description: IRQ from MSS to GLINK + + mboxes: + items: + - description: Mailbox for communication between APPS and MSS + + label: + const: modem + + apr: false + fastrpc: false + +required: + - compatible + - reg + - reg-names + - iommus + - interconnects + - interrupts + - interrupt-names + - clocks + - clock-names + - power-domains + - power-domain-names + - resets + - reset-names + - qcom,halt-regs + - qcom,ext-regs + - qcom,qaccept-regs + - memory-region + - qcom,qmp + - qcom,smem-states + - qcom,smem-state-names + - glink-edge + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-sc7280.h> + #include <dt-bindings/clock/qcom,rpmh.h> + #include <dt-bindings/interconnect/qcom,sc7280.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/mailbox/qcom-ipcc.h> + #include <dt-bindings/power/qcom-rpmpd.h> + #include <dt-bindings/reset/qcom,sdm845-aoss.h> + #include <dt-bindings/reset/qcom,sdm845-pdc.h> + + remoteproc_mpss: remoteproc@4080000 { + compatible = "qcom,sc7280-mss-pil"; + reg = <0x04080000 0x10000>, <0x04180000 0x48>; + reg-names = "qdsp6", "rmb"; + + iommus = <&apps_smmu 0x124 0x0>, <&apps_smmu 0x488 0x7>; + + interconnects = <&mc_virt MASTER_LLCC 0 &mc_virt SLAVE_EBI1 0>; + + interrupts-extended = <&intc GIC_SPI 264 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 3 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 7 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "wdog", "fatal", "ready", "handover", + "stop-ack", "shutdown-ack"; + + clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, + <&gcc GCC_MSS_OFFLINE_AXI_CLK>, + <&gcc GCC_MSS_SNOC_AXI_CLK>, + <&rpmhcc RPMH_PKA_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "offline", "snoc_axi", "pka", "xo"; + + power-domains = <&rpmhpd SC7280_CX>, + <&rpmhpd SC7280_MSS>; + power-domain-names = "cx", "mss"; + + memory-region = <&mba_mem>, <&mpss_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&modem_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + resets = <&aoss_reset AOSS_CC_MSS_RESTART>, + <&pdc_reset PDC_MODEM_SYNC_RESET>; + reset-names = "mss_restart", "pdc_reset"; + + qcom,halt-regs = <&tcsr_mutex 0x23000 0x25000 0x28000 0x33000>; + qcom,ext-regs = <&tcsr 0x10000 0x10004>, <&tcsr_mutex 0x26004 0x26008>; + qcom,qaccept-regs = <&tcsr_mutex 0x23030 0x23040 0x23020>; + + glink-edge { + interrupts-extended = <&ipcc IPCC_CLIENT_MPSS + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_CLIENT_MPSS + IPCC_MPROC_SIGNAL_GLINK_QMP>; + label = "modem"; + qcom,remote-pid = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml index d99a729d2710..3f06d66cbe47 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-wpss-pil.yaml @@ -76,7 +76,7 @@ properties: - const: pdc_sync memory-region: - $ref: /schemas/types.yaml#/definitions/phandle + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core firmware-name: @@ -102,13 +102,12 @@ properties: - description: Stop the modem qcom,smem-state-names: - $ref: /schemas/types.yaml#/definitions/string description: The names of the state bits used for SMP2P output const: stop glink-edge: - type: object - description: | + $ref: qcom,glink-edge.yaml# + description: Qualcomm G-Link subnode which represents communication edge, channels and devices related to the ADSP. @@ -122,21 +121,11 @@ properties: - description: Mailbox for communication between APPS and WPSS label: - description: The names of the state bits used for SMP2P output items: - const: wpss - qcom,remote-pid: - $ref: /schemas/types.yaml#/definitions/uint32 - description: ID of the shared memory used by GLINK for communication with WPSS - - required: - - interrupts - - mboxes - - label - - qcom,remote-pid - - additionalProperties: false + apr: false + fastrpc: false required: - compatible diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml index 1535bbbe25da..20df83a96ef3 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdm845-adsp-pil.yaml @@ -90,7 +90,6 @@ properties: - description: Stop the modem qcom,smem-state-names: - $ref: /schemas/types.yaml#/definitions/string description: The names of the state bits used for SMP2P output items: - const: stop diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml new file mode 100644 index 000000000000..06eebf791e32 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,smd-edge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SMD Edge communication channel nodes + +maintainers: + - Bjorn Andersson <bjorn.andersson@linaro.org> + +description: + Qualcomm SMD subnode represents a remote subsystem or a remote processor of + some sort - or in SMD language an "edge". The name of the edges are not + important. + See also Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml + +properties: + $nodename: + const: "smd-edge" + + interrupts: + maxItems: 1 + + label: + description: + Name of the edge, used for debugging and identification purposes. The + node name will be used if this is not present. + + mboxes: + maxItems: 1 + description: + Reference to the mailbox representing the outgoing doorbell in APCS for + this client. + + qcom,ipc: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to a syscon node representing the APCS registers + - description: u32 representing offset to the register within the syscon + - description: u32 representing the ipc bit within the register + description: + Three entries specifying the outgoing ipc bit used for signaling the + remote processor. + + qcom,smd-edge: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier of the remote processor in the smd channel allocation + table. + + qcom,remote-pid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier for the remote processor as known by the rest of the + system. + +required: + - interrupts + - qcom,smd-edge + +oneOf: + - required: + - mboxes + - required: + - qcom,ipc + +additionalProperties: true + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/mailbox/qcom-ipcc.h> + + remoteproc { + // ... + + smd-edge { + interrupts = <GIC_SPI 156 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&apcs 8 8>; + qcom,smd-edge = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml index d7c3a78e37e6..cd55d80137f7 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.yaml @@ -36,17 +36,18 @@ properties: enum: - ti,am3356-pru # for AM335x SoC family (AM3356+ SoCs only) - ti,am4376-pru # for AM437x SoC family (AM4376+ SoCs only) + - ti,am5728-pru # for AM57xx SoC family + - ti,am625-pru # for PRUs in K3 AM62x SoC family - ti,am642-pru # for PRUs in K3 AM64x SoC family - ti,am642-rtu # for RTUs in K3 AM64x SoC family - ti,am642-tx-pru # for Tx_PRUs in K3 AM64x SoC family - - ti,am5728-pru # for AM57xx SoC family - - ti,k2g-pru # for 66AK2G SoC family - ti,am654-pru # for PRUs in K3 AM65x SoC family - ti,am654-rtu # for RTUs in K3 AM65x SoC family - ti,am654-tx-pru # for Tx_PRUs in K3 AM65x SR2.0 SoCs - ti,j721e-pru # for PRUs in K3 J721E SoC family - ti,j721e-rtu # for RTUs in K3 J721E SoC family - ti,j721e-tx-pru # for Tx_PRUs in K3 J721E SoC family + - ti,k2g-pru # for 66AK2G SoC family reg: items: diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index 50f834563e19..09d5bfa920f2 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -92,12 +92,33 @@ examples: qcom,ipc = <&apcs 8 0>; qcom,smd-edge = <15>; - rpm-requests { - compatible = "qcom,rpm-msm8974"; - qcom,smd-channels = "rpm_requests"; + rpm-requests { + compatible = "qcom,rpm-msm8916"; + qcom,smd-channels = "rpm_requests"; + + clock-controller { + compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; + #clock-cells = <1>; + clocks = <&xo_board>; + clock-names = "xo"; + }; - /* Regulator nodes to follow */ + power-controller { + compatible = "qcom,msm8916-rpmpd"; + #power-domain-cells = <1>; + operating-points-v2 = <&rpmpd_opp_table>; + + rpmpd_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-1 { + opp-level = <1>; + }; + opp-2 { + opp-level = <2>; + }; + }; }; }; - }; -... + }; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml index bca07bb13ebf..9b3efe97f47c 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml @@ -21,51 +21,13 @@ properties: patternProperties: "^.*-edge|rpm$": - type: object + $ref: /schemas/remoteproc/qcom,smd-edge.yaml# description: Each subnode of the SMD node represents a remote subsystem or a remote processor of some sort - or in SMD language an "edge". The name of the edges are not important. properties: - interrupts: - maxItems: 1 - - label: - $ref: /schemas/types.yaml#/definitions/string - description: - Name of the edge, used for debugging and identification purposes. The - node name will be used if this is not present. - - mboxes: - maxItems: 1 - description: - Reference to the mailbox representing the outgoing doorbell in APCS for - this client. - - qcom,ipc: - $ref: /schemas/types.yaml#/definitions/phandle-array - items: - - items: - - description: phandle to a syscon node representing the APCS registers - - description: u32 representing offset to the register within the syscon - - description: u32 representing the ipc bit within the register - description: - Three entries specifying the outgoing ipc bit used for signaling the - remote processor. - - qcom,smd-edge: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - The identifier of the remote processor in the smd channel allocation - table. - - qcom,remote-pid: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - The identifier for the remote processor as known by the rest of the - system. - rpm-requests: type: object description: @@ -89,17 +51,7 @@ patternProperties: additionalProperties: true - required: - - interrupts - - qcom,smd-edge - - oneOf: - - required: - - mboxes - - required: - - qcom,ipc - - additionalProperties: false + unevaluatedProperties: false required: - compatible diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 2bd6b4a52637..d8ac0be36e6c 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -24,6 +24,7 @@ properties: - qcom,apss-wdt-sc8280xp - qcom,apss-wdt-sdm845 - qcom,apss-wdt-sdx55 + - qcom,apss-wdt-sdx65 - qcom,apss-wdt-sm6350 - qcom,apss-wdt-sm8150 - qcom,apss-wdt-sm8250 diff --git a/Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml b/Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml index 11b220a5e0f6..099245fe7b10 100644 --- a/Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml @@ -29,6 +29,7 @@ properties: - realtek,rtl8380-wdt - realtek,rtl8390-wdt - realtek,rtl9300-wdt + - realtek,rtl9310-wdt reg: maxItems: 1 diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst index db476bb170b6..5149ecdc53c7 100644 --- a/Documentation/driver-api/cxl/memory-devices.rst +++ b/Documentation/driver-api/cxl/memory-devices.rst @@ -362,6 +362,14 @@ CXL Core .. kernel-doc:: drivers/cxl/core/mbox.c :doc: cxl mbox +CXL Regions +----------- +.. kernel-doc:: drivers/cxl/core/region.c + :doc: cxl core region + +.. kernel-doc:: drivers/cxl/core/region.c + :identifiers: + External Interfaces =================== diff --git a/Documentation/fault-injection/fault-injection.rst b/Documentation/fault-injection/fault-injection.rst index eb9c2d9a4f5f..17779a2772e5 100644 --- a/Documentation/fault-injection/fault-injection.rst +++ b/Documentation/fault-injection/fault-injection.rst @@ -169,6 +169,13 @@ configuration of fault-injection capabilities. default is 'N', setting it to 'Y' will disable disconnect injection on the RPC server. +- /sys/kernel/debug/fail_sunrpc/ignore-cache-wait: + + Format: { 'Y' | 'N' } + + default is 'N', setting it to 'Y' will disable cache wait + injection on the RPC server. + - /sys/kernel/debug/fail_function/inject: Format: { 'function-name' | '!function-name' | '' } diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index a7173843a294..7fb398649f51 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -672,7 +672,7 @@ Future kconfig work Work on kconfig is welcomed on both areas of clarifying semantics and on evaluating the use of a full SAT solver for it. A full SAT solver can be desirable to enable more complex dependency mappings and / or queries, -for instance on possible use case for a SAT solver could be that of handling +for instance one possible use case for a SAT solver could be that of handling the current known recursive dependency issues. It is not known if this would address such issues but such evaluation is desirable. If support for a full SAT solver proves too complex or that it cannot address recursive dependency issues diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst index c9887f241c6c..0f731d9196b0 100644 --- a/Documentation/mm/highmem.rst +++ b/Documentation/mm/highmem.rst @@ -60,17 +60,40 @@ list shows them in order of preference of use. This function should be preferred, where feasible, over all the others. These mappings are thread-local and CPU-local, meaning that the mapping - can only be accessed from within this thread and the thread is bound the - CPU while the mapping is active. Even if the thread is preempted (since - preemption is never disabled by the function) the CPU can not be - unplugged from the system via CPU-hotplug until the mapping is disposed. + can only be accessed from within this thread and the thread is bound to the + CPU while the mapping is active. Although preemption is never disabled by + this function, the CPU can not be unplugged from the system via + CPU-hotplug until the mapping is disposed. It's valid to take pagefaults in a local kmap region, unless the context in which the local mapping is acquired does not allow it for other reasons. + As said, pagefaults and preemption are never disabled. There is no need to + disable preemption because, when context switches to a different task, the + maps of the outgoing task are saved and those of the incoming one are + restored. + kmap_local_page() always returns a valid virtual address and it is assumed that kunmap_local() will never fail. + On CONFIG_HIGHMEM=n kernels and for low memory pages this returns the + virtual address of the direct mapping. Only real highmem pages are + temporarily mapped. Therefore, users may call a plain page_address() + for pages which are known to not come from ZONE_HIGHMEM. However, it is + always safe to use kmap_local_page() / kunmap_local(). + + While it is significantly faster than kmap(), for the higmem case it + comes with restrictions about the pointers validity. Contrary to kmap() + mappings, the local mappings are only valid in the context of the caller + and cannot be handed to other contexts. This implies that users must + be absolutely sure to keep the use of the return address local to the + thread which mapped it. + + Most code can be designed to use thread local mappings. User should + therefore try to design their code to avoid the use of kmap() by mapping + pages in the same thread the address will be used and prefer + kmap_local_page(). + Nesting kmap_local_page() and kmap_atomic() mappings is allowed to a certain extent (up to KMAP_TYPE_NR) but their invocations have to be strictly ordered because the map implementation is stack based. See kmap_local_page() kdocs diff --git a/Documentation/mm/vmemmap_dedup.rst b/Documentation/mm/vmemmap_dedup.rst index c9c495f62d12..a4b12ff906c4 100644 --- a/Documentation/mm/vmemmap_dedup.rst +++ b/Documentation/mm/vmemmap_dedup.rst @@ -7,23 +7,25 @@ A vmemmap diet for HugeTLB and Device DAX HugeTLB ======= -The struct page structures (page structs) are used to describe a physical -page frame. By default, there is a one-to-one mapping from a page frame to -it's corresponding page struct. +This section is to explain how HugeTLB Vmemmap Optimization (HVO) works. + +The ``struct page`` structures are used to describe a physical page frame. By +default, there is a one-to-one mapping from a page frame to it's corresponding +``struct page``. HugeTLB pages consist of multiple base page size pages and is supported by many architectures. See Documentation/admin-guide/mm/hugetlbpage.rst for more details. On the x86-64 architecture, HugeTLB pages of size 2MB and 1GB are currently supported. Since the base page size on x86 is 4KB, a 2MB HugeTLB page consists of 512 base pages and a 1GB HugeTLB page consists of 4096 base pages. -For each base page, there is a corresponding page struct. +For each base page, there is a corresponding ``struct page``. -Within the HugeTLB subsystem, only the first 4 page structs are used to -contain unique information about a HugeTLB page. __NR_USED_SUBPAGE provides -this upper limit. The only 'useful' information in the remaining page structs +Within the HugeTLB subsystem, only the first 4 ``struct page`` are used to +contain unique information about a HugeTLB page. ``__NR_USED_SUBPAGE`` provides +this upper limit. The only 'useful' information in the remaining ``struct page`` is the compound_head field, and this field is the same for all tail pages. -By removing redundant page structs for HugeTLB pages, memory can be returned +By removing redundant ``struct page`` for HugeTLB pages, memory can be returned to the buddy allocator for other uses. Different architectures support different HugeTLB pages. For example, the @@ -44,7 +46,7 @@ page. | | 64KB | 2MB | 512MB | 16GB | | +--------------+-----------+-----------+-----------+-----------+-----------+ -When the system boot up, every HugeTLB page has more than one struct page +When the system boot up, every HugeTLB page has more than one ``struct page`` structs which size is (unit: pages):: struct_size = HugeTLB_Size / PAGE_SIZE * sizeof(struct page) / PAGE_SIZE @@ -74,10 +76,10 @@ Where n is how many pte entries which one page can contains. So the value of n is (PAGE_SIZE / sizeof(pte_t)). This optimization only supports 64-bit system, so the value of sizeof(pte_t) -is 8. And this optimization also applicable only when the size of struct page -is a power of two. In most cases, the size of struct page is 64 bytes (e.g. +is 8. And this optimization also applicable only when the size of ``struct page`` +is a power of two. In most cases, the size of ``struct page`` is 64 bytes (e.g. x86-64 and arm64). So if we use pmd level mapping for a HugeTLB page, the -size of struct page structs of it is 8 page frames which size depends on the +size of ``struct page`` structs of it is 8 page frames which size depends on the size of the base page. For the HugeTLB page of the pud level mapping, then:: @@ -86,7 +88,7 @@ For the HugeTLB page of the pud level mapping, then:: = PAGE_SIZE / 8 * 8 (pages) = PAGE_SIZE (pages) -Where the struct_size(pmd) is the size of the struct page structs of a +Where the struct_size(pmd) is the size of the ``struct page`` structs of a HugeTLB page of the pmd level mapping. E.g.: A 2MB HugeTLB page on x86_64 consists in 8 page frames while 1GB @@ -94,7 +96,7 @@ HugeTLB page consists in 4096. Next, we take the pmd level mapping of the HugeTLB page as an example to show the internal implementation of this optimization. There are 8 pages -struct page structs associated with a HugeTLB page which is pmd mapped. +``struct page`` structs associated with a HugeTLB page which is pmd mapped. Here is how things look before optimization:: @@ -122,10 +124,10 @@ Here is how things look before optimization:: +-----------+ The value of page->compound_head is the same for all tail pages. The first -page of page structs (page 0) associated with the HugeTLB page contains the 4 -page structs necessary to describe the HugeTLB. The only use of the remaining -pages of page structs (page 1 to page 7) is to point to page->compound_head. -Therefore, we can remap pages 1 to 7 to page 0. Only 1 page of page structs +page of ``struct page`` (page 0) associated with the HugeTLB page contains the 4 +``struct page`` necessary to describe the HugeTLB. The only use of the remaining +pages of ``struct page`` (page 1 to page 7) is to point to page->compound_head. +Therefore, we can remap pages 1 to 7 to page 0. Only 1 page of ``struct page`` will be used for each HugeTLB page. This will allow us to free the remaining 7 pages to the buddy allocator. @@ -167,13 +169,37 @@ entries that can be cached in a single TLB entry. The contiguous bit is used to increase the mapping size at the pmd and pte (last) level. So this type of HugeTLB page can be optimized only when its -size of the struct page structs is greater than 1 page. +size of the ``struct page`` structs is greater than **1** page. Notice: The head vmemmap page is not freed to the buddy allocator and all tail vmemmap pages are mapped to the head vmemmap page frame. So we can see -more than one struct page struct with PG_head (e.g. 8 per 2 MB HugeTLB page) -associated with each HugeTLB page. The compound_head() can handle this -correctly (more details refer to the comment above compound_head()). +more than one ``struct page`` struct with ``PG_head`` (e.g. 8 per 2 MB HugeTLB +page) associated with each HugeTLB page. The ``compound_head()`` can handle +this correctly. There is only **one** head ``struct page``, the tail +``struct page`` with ``PG_head`` are fake head ``struct page``. We need an +approach to distinguish between those two different types of ``struct page`` so +that ``compound_head()`` can return the real head ``struct page`` when the +parameter is the tail ``struct page`` but with ``PG_head``. The following code +snippet describes how to distinguish between real and fake head ``struct page``. + +.. code-block:: c + + if (test_bit(PG_head, &page->flags)) { + unsigned long head = READ_ONCE(page[1].compound_head); + + if (head & 1) { + if (head == (unsigned long)page + 1) + /* head struct page */ + else + /* tail struct page */ + } else { + /* head struct page */ + } + } + +We can safely access the field of the **page[1]** with ``PG_head`` because the +page is a compound page composed with at least two contiguous pages. +The implementation refers to ``page_fixed_fake_head()``. Device DAX ========== @@ -187,7 +213,7 @@ PMD_SIZE (2M on x86_64) and PUD_SIZE (1G on x86_64). The differences with HugeTLB are relatively minor. -It only use 3 page structs for storing all information as opposed +It only use 3 ``struct page`` for storing all information as opposed to 4 on HugeTLB pages. There's no remapping of vmemmap given that device-dax memory is not part of diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index 53a18ff7cf23..7823a069a903 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -1982,15 +1982,6 @@ uses the response as an indication that the link is operating. This gives some assurance that traffic is actually flowing to and from one or more peers on the local network. -The ARP monitor relies on the device driver itself to verify -that traffic is flowing. In particular, the driver must keep up to -date the last receive time, dev->last_rx. Drivers that use NETIF_F_LLTX -flag must also update netdev_queue->trans_start. If they do not, then the -ARP monitor will immediately fail any slaves using that driver, and -those slaves will stay down. If networking monitoring (tcpdump, etc) -shows the ARP requests and replies on the network, then it may be that -your device driver is not updating last_rx and trans_start. - 7.2 Configuring Multiple ARP Targets ------------------------------------ diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 9788b19f9ff7..abd7c32126ce 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8262,15 +8262,15 @@ dump related UV data. Also the vcpu ioctl `KVM_S390_PV_CPU_COMMAND` is available and supports the `KVM_PV_DUMP_CPU` subcommand. 8.38 KVM_CAP_VM_DISABLE_NX_HUGE_PAGES ---------------------------- +------------------------------------- -:Capability KVM_CAP_VM_DISABLE_NX_HUGE_PAGES +:Capability: KVM_CAP_VM_DISABLE_NX_HUGE_PAGES :Architectures: x86 :Type: vm :Parameters: arg[0] must be 0. -:Returns 0 on success, -EPERM if the userspace process does not - have CAP_SYS_BOOT, -EINVAL if args[0] is not 0 or any vCPUs have been - created. +:Returns: 0 on success, -EPERM if the userspace process does not + have CAP_SYS_BOOT, -EINVAL if args[0] is not 0 or any vCPUs have been + created. This capability disables the NX huge pages mitigation for iTLB MULTIHIT. diff --git a/MAINTAINERS b/MAINTAINERS index 32f51b6a51ff..f2d64020399b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1390,10 +1390,14 @@ F: include/uapi/linux/apm_bios.h APPARMOR SECURITY MODULE M: John Johansen <john.johansen@canonical.com> -L: apparmor@lists.ubuntu.com (subscribers-only, general discussion) +M: John Johansen <john@apparmor.net> +L: apparmor@lists.ubuntu.com (moderated for non-subscribers) S: Supported -W: wiki.apparmor.net +W: apparmor.net +B: https://gitlab.com/apparmor/apparmor-kernel +C: irc://irc.oftc.net/apparmor T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor +T: https://gitlab.com/apparmor/apparmor-kernel.git F: Documentation/admin-guide/LSM/apparmor.rst F: security/apparmor/ @@ -4783,7 +4787,6 @@ L: keyrings@vger.kernel.org S: Maintained F: Documentation/admin-guide/module-signing.rst F: certs/ -F: scripts/check-blacklist-hashes.awk F: scripts/sign-file.c F: tools/certs/ @@ -9691,7 +9694,7 @@ F: arch/powerpc/platforms/powernv/copy-paste.h F: arch/powerpc/platforms/powernv/vas* IBM Power Virtual Ethernet Device Driver -M: Cristobal Forno <cforno12@linux.ibm.com> +M: Nick Child <nnac123@linux.ibm.com> L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/ibm/ibmveth.* @@ -11063,6 +11066,7 @@ R: Sergey Senozhatsky <senozhatsky@chromium.org> L: linux-cifs@vger.kernel.org S: Maintained T: git git://git.samba.org/ksmbd.git +F: Documentation/filesystems/cifs/ksmbd.rst F: fs/ksmbd/ F: fs/smbfs_common/ @@ -12839,7 +12843,7 @@ F: Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml F: drivers/net/wireless/mediatek/mt76/ MEDIATEK MT7601U WIRELESS LAN DRIVER -M: Jakub Kicinski <kubakici@wp.pl> +M: Jakub Kicinski <kuba@kernel.org> L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mediatek/mt7601u/ @@ -13745,6 +13749,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git modules-next F: include/linux/module.h F: kernel/module/ +F: scripts/module* MONOLITHIC POWER SYSTEM PMIC DRIVER M: Saravanan Sekar <sravanhome@gmail.com> @@ -16056,6 +16061,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git F: Documentation/devicetree/bindings/pinctrl/ F: Documentation/driver-api/pin-control.rst F: drivers/pinctrl/ +F: include/dt-bindings/pinctrl/ F: include/linux/pinctrl/ PIN CONTROLLER - AMD @@ -129,6 +129,9 @@ endif $(if $(word 2, $(KBUILD_EXTMOD)), \ $(error building multiple external modules is not supported)) +$(foreach x, % :, $(if $(findstring $x, $(KBUILD_EXTMOD)), \ + $(error module directory path cannot contain '$x'))) + # Remove trailing slashes ifneq ($(filter %/, $(KBUILD_EXTMOD)),) KBUILD_EXTMOD := $(shell dirname $(KBUILD_EXTMOD).) @@ -755,8 +758,6 @@ KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE KBUILD_CFLAGS += -O2 -else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3 -KBUILD_CFLAGS += -O3 else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os endif @@ -1033,6 +1034,11 @@ KBUILD_CFLAGS += $(KCFLAGS) KBUILD_LDFLAGS_MODULE += --build-id=sha1 LDFLAGS_vmlinux += --build-id=sha1 +KBUILD_LDFLAGS += -z noexecstack +ifeq ($(CONFIG_LD_IS_BFD),y) +KBUILD_LDFLAGS += $(call ld-option,--no-warn-rwx-segments) +endif + ifeq ($(CONFIG_STRIP_ASM_SYMS),y) LDFLAGS_vmlinux += $(call ld-option, -X,) endif @@ -1370,16 +1376,21 @@ endif ifneq ($(dtstree),) -%.dtb: include/config/kernel.release scripts_dtc +%.dtb: dtbs_prepare $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@ -%.dtbo: include/config/kernel.release scripts_dtc +%.dtbo: dtbs_prepare $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@ -PHONY += dtbs dtbs_install dtbs_check -dtbs: include/config/kernel.release scripts_dtc +PHONY += dtbs dtbs_prepare dtbs_install dtbs_check +dtbs: dtbs_prepare $(Q)$(MAKE) $(build)=$(dtstree) +# include/config/kernel.release is actually needed when installing DTBs because +# INSTALL_DTBS_PATH contains $(KERNELRELEASE). However, we do not want to make +# dtbs_install depend on it as dtbs_install may run as root. +dtbs_prepare: include/config/kernel.release scripts_dtc + ifneq ($(filter dtbs_check, $(MAKECMDGOALS)),) export CHECK_DTBS=y dtbs: dt_binding_check diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig index 0016149f9583..e31a8ebc3ecc 100644 --- a/arch/arc/configs/axs101_defconfig +++ b/arch/arc/configs/axs101_defconfig @@ -9,7 +9,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig index 5b031582a1cf..e0e8567f0d75 100644 --- a/arch/arc/configs/axs103_defconfig +++ b/arch/arc/configs/axs103_defconfig @@ -9,7 +9,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig index d4eec39e0112..fcbc952bc75b 100644 --- a/arch/arc/configs/axs103_smp_defconfig +++ b/arch/arc/configs/axs103_smp_defconfig @@ -9,7 +9,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/haps_hs_defconfig b/arch/arc/configs/haps_hs_defconfig index 7337cdf4ffdd..d87ad7e88d62 100644 --- a/arch/arc/configs/haps_hs_defconfig +++ b/arch/arc/configs/haps_hs_defconfig @@ -11,7 +11,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set diff --git a/arch/arc/configs/haps_hs_smp_defconfig b/arch/arc/configs/haps_hs_smp_defconfig index bc927221afc0..8d82cdb7f86a 100644 --- a/arch/arc/configs/haps_hs_smp_defconfig +++ b/arch/arc/configs/haps_hs_smp_defconfig @@ -11,7 +11,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/hsdk_defconfig b/arch/arc/configs/hsdk_defconfig index aa000075a575..f856b03e0fb5 100644 --- a/arch/arc/configs/hsdk_defconfig +++ b/arch/arc/configs/hsdk_defconfig @@ -9,7 +9,6 @@ CONFIG_NAMESPACES=y # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_RAM=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/nsim_700_defconfig b/arch/arc/configs/nsim_700_defconfig index 326f6cde7826..a1ce12bf5b16 100644 --- a/arch/arc/configs/nsim_700_defconfig +++ b/arch/arc/configs/nsim_700_defconfig @@ -11,7 +11,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y diff --git a/arch/arc/configs/nsimosci_defconfig b/arch/arc/configs/nsimosci_defconfig index bf39a0091679..ca10f4a2c823 100644 --- a/arch/arc/configs/nsimosci_defconfig +++ b/arch/arc/configs/nsimosci_defconfig @@ -10,7 +10,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y diff --git a/arch/arc/configs/nsimosci_hs_defconfig b/arch/arc/configs/nsimosci_hs_defconfig index 7121bd71c543..31b6ec3683c6 100644 --- a/arch/arc/configs/nsimosci_hs_defconfig +++ b/arch/arc/configs/nsimosci_hs_defconfig @@ -10,7 +10,6 @@ CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y diff --git a/arch/arc/configs/nsimosci_hs_smp_defconfig b/arch/arc/configs/nsimosci_hs_smp_defconfig index f9863b294a70..41a0037f48a5 100644 --- a/arch/arc/configs/nsimosci_hs_smp_defconfig +++ b/arch/arc/configs/nsimosci_hs_smp_defconfig @@ -8,7 +8,6 @@ CONFIG_IKCONFIG_PROC=y # CONFIG_UTS_NS is not set # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig index a12656ec0072..d93b65008d4a 100644 --- a/arch/arc/configs/tb10x_defconfig +++ b/arch/arc/configs/tb10x_defconfig @@ -14,7 +14,6 @@ CONFIG_INITRAMFS_SOURCE="../tb10x-rootfs.cpio" CONFIG_INITRAMFS_ROOT_UID=2100 CONFIG_INITRAMFS_ROOT_GID=501 # CONFIG_RD_GZIP is not set -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_KALLSYMS_ALL=y # CONFIG_AIO is not set CONFIG_EMBEDDED=y diff --git a/arch/arc/configs/vdk_hs38_defconfig b/arch/arc/configs/vdk_hs38_defconfig index d7c858df520c..0c3b21416819 100644 --- a/arch/arc/configs/vdk_hs38_defconfig +++ b/arch/arc/configs/vdk_hs38_defconfig @@ -4,7 +4,6 @@ CONFIG_HIGH_RES_TIMERS=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/vdk_hs38_smp_defconfig b/arch/arc/configs/vdk_hs38_smp_defconfig index 015c1d43889e..f9ad9d3ee702 100644 --- a/arch/arc/configs/vdk_hs38_smp_defconfig +++ b/arch/arc/configs/vdk_hs38_smp_defconfig @@ -4,7 +4,6 @@ CONFIG_HIGH_RES_TIMERS=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_BLK_DEV_INITRD=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arm/boot/dts/imxrt1170-pinfunc.h b/arch/arm/boot/dts/imxrt1170-pinfunc.h new file mode 100644 index 000000000000..3b9fff2f08e1 --- /dev/null +++ b/arch/arm/boot/dts/imxrt1170-pinfunc.h @@ -0,0 +1,1561 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2021 + * Author(s): Jesse Taube <Mr.Bossman075@gmail.com> + */ + +#ifndef _DT_BINDINGS_PINCTRL_IMXRT1170_PINFUNC_H +#define _DT_BINDINGS_PINCTRL_IMXRT1170_PINFUNC_H + +#define IMX_PAD_SION 0x40000000 + +/* + * The pin function ID is a tuple of + * <mux_reg conf_reg input_reg mux_mode input_val> + */ + +#define IOMUXC_GPIO_LPSR_00_FLEXCAN3_TX 0x000 0x040 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_00_MIC_CLK 0x000 0x040 0x0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_00_MQS_RIGHT 0x000 0x040 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_00_ARM_CM4_EVENTO 0x000 0x040 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_00_GPIO_MUX6_IO00 0x000 0x040 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_00_LPUART12_TXD 0x000 0x040 0x0B0 0x6 0x0 +#define IOMUXC_GPIO_LPSR_00_SAI4_MCLK 0x000 0x040 0x0C8 0x7 0x0 +#define IOMUXC_GPIO_LPSR_00_GPIO12_IO00 0x000 0x040 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_01_FLEXCAN3_RX 0x004 0x044 0x080 0x0 0x0 +#define IOMUXC_GPIO_LPSR_01_MIC_BITSTREAM0 0x004 0x044 0x0B4 0x1 0x0 +#define IOMUXC_GPIO_LPSR_01_MQS_LEFT 0x004 0x044 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_01_ARM_CM4_EVENTI 0x004 0x044 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_01_GPIO_MUX6_IO01 0x004 0x044 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_01_LPUART12_RXD 0x004 0x044 0x0AC 0x6 0x0 +#define IOMUXC_GPIO_LPSR_01_GPIO12_IO01 0x004 0x044 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_02_GPIO12_IO02 0x008 0x048 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_02_SRC_BOOT_MODE00 0x008 0x048 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_02_LPSPI5_SCK 0x008 0x048 0x098 0x1 0x0 +#define IOMUXC_GPIO_LPSR_02_SAI4_TX_DATA 0x008 0x048 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_02_MQS_RIGHT 0x008 0x048 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_02_GPIO_MUX6_IO02 0x008 0x048 0x0 0x5 0x0 + +#define IOMUXC_GPIO_LPSR_03_SRC_BOOT_MODE01 0x00C 0x04C 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_03_LPSPI5_PCS0 0x00C 0x04C 0x094 0x1 0x0 +#define IOMUXC_GPIO_LPSR_03_SAI4_TX_SYNC 0x00C 0x04C 0x0DC 0x2 0x0 +#define IOMUXC_GPIO_LPSR_03_MQS_LEFT 0x00C 0x04C 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_03_GPIO_MUX6_IO03 0x00C 0x04C 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_03_GPIO12_IO03 0x00C 0x04C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_04_LPI2C5_SDA 0x010 0x050 0x088 0x0 0x0 +#define IOMUXC_GPIO_LPSR_04_LPSPI5_SOUT 0x010 0x050 0x0A0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_04_SAI4_TX_BCLK 0x010 0x050 0x0D8 0x2 0x0 +#define IOMUXC_GPIO_LPSR_04_LPUART12_RTS_B 0x010 0x050 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_04_GPIO_MUX6_IO04 0x010 0x050 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_04_LPUART11_TXD 0x010 0x050 0x0A8 0x6 0x0 +#define IOMUXC_GPIO_LPSR_04_GPIO12_IO04 0x010 0x050 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_05_GPIO12_IO05 0x014 0x054 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_05_LPI2C5_SCL 0x014 0x054 0x084 0x0 0x0 +#define IOMUXC_GPIO_LPSR_05_LPSPI5_SIN 0x014 0x054 0x09C 0x1 0x0 +#define IOMUXC_GPIO_LPSR_05_SAI4_MCLK 0x014 0x054 0x0C8 0x2 0x1 +#define IOMUXC_GPIO_LPSR_05_LPUART12_CTS_B 0x014 0x054 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_05_GPIO_MUX6_IO05 0x014 0x054 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_05_LPUART11_RXD 0x014 0x054 0x0A4 0x6 0x0 +#define IOMUXC_GPIO_LPSR_05_NMI_GLUE_NMI 0x014 0x054 0x0C4 0x7 0x0 + +#define IOMUXC_GPIO_LPSR_06_LPI2C6_SDA 0x018 0x058 0x090 0x0 0x0 +#define IOMUXC_GPIO_LPSR_06_SAI4_RX_DATA 0x018 0x058 0x0D0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_06_LPUART12_TXD 0x018 0x058 0x0B0 0x3 0x1 +#define IOMUXC_GPIO_LPSR_06_LPSPI6_PCS3 0x018 0x058 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_06_GPIO_MUX6_IO06 0x018 0x058 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_06_FLEXCAN3_TX 0x018 0x058 0x0 0x6 0x0 +#define IOMUXC_GPIO_LPSR_06_PIT2_TRIGGER3 0x018 0x058 0x0 0x7 0x0 +#define IOMUXC_GPIO_LPSR_06_LPSPI5_PCS1 0x018 0x058 0x0 0x8 0x0 +#define IOMUXC_GPIO_LPSR_06_GPIO12_IO06 0x018 0x058 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_07_LPI2C6_SCL 0x01C 0x05C 0x08C 0x0 0x0 +#define IOMUXC_GPIO_LPSR_07_SAI4_RX_BCLK 0x01C 0x05C 0x0CC 0x2 0x0 +#define IOMUXC_GPIO_LPSR_07_LPUART12_RXD 0x01C 0x05C 0x0AC 0x3 0x1 +#define IOMUXC_GPIO_LPSR_07_LPSPI6_PCS2 0x01C 0x05C 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_07_GPIO_MUX6_IO07 0x01C 0x05C 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_07_FLEXCAN3_RX 0x01C 0x05C 0x080 0x6 0x1 +#define IOMUXC_GPIO_LPSR_07_PIT2_TRIGGER2 0x01C 0x05C 0x0 0x7 0x0 +#define IOMUXC_GPIO_LPSR_07_LPSPI5_PCS2 0x01C 0x05C 0x0 0x8 0x0 +#define IOMUXC_GPIO_LPSR_07_GPIO12_IO07 0x01C 0x05C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_08_GPIO12_IO08 0x020 0x060 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_08_LPUART11_TXD 0x020 0x060 0x0A8 0x0 0x1 +#define IOMUXC_GPIO_LPSR_08_FLEXCAN3_TX 0x020 0x060 0x0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_08_SAI4_RX_SYNC 0x020 0x060 0x0D4 0x2 0x0 +#define IOMUXC_GPIO_LPSR_08_MIC_CLK 0x020 0x060 0x0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_08_LPSPI6_PCS1 0x020 0x060 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_08_GPIO_MUX6_IO08 0x020 0x060 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_08_LPI2C5_SDA 0x020 0x060 0x088 0x6 0x1 +#define IOMUXC_GPIO_LPSR_08_PIT2_TRIGGER1 0x020 0x060 0x0 0x7 0x0 +#define IOMUXC_GPIO_LPSR_08_LPSPI5_PCS3 0x020 0x060 0x0 0x8 0x0 + +#define IOMUXC_GPIO_LPSR_09_GPIO12_IO09 0x024 0x064 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_09_LPUART11_RXD 0x024 0x064 0x0A4 0x0 0x1 +#define IOMUXC_GPIO_LPSR_09_FLEXCAN3_RX 0x024 0x064 0x080 0x1 0x2 +#define IOMUXC_GPIO_LPSR_09_PIT2_TRIGGER0 0x024 0x064 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_09_MIC_BITSTREAM0 0x024 0x064 0x0B4 0x3 0x1 +#define IOMUXC_GPIO_LPSR_09_LPSPI6_PCS0 0x024 0x064 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_09_GPIO_MUX6_IO09 0x024 0x064 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_09_LPI2C5_SCL 0x024 0x064 0x084 0x6 0x1 +#define IOMUXC_GPIO_LPSR_09_SAI4_TX_DATA 0x024 0x064 0x0 0x7 0x0 + +#define IOMUXC_GPIO_LPSR_10_GPIO12_IO10 0x028 0x068 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_10_JTAG_MUX_TRSTB 0x028 0x068 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_10_LPUART11_CTS_B 0x028 0x068 0x0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_10_LPI2C6_SDA 0x028 0x068 0x090 0x2 0x1 +#define IOMUXC_GPIO_LPSR_10_MIC_BITSTREAM1 0x028 0x068 0x0B8 0x3 0x0 +#define IOMUXC_GPIO_LPSR_10_LPSPI6_SCK 0x028 0x068 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_10_GPIO_MUX6_IO10 0x028 0x068 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_10_LPI2C5_SCLS 0x028 0x068 0x0 0x6 0x0 +#define IOMUXC_GPIO_LPSR_10_SAI4_TX_SYNC 0x028 0x068 0x0DC 0x7 0x1 +#define IOMUXC_GPIO_LPSR_10_LPUART12_TXD 0x028 0x068 0x0B0 0x8 0x2 + +#define IOMUXC_GPIO_LPSR_11_JTAG_MUX_TDO 0x02C 0x06C 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_11_LPUART11_RTS_B 0x02C 0x06C 0x0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_11_LPI2C6_SCL 0x02C 0x06C 0x08C 0x2 0x1 +#define IOMUXC_GPIO_LPSR_11_MIC_BITSTREAM2 0x02C 0x06C 0x0BC 0x3 0x0 +#define IOMUXC_GPIO_LPSR_11_LPSPI6_SOUT 0x02C 0x06C 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_11_GPIO_MUX6_IO11 0x02C 0x06C 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_11_LPI2C5_SDAS 0x02C 0x06C 0x0 0x6 0x0 +#define IOMUXC_GPIO_LPSR_11_ARM_TRACE_SWO 0x02C 0x06C 0x0 0x7 0x0 +#define IOMUXC_GPIO_LPSR_11_LPUART12_RXD 0x02C 0x06C 0x0AC 0x8 0x2 +#define IOMUXC_GPIO_LPSR_11_GPIO12_IO11 0x02C 0x06C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_12_GPIO12_IO12 0x030 0x070 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_12_JTAG_MUX_TDI 0x030 0x070 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_12_PIT2_TRIGGER0 0x030 0x070 0x0 0x1 0x0 +#define IOMUXC_GPIO_LPSR_12_MIC_BITSTREAM3 0x030 0x070 0x0C0 0x3 0x0 +#define IOMUXC_GPIO_LPSR_12_LPSPI6_SIN 0x030 0x070 0x0 0x4 0x0 +#define IOMUXC_GPIO_LPSR_12_GPIO_MUX6_IO12 0x030 0x070 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_12_LPI2C5_HREQ 0x030 0x070 0x0 0x6 0x0 +#define IOMUXC_GPIO_LPSR_12_SAI4_TX_BCLK 0x030 0x070 0x0D8 0x7 0x1 +#define IOMUXC_GPIO_LPSR_12_LPSPI5_SCK 0x030 0x070 0x098 0x8 0x1 + +#define IOMUXC_GPIO_LPSR_13_GPIO12_IO13 0x034 0x074 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_13_JTAG_MUX_MOD 0x034 0x074 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_13_MIC_BITSTREAM1 0x034 0x074 0x0B8 0x1 0x1 +#define IOMUXC_GPIO_LPSR_13_PIT2_TRIGGER1 0x034 0x074 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_13_GPIO_MUX6_IO13 0x034 0x074 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_13_SAI4_RX_DATA 0x034 0x074 0x0D0 0x7 0x1 +#define IOMUXC_GPIO_LPSR_13_LPSPI5_PCS0 0x034 0x074 0x094 0x8 0x1 + +#define IOMUXC_GPIO_LPSR_14_JTAG_MUX_TCK 0x038 0x078 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_14_MIC_BITSTREAM2 0x038 0x078 0x0BC 0x1 0x1 +#define IOMUXC_GPIO_LPSR_14_PIT2_TRIGGER2 0x038 0x078 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_14_GPIO_MUX6_IO14 0x038 0x078 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_14_SAI4_RX_BCLK 0x038 0x078 0x0CC 0x7 0x1 +#define IOMUXC_GPIO_LPSR_14_LPSPI5_SOUT 0x038 0x078 0x0A0 0x8 0x1 +#define IOMUXC_GPIO_LPSR_14_GPIO12_IO14 0x038 0x078 0x0 0xA 0x0 + +#define IOMUXC_GPIO_LPSR_15_GPIO12_IO15 0x03C 0x07C 0x0 0xA 0x0 +#define IOMUXC_GPIO_LPSR_15_JTAG_MUX_TMS 0x03C 0x07C 0x0 0x0 0x0 +#define IOMUXC_GPIO_LPSR_15_MIC_BITSTREAM3 0x03C 0x07C 0x0C0 0x1 0x1 +#define IOMUXC_GPIO_LPSR_15_PIT2_TRIGGER3 0x03C 0x07C 0x0 0x2 0x0 +#define IOMUXC_GPIO_LPSR_15_GPIO_MUX6_IO15 0x03C 0x07C 0x0 0x5 0x0 +#define IOMUXC_GPIO_LPSR_15_SAI4_RX_SYNC 0x03C 0x07C 0x0D4 0x7 0x1 +#define IOMUXC_GPIO_LPSR_15_LPSPI5_SIN 0x03C 0x07C 0x09C 0x8 0x1 + +#define IOMUXC_WAKEUP_DIG_GPIO13_IO00 0x40C94000 0x40C94040 0x0 0x5 0x0 +#define IOMUXC_WAKEUP_DIG_NMI_GLUE_NMI 0x40C94000 0x40C94040 0x0C4 0x7 0x1 + +#define IOMUXC_PMIC_ON_REQ_DIG_SNVS_LP_PMIC_ON_REQ 0x40C94004 0x40C94044 0x0 0x0 0x0 +#define IOMUXC_PMIC_ON_REQ_DIG_GPIO13_IO01 0x40C94004 0x40C94044 0x0 0x5 0x0 + +#define IOMUXC_PMIC_STBY_REQ_DIG_CCM_PMIC_VSTBY_REQ 0x40C94008 0x40C94048 0x0 0x0 0x0 +#define IOMUXC_PMIC_STBY_REQ_DIG_GPIO13_IO02 0x40C94008 0x40C94048 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_00_DIG_SNVS_TAMPER0 0x40C9400C 0x40C9404C 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_00_DIG_GPIO13_IO03 0x40C9400C 0x40C9404C 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_01_DIG_SNVS_TAMPER1 0x40C94010 0x40C94050 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_01_DIG_GPIO13_IO04 0x40C94010 0x40C94050 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_02_DIG_SNVS_TAMPER2 0x40C94014 0x40C94054 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_02_DIG_GPIO13_IO05 0x40C94014 0x40C94054 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_03_DIG_SNVS_TAMPER3 0x40C94018 0x40C94058 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_03_DIG_GPIO13_IO06 0x40C94018 0x40C94058 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_04_DIG_SNVS_TAMPER4 0x40C9401C 0x40C9405C 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_04_DIG_GPIO13_IO07 0x40C9401C 0x40C9405C 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_05_DIG_SNVS_TAMPER5 0x40C94020 0x40C94060 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_05_DIG_GPIO13_IO08 0x40C94020 0x40C94060 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_06_DIG_SNVS_TAMPER6 0x40C94024 0x40C94064 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_06_DIG_GPIO13_IO09 0x40C94024 0x40C94064 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_07_DIG_SNVS_TAMPER7 0x40C94028 0x40C94068 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_07_DIG_GPIO13_IO10 0x40C94028 0x40C94068 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_08_DIG_SNVS_TAMPER8 0x40C9402C 0x40C9406C 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_08_DIG_GPIO13_IO11 0x40C9402C 0x40C9406C 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SNVS_09_DIG_SNVS_TAMPER9 0x40C94030 0x40C94070 0x0 0x0 0x0 +#define IOMUXC_GPIO_SNVS_09_DIG_GPIO13_IO12 0x40C94030 0x40C94070 0x0 0x5 0x0 + +#define IOMUXC_TEST_MODE_DIG 0x0 0x40C94034 0x0 0x0 0x0 + +#define IOMUXC_POR_B_DIG 0x0 0x40C94038 0x0 0x0 0x0 + +#define IOMUXC_ONOFF_DIG 0x0 0x40C9403C 0x0 0x0 0x0 + +#define IOMUXC_GPIO_EMC_B1_00_SEMC_DATA00 0x010 0x254 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_00_FLEXPWM4_PWM0_A 0x010 0x254 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_00_GPIO_MUX1_IO00 0x010 0x254 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_00_FLEXIO1_D00 0x010 0x254 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_00_GPIO7_IO00 0x010 0x254 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_01_GPIO7_IO01 0x014 0x258 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_01_SEMC_DATA01 0x014 0x258 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_01_FLEXPWM4_PWM0_B 0x014 0x258 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_01_GPIO_MUX1_IO01 0x014 0x258 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_01_FLEXIO1_D01 0x014 0x258 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_02_SEMC_DATA02 0x018 0x25C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_02_FLEXPWM4_PWM1_A 0x018 0x25C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_02_GPIO_MUX1_IO02 0x018 0x25C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_02_FLEXIO1_D02 0x018 0x25C 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_02_GPIO7_IO02 0x018 0x25C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_03_SEMC_DATA03 0x01C 0x260 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_03_FLEXPWM4_PWM1_B 0x01C 0x260 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_03_GPIO_MUX1_IO03 0x01C 0x260 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_03_FLEXIO1_D03 0x01C 0x260 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_03_GPIO7_IO03 0x01C 0x260 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_04_GPIO7_IO04 0x020 0x264 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_04_SEMC_DATA04 0x020 0x264 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_04_FLEXPWM4_PWM2_A 0x020 0x264 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_04_GPIO_MUX1_IO04 0x020 0x264 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_04_FLEXIO1_D04 0x020 0x264 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_05_SEMC_DATA05 0x024 0x268 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_05_FLEXPWM4_PWM2_B 0x024 0x268 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_05_GPIO_MUX1_IO05 0x024 0x268 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_05_FLEXIO1_D05 0x024 0x268 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_05_GPIO7_IO05 0x024 0x268 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_06_SEMC_DATA06 0x028 0x26C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_06_FLEXPWM2_PWM0_A 0x028 0x26C 0x518 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_06_GPIO_MUX1_IO06 0x028 0x26C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_06_FLEXIO1_D06 0x028 0x26C 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_06_GPIO7_IO06 0x028 0x26C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_07_GPIO7_IO07 0x02C 0x270 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_07_SEMC_DATA07 0x02C 0x270 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_07_FLEXPWM2_PWM0_B 0x02C 0x270 0x524 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_07_GPIO_MUX1_IO07 0x02C 0x270 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_07_FLEXIO1_D07 0x02C 0x270 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_08_SEMC_DM00 0x030 0x274 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_08_FLEXPWM2_PWM1_A 0x030 0x274 0x51C 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_08_GPIO_MUX1_IO08 0x030 0x274 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_08_FLEXIO1_D08 0x030 0x274 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_08_GPIO7_IO08 0x030 0x274 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_09_SEMC_ADDR00 0x034 0x278 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_09_FLEXPWM2_PWM1_B 0x034 0x278 0x528 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_09_GPT5_CAPTURE1 0x034 0x278 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_09_GPIO_MUX1_IO09 0x034 0x278 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_09_FLEXIO1_D09 0x034 0x278 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_09_GPIO7_IO09 0x034 0x278 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_10_SEMC_ADDR01 0x038 0x27C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_10_FLEXPWM2_PWM2_A 0x038 0x27C 0x520 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_10_GPT5_CAPTURE2 0x038 0x27C 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_10_GPIO_MUX1_IO10 0x038 0x27C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_10_FLEXIO1_D10 0x038 0x27C 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_10_GPIO7_IO10 0x038 0x27C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_11_GPIO7_IO11 0x03C 0x280 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_11_SEMC_ADDR02 0x03C 0x280 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_11_FLEXPWM2_PWM2_B 0x03C 0x280 0x52C 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_11_GPT5_COMPARE1 0x03C 0x280 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_11_GPIO_MUX1_IO11 0x03C 0x280 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_11_FLEXIO1_D11 0x03C 0x280 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_12_SEMC_ADDR03 0x040 0x284 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_12_XBAR1_INOUT04 0x040 0x284 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_12_GPT5_COMPARE2 0x040 0x284 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_12_GPIO_MUX1_IO12 0x040 0x284 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_12_FLEXIO1_D12 0x040 0x284 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_12_GPIO7_IO12 0x040 0x284 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_13_SEMC_ADDR04 0x044 0x288 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_13_XBAR1_INOUT05 0x044 0x288 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_13_GPT5_COMPARE3 0x044 0x288 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_13_GPIO_MUX1_IO13 0x044 0x288 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_13_FLEXIO1_D13 0x044 0x288 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_13_GPIO7_IO13 0x044 0x288 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_14_GPIO7_IO14 0x048 0x28C 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_14_SEMC_ADDR05 0x048 0x28C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_14_XBAR1_INOUT06 0x048 0x28C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_14_GPT5_CLK 0x048 0x28C 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_14_GPIO_MUX1_IO14 0x048 0x28C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_14_FLEXIO1_D14 0x048 0x28C 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_15_SEMC_ADDR06 0x04C 0x290 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_15_XBAR1_INOUT07 0x04C 0x290 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_15_GPIO_MUX1_IO15 0x04C 0x290 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_15_FLEXIO1_D15 0x04C 0x290 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_15_GPIO7_IO15 0x04C 0x290 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_16_SEMC_ADDR07 0x050 0x294 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_16_XBAR1_INOUT08 0x050 0x294 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_16_GPIO_MUX1_IO16 0x050 0x294 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_16_FLEXIO1_D16 0x050 0x294 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_16_GPIO7_IO16 0x050 0x294 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_17_GPIO7_IO17 0x054 0x298 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_17_SEMC_ADDR08 0x054 0x298 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_17_FLEXPWM4_PWM3_A 0x054 0x298 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_17_TMR1_TIMER0 0x054 0x298 0x63C 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_17_GPIO_MUX1_IO17 0x054 0x298 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_17_FLEXIO1_D17 0x054 0x298 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_18_SEMC_ADDR09 0x058 0x29C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_18_FLEXPWM4_PWM3_B 0x058 0x29C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_18_TMR2_TIMER0 0x058 0x29C 0x648 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_18_GPIO_MUX1_IO18 0x058 0x29C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_18_FLEXIO1_D18 0x058 0x29C 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_18_GPIO7_IO18 0x058 0x29C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_19_SEMC_ADDR11 0x05C 0x2A0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_19_FLEXPWM2_PWM3_A 0x05C 0x2A0 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_19_TMR3_TIMER0 0x05C 0x2A0 0x654 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_19_GPIO_MUX1_IO19 0x05C 0x2A0 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_19_FLEXIO1_D19 0x05C 0x2A0 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_19_GPIO7_IO19 0x05C 0x2A0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_20_SEMC_ADDR12 0x060 0x2A4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_20_FLEXPWM2_PWM3_B 0x060 0x2A4 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_20_TMR4_TIMER0 0x060 0x2A4 0x660 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_20_GPIO_MUX1_IO20 0x060 0x2A4 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_20_FLEXIO1_D20 0x060 0x2A4 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_20_GPIO7_IO20 0x060 0x2A4 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_21_GPIO7_IO21 0x064 0x2A8 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_21_SEMC_BA0 0x064 0x2A8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_21_FLEXPWM3_PWM3_A 0x064 0x2A8 0x53C 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_21_GPIO_MUX1_IO21 0x064 0x2A8 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_21_FLEXIO1_D21 0x064 0x2A8 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_22_GPIO7_IO22 0x068 0x2AC 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_22_SEMC_BA1 0x068 0x2AC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_22_FLEXPWM3_PWM3_B 0x068 0x2AC 0x54C 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_22_GPIO_MUX1_IO22 0x068 0x2AC 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_22_FLEXIO1_D22 0x068 0x2AC 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_23_SEMC_ADDR10 0x06C 0x2B0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_23_FLEXPWM1_PWM0_A 0x06C 0x2B0 0x500 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_23_GPIO_MUX1_IO23 0x06C 0x2B0 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_23_FLEXIO1_D23 0x06C 0x2B0 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_23_GPIO7_IO23 0x06C 0x2B0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_24_GPIO7_IO24 0x070 0x2B4 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_24_SEMC_CAS 0x070 0x2B4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_24_FLEXPWM1_PWM0_B 0x070 0x2B4 0x50C 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_24_GPIO_MUX1_IO24 0x070 0x2B4 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_24_FLEXIO1_D24 0x070 0x2B4 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_25_GPIO7_IO25 0x074 0x2B8 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_25_SEMC_RAS 0x074 0x2B8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_25_FLEXPWM1_PWM1_A 0x074 0x2B8 0x504 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_25_GPIO_MUX1_IO25 0x074 0x2B8 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_25_FLEXIO1_D25 0x074 0x2B8 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_26_SEMC_CLK 0x078 0x2BC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_26_FLEXPWM1_PWM1_B 0x078 0x2BC 0x510 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_26_GPIO_MUX1_IO26 0x078 0x2BC 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_26_FLEXIO1_D26 0x078 0x2BC 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_26_GPIO7_IO26 0x078 0x2BC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_27_GPIO7_IO27 0x07C 0x2C0 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_27_SEMC_CKE 0x07C 0x2C0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_27_FLEXPWM1_PWM2_A 0x07C 0x2C0 0x508 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_27_GPIO_MUX1_IO27 0x07C 0x2C0 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_27_FLEXIO1_D27 0x07C 0x2C0 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_28_GPIO7_IO28 0x080 0x2C4 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_28_SEMC_WE 0x080 0x2C4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_28_FLEXPWM1_PWM2_B 0x080 0x2C4 0x514 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_28_GPIO_MUX1_IO28 0x080 0x2C4 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_28_FLEXIO1_D28 0x080 0x2C4 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_29_SEMC_CS0 0x084 0x2C8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_29_FLEXPWM3_PWM0_A 0x084 0x2C8 0x530 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_29_GPIO_MUX1_IO29 0x084 0x2C8 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_29_FLEXIO1_D29 0x084 0x2C8 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_29_GPIO7_IO29 0x084 0x2C8 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_30_SEMC_DATA08 0x088 0x2CC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_30_FLEXPWM3_PWM0_B 0x088 0x2CC 0x540 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_30_GPIO_MUX1_IO30 0x088 0x2CC 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_30_FLEXIO1_D30 0x088 0x2CC 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B1_30_GPIO7_IO30 0x088 0x2CC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_31_GPIO7_IO31 0x08C 0x2D0 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_31_SEMC_DATA09 0x08C 0x2D0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_31_FLEXPWM3_PWM1_A 0x08C 0x2D0 0x534 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_31_GPIO_MUX1_IO31 0x08C 0x2D0 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_31_FLEXIO1_D31 0x08C 0x2D0 0x0 0x8 0x0 + +#define IOMUXC_GPIO_EMC_B1_32_GPIO8_IO00 0x090 0x2D4 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_32_SEMC_DATA10 0x090 0x2D4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_32_FLEXPWM3_PWM1_B 0x090 0x2D4 0x544 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_32_GPIO_MUX2_IO00 0x090 0x2D4 0x0 0x5 0x0 + +#define IOMUXC_GPIO_EMC_B1_33_SEMC_DATA11 0x094 0x2D8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_33_FLEXPWM3_PWM2_A 0x094 0x2D8 0x538 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_33_GPIO_MUX2_IO01 0x094 0x2D8 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_33_GPIO8_IO01 0x094 0x2D8 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_34_GPIO8_IO02 0x098 0x2DC 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_34_SEMC_DATA12 0x098 0x2DC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_34_FLEXPWM3_PWM2_B 0x098 0x2DC 0x548 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_34_GPIO_MUX2_IO02 0x098 0x2DC 0x0 0x5 0x0 + +#define IOMUXC_GPIO_EMC_B1_35_GPIO8_IO03 0x09C 0x2E0 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_35_SEMC_DATA13 0x09C 0x2E0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_35_XBAR1_INOUT09 0x09C 0x2E0 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_35_GPIO_MUX2_IO03 0x09C 0x2E0 0x0 0x5 0x0 + +#define IOMUXC_GPIO_EMC_B1_36_SEMC_DATA14 0x0A0 0x2E4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_36_XBAR1_INOUT10 0x0A0 0x2E4 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_36_GPIO_MUX2_IO04 0x0A0 0x2E4 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_36_GPIO8_IO04 0x0A0 0x2E4 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_37_GPIO8_IO05 0x0A4 0x2E8 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_37_SEMC_DATA15 0x0A4 0x2E8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_37_XBAR1_INOUT11 0x0A4 0x2E8 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_37_GPIO_MUX2_IO05 0x0A4 0x2E8 0x0 0x5 0x0 + +#define IOMUXC_GPIO_EMC_B1_38_GPIO8_IO06 0x0A8 0x2EC 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_38_SEMC_DM01 0x0A8 0x2EC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_38_FLEXPWM1_PWM3_A 0x0A8 0x2EC 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_38_TMR1_TIMER1 0x0A8 0x2EC 0x640 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_38_GPIO_MUX2_IO06 0x0A8 0x2EC 0x0 0x5 0x0 + +#define IOMUXC_GPIO_EMC_B1_39_SEMC_DQS 0x0AC 0x2F0 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_39_FLEXPWM1_PWM3_B 0x0AC 0x2F0 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_39_TMR2_TIMER1 0x0AC 0x2F0 0x64C 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_39_GPIO_MUX2_IO07 0x0AC 0x2F0 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_39_GPIO8_IO07 0x0AC 0x2F0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_40_SEMC_RDY 0x0B0 0x2F4 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_40_XBAR1_INOUT12 0x0B0 0x2F4 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_40_MQS_RIGHT 0x0B0 0x2F4 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_40_LPUART6_TXD 0x0B0 0x2F4 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B1_40_GPIO_MUX2_IO08 0x0B0 0x2F4 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_40_ENET_1G_MDC 0x0B0 0x2F4 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B1_40_CCM_CLKO1 0x0B0 0x2F4 0x0 0x9 0x0 +#define IOMUXC_GPIO_EMC_B1_40_GPIO8_IO08 0x0B0 0x2F4 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B1_41_GPIO8_IO09 0x0B4 0x2F8 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B1_41_SEMC_CSX00 0x0B4 0x2F8 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B1_41_XBAR1_INOUT13 0x0B4 0x2F8 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B1_41_MQS_LEFT 0x0B4 0x2F8 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B1_41_LPUART6_RXD 0x0B4 0x2F8 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B1_41_FLEXSPI2_B_DATA07 0x0B4 0x2F8 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B1_41_GPIO_MUX2_IO09 0x0B4 0x2F8 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B1_41_ENET_1G_MDIO 0x0B4 0x2F8 0x4C8 0x7 0x0 +#define IOMUXC_GPIO_EMC_B1_41_CCM_CLKO2 0x0B4 0x2F8 0x0 0x9 0x0 + +#define IOMUXC_GPIO_EMC_B2_00_SEMC_DATA16 0x0B8 0x2FC 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_00_CCM_ENET_REF_CLK_25M 0x0B8 0x2FC 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_00_TMR3_TIMER1 0x0B8 0x2FC 0x658 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_00_LPUART6_CTS_B 0x0B8 0x2FC 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_00_FLEXSPI2_B_DATA06 0x0B8 0x2FC 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_00_GPIO_MUX2_IO10 0x0B8 0x2FC 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_00_XBAR1_INOUT20 0x0B8 0x2FC 0x6D8 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_00_ENET_QOS_1588_EVENT1_OUT 0x0B8 0x2FC 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_00_LPSPI1_SCK 0x0B8 0x2FC 0x5D0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_00_LPI2C2_SCL 0x0B8 0x2FC 0x5B4 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_00_GPIO8_IO10 0x0B8 0x2FC 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_00_FLEXPWM3_PWM0_A 0x0B8 0x2FC 0x530 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_01_SEMC_DATA17 0x0BC 0x300 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_01_USDHC2_CD_B 0x0BC 0x300 0x6D0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_01_TMR4_TIMER1 0x0BC 0x300 0x664 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_01_LPUART6_RTS_B 0x0BC 0x300 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_01_FLEXSPI2_B_DATA05 0x0BC 0x300 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_01_GPIO_MUX2_IO11 0x0BC 0x300 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_01_XBAR1_INOUT21 0x0BC 0x300 0x6DC 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_01_ENET_QOS_1588_EVENT1_IN 0x0BC 0x300 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_01_LPSPI1_PCS0 0x0BC 0x300 0x5CC 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_01_LPI2C2_SDA 0x0BC 0x300 0x5B8 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_01_GPIO8_IO11 0x0BC 0x300 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_01_FLEXPWM3_PWM0_B 0x0BC 0x300 0x540 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_02_SEMC_DATA18 0x0C0 0x304 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_02_USDHC2_WP 0x0C0 0x304 0x6D4 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_02_VIDEO_MUX_CSI_DATA23 0x0C0 0x304 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_02_FLEXSPI2_B_DATA04 0x0C0 0x304 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_02_GPIO_MUX2_IO12 0x0C0 0x304 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_02_XBAR1_INOUT22 0x0C0 0x304 0x6E0 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_02_ENET_QOS_1588_EVENT1_AUX_IN 0x0C0 0x304 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_02_LPSPI1_SOUT 0x0C0 0x304 0x5D8 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_02_GPIO8_IO12 0x0C0 0x304 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_02_FLEXPWM3_PWM1_A 0x0C0 0x304 0x534 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_03_SEMC_DATA19 0x0C4 0x308 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_03_USDHC2_VSELECT 0x0C4 0x308 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_03_VIDEO_MUX_CSI_DATA22 0x0C4 0x308 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_03_FLEXSPI2_B_DATA03 0x0C4 0x308 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_03_GPIO_MUX2_IO13 0x0C4 0x308 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_03_XBAR1_INOUT23 0x0C4 0x308 0x6E4 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_03_ENET_1G_TX_DATA03 0x0C4 0x308 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_03_LPSPI1_SIN 0x0C4 0x308 0x5D4 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_03_GPIO8_IO13 0x0C4 0x308 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_03_FLEXPWM3_PWM1_B 0x0C4 0x308 0x544 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_04_SEMC_DATA20 0x0C8 0x30C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_04_USDHC2_RESET_B 0x0C8 0x30C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_04_SAI2_MCLK 0x0C8 0x30C 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_04_VIDEO_MUX_CSI_DATA21 0x0C8 0x30C 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_04_FLEXSPI2_B_DATA02 0x0C8 0x30C 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_04_GPIO_MUX2_IO14 0x0C8 0x30C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_04_XBAR1_INOUT24 0x0C8 0x30C 0x6E8 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_04_ENET_1G_TX_DATA02 0x0C8 0x30C 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_04_LPSPI3_SCK 0x0C8 0x30C 0x600 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_04_GPIO8_IO14 0x0C8 0x30C 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_04_FLEXPWM3_PWM2_A 0x0C8 0x30C 0x538 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_05_SEMC_DATA21 0x0CC 0x310 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_05_GPT3_CLK 0x0CC 0x310 0x598 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_05_SAI2_RX_SYNC 0x0CC 0x310 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_05_VIDEO_MUX_CSI_DATA20 0x0CC 0x310 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_05_FLEXSPI2_B_DATA01 0x0CC 0x310 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_05_GPIO_MUX2_IO15 0x0CC 0x310 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_05_XBAR1_INOUT25 0x0CC 0x310 0x6EC 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_05_ENET_1G_RX_CLK 0x0CC 0x310 0x4CC 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_05_LPSPI3_PCS0 0x0CC 0x310 0x5F0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_05_PIT1_TRIGGER0 0x0CC 0x310 0x0 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_05_GPIO8_IO15 0x0CC 0x310 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_05_FLEXPWM3_PWM2_B 0x0CC 0x310 0x548 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_06_SEMC_DATA22 0x0D0 0x314 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_06_GPT3_CAPTURE1 0x0D0 0x314 0x590 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_06_GPIO8_IO16 0x0D0 0x314 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_06_SAI2_RX_BCLK 0x0D0 0x314 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_06_FLEXPWM3_PWM3_A 0x0D0 0x314 0x53C 0xB 0x1 +#define IOMUXC_GPIO_EMC_B2_06_VIDEO_MUX_CSI_DATA19 0x0D0 0x314 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_06_FLEXSPI2_B_DATA00 0x0D0 0x314 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_06_GPIO_MUX2_IO16 0x0D0 0x314 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_06_XBAR1_INOUT26 0x0D0 0x314 0x6F0 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_06_ENET_1G_TX_ER 0x0D0 0x314 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_06_LPSPI3_SOUT 0x0D0 0x314 0x608 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_06_PIT1_TRIGGER1 0x0D0 0x314 0x0 0x9 0x0 + +#define IOMUXC_GPIO_EMC_B2_07_SEMC_DATA23 0x0D4 0x318 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_07_GPT3_CAPTURE2 0x0D4 0x318 0x594 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_07_SAI2_RX_DATA 0x0D4 0x318 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_07_VIDEO_MUX_CSI_DATA18 0x0D4 0x318 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_07_FLEXSPI2_B_DQS 0x0D4 0x318 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_07_GPIO_MUX2_IO17 0x0D4 0x318 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_07_XBAR1_INOUT27 0x0D4 0x318 0x6F4 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_07_ENET_1G_RX_DATA03 0x0D4 0x318 0x4DC 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_07_LPSPI3_SIN 0x0D4 0x318 0x604 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_07_PIT1_TRIGGER2 0x0D4 0x318 0x0 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_07_GPIO8_IO17 0x0D4 0x318 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_07_FLEXPWM3_PWM3_B 0x0D4 0x318 0x54C 0xB 0x1 + +#define IOMUXC_GPIO_EMC_B2_08_SEMC_DM02 0x0D8 0x31C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_08_GPT3_COMPARE1 0x0D8 0x31C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_08_SAI2_TX_DATA 0x0D8 0x31C 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_08_VIDEO_MUX_CSI_DATA17 0x0D8 0x31C 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_08_FLEXSPI2_B_SS0_B 0x0D8 0x31C 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_08_GPIO_MUX2_IO18 0x0D8 0x31C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_08_XBAR1_INOUT28 0x0D8 0x31C 0x6F8 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_08_ENET_1G_RX_DATA02 0x0D8 0x31C 0x4D8 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_08_LPSPI3_PCS1 0x0D8 0x31C 0x5F4 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_08_PIT1_TRIGGER3 0x0D8 0x31C 0x0 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_08_GPIO8_IO18 0x0D8 0x31C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_09_GPIO8_IO19 0x0DC 0x320 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_09_SEMC_DATA24 0x0DC 0x320 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_09_GPT3_COMPARE2 0x0DC 0x320 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_09_SAI2_TX_BCLK 0x0DC 0x320 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_09_VIDEO_MUX_CSI_DATA16 0x0DC 0x320 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_09_FLEXSPI2_B_SCLK 0x0DC 0x320 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_09_GPIO_MUX2_IO19 0x0DC 0x320 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_09_XBAR1_INOUT29 0x0DC 0x320 0x6FC 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_09_ENET_1G_CRS 0x0DC 0x320 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_09_LPSPI3_PCS2 0x0DC 0x320 0x5F8 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_09_TMR1_TIMER0 0x0DC 0x320 0x63C 0x9 0x1 + +#define IOMUXC_GPIO_EMC_B2_10_GPIO8_IO20 0x0E0 0x324 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_10_SEMC_DATA25 0x0E0 0x324 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_10_GPT3_COMPARE3 0x0E0 0x324 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_10_SAI2_TX_SYNC 0x0E0 0x324 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_10_VIDEO_MUX_CSI_FIELD 0x0E0 0x324 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_10_FLEXSPI2_A_SCLK 0x0E0 0x324 0x58C 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_10_GPIO_MUX2_IO20 0x0E0 0x324 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_10_XBAR1_INOUT30 0x0E0 0x324 0x700 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_10_ENET_1G_COL 0x0E0 0x324 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_10_LPSPI3_PCS3 0x0E0 0x324 0x5FC 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_10_TMR1_TIMER1 0x0E0 0x324 0x640 0x9 0x1 + +#define IOMUXC_GPIO_EMC_B2_11_SEMC_DATA26 0x0E4 0x328 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_11_SPDIF_IN 0x0E4 0x328 0x6B4 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_11_ENET_1G_TX_DATA00 0x0E4 0x328 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_11_SAI3_RX_SYNC 0x0E4 0x328 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_11_FLEXSPI2_A_SS0_B 0x0E4 0x328 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_11_GPIO_MUX2_IO21 0x0E4 0x328 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_11_XBAR1_INOUT31 0x0E4 0x328 0x704 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_11_EMVSIM1_IO 0x0E4 0x328 0x69C 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_11_TMR1_TIMER2 0x0E4 0x328 0x644 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_11_GPIO8_IO21 0x0E4 0x328 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_12_SEMC_DATA27 0x0E8 0x32C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_12_SPDIF_OUT 0x0E8 0x32C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_12_ENET_1G_TX_DATA01 0x0E8 0x32C 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_12_SAI3_RX_BCLK 0x0E8 0x32C 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_12_FLEXSPI2_A_DQS 0x0E8 0x32C 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_12_GPIO_MUX2_IO22 0x0E8 0x32C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_12_XBAR1_INOUT32 0x0E8 0x32C 0x708 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_12_EMVSIM1_CLK 0x0E8 0x32C 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_12_TMR1_TIMER3 0x0E8 0x32C 0x0 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_12_GPIO8_IO22 0x0E8 0x32C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_13_GPIO8_IO23 0x0EC 0x330 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_13_SEMC_DATA28 0x0EC 0x330 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_13_ENET_1G_TX_EN 0x0EC 0x330 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_13_SAI3_RX_DATA 0x0EC 0x330 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_13_FLEXSPI2_A_DATA00 0x0EC 0x330 0x57C 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_13_GPIO_MUX2_IO23 0x0EC 0x330 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_13_XBAR1_INOUT33 0x0EC 0x330 0x70C 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_13_EMVSIM1_RST 0x0EC 0x330 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_13_TMR2_TIMER0 0x0EC 0x330 0x648 0x9 0x1 + +#define IOMUXC_GPIO_EMC_B2_14_SEMC_DATA29 0x0F0 0x334 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_14_ENET_1G_TX_CLK_IO 0x0F0 0x334 0x4E8 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_14_SAI3_TX_DATA 0x0F0 0x334 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_14_FLEXSPI2_A_DATA01 0x0F0 0x334 0x580 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_14_GPIO_MUX2_IO24 0x0F0 0x334 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_14_XBAR1_INOUT34 0x0F0 0x334 0x710 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_14_SFA_ipp_do_atx_clk_under_test 0x0F0 0x334 0x0 0x7 0x0 +#define IOMUXC_GPIO_EMC_B2_14_EMVSIM1_SVEN 0x0F0 0x334 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_14_TMR2_TIMER1 0x0F0 0x334 0x64C 0x9 0x1 +#define IOMUXC_GPIO_EMC_B2_14_GPIO8_IO24 0x0F0 0x334 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_15_SEMC_DATA30 0x0F4 0x338 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_15_ENET_1G_RX_DATA00 0x0F4 0x338 0x4D0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_15_SAI3_TX_BCLK 0x0F4 0x338 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_15_FLEXSPI2_A_DATA02 0x0F4 0x338 0x584 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_15_GPIO_MUX2_IO25 0x0F4 0x338 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_15_XBAR1_INOUT35 0x0F4 0x338 0x714 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_15_EMVSIM1_PD 0x0F4 0x338 0x6A0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_15_TMR2_TIMER2 0x0F4 0x338 0x650 0x9 0x0 +#define IOMUXC_GPIO_EMC_B2_15_GPIO8_IO25 0x0F4 0x338 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_16_GPIO8_IO26 0x0F8 0x33C 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_16_SEMC_DATA31 0x0F8 0x33C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_16_XBAR1_INOUT14 0x0F8 0x33C 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_16_ENET_1G_RX_DATA01 0x0F8 0x33C 0x4D4 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_16_SAI3_TX_SYNC 0x0F8 0x33C 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_16_FLEXSPI2_A_DATA03 0x0F8 0x33C 0x588 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_16_GPIO_MUX2_IO26 0x0F8 0x33C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_16_EMVSIM1_POWER_FAIL 0x0F8 0x33C 0x6A4 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_16_TMR2_TIMER3 0x0F8 0x33C 0x0 0x9 0x0 + +#define IOMUXC_GPIO_EMC_B2_17_SEMC_DM03 0x0FC 0x340 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_17_XBAR1_INOUT15 0x0FC 0x340 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_17_ENET_1G_RX_EN 0x0FC 0x340 0x4E0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_17_SAI3_MCLK 0x0FC 0x340 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_17_FLEXSPI2_A_DATA04 0x0FC 0x340 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_17_GPIO_MUX2_IO27 0x0FC 0x340 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_17_WDOG1_ANY 0x0FC 0x340 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_17_TMR3_TIMER0 0x0FC 0x340 0x654 0x9 0x1 +#define IOMUXC_GPIO_EMC_B2_17_GPIO8_IO27 0x0FC 0x340 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_18_SEMC_DQS4 0x100 0x344 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_18_XBAR1_INOUT16 0x100 0x344 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_18_ENET_1G_RX_ER 0x100 0x344 0x4E4 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_18_EWM_OUT_B 0x100 0x344 0x0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_18_FLEXSPI2_A_DATA05 0x100 0x344 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_18_GPIO_MUX2_IO28 0x100 0x344 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_18_FLEXSPI1_A_DQS 0x100 0x344 0x550 0x6 0x0 +#define IOMUXC_GPIO_EMC_B2_18_WDOG1_B 0x100 0x344 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_18_TMR3_TIMER1 0x100 0x344 0x658 0x9 0x1 +#define IOMUXC_GPIO_EMC_B2_18_GPIO8_IO28 0x100 0x344 0x0 0xA 0x0 + +#define IOMUXC_GPIO_EMC_B2_19_GPIO8_IO29 0x104 0x348 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_19_SEMC_CLKX00 0x104 0x348 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_19_ENET_MDC 0x104 0x348 0x0 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_19_ENET_1G_MDC 0x104 0x348 0x0 0x2 0x0 +#define IOMUXC_GPIO_EMC_B2_19_ENET_1G_REF_CLK 0x104 0x348 0x4C4 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_19_FLEXSPI2_A_DATA06 0x104 0x348 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_19_GPIO_MUX2_IO29 0x104 0x348 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_19_ENET_QOS_MDC 0x104 0x348 0x0 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_19_TMR3_TIMER2 0x104 0x348 0x65C 0x9 0x0 + +#define IOMUXC_GPIO_EMC_B2_20_GPIO8_IO30 0x108 0x34C 0x0 0xA 0x0 +#define IOMUXC_GPIO_EMC_B2_20_SEMC_CLKX01 0x108 0x34C 0x0 0x0 0x0 +#define IOMUXC_GPIO_EMC_B2_20_ENET_MDIO 0x108 0x34C 0x4AC 0x1 0x0 +#define IOMUXC_GPIO_EMC_B2_20_ENET_1G_MDIO 0x108 0x34C 0x4C8 0x2 0x1 +#define IOMUXC_GPIO_EMC_B2_20_ENET_QOS_REF_CLK 0x108 0x34C 0x4A0 0x3 0x0 +#define IOMUXC_GPIO_EMC_B2_20_FLEXSPI2_A_DATA07 0x108 0x34C 0x0 0x4 0x0 +#define IOMUXC_GPIO_EMC_B2_20_GPIO_MUX2_IO30 0x108 0x34C 0x0 0x5 0x0 +#define IOMUXC_GPIO_EMC_B2_20_ENET_QOS_MDIO 0x108 0x34C 0x4EC 0x8 0x0 +#define IOMUXC_GPIO_EMC_B2_20_TMR3_TIMER3 0x108 0x34C 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_00_GPIO8_IO31 0x10C 0x350 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_00_EMVSIM1_IO 0x10C 0x350 0x69C 0x0 0x1 +#define IOMUXC_GPIO_AD_00_FLEXCAN2_TX 0x10C 0x350 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_00_ENET_1G_1588_EVENT1_IN 0x10C 0x350 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_00_GPT2_CAPTURE1 0x10C 0x350 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_00_FLEXPWM1_PWM0_A 0x10C 0x350 0x500 0x4 0x1 +#define IOMUXC_GPIO_AD_00_GPIO_MUX2_IO31 0x10C 0x350 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_00_LPUART7_TXD 0x10C 0x350 0x630 0x6 0x0 +#define IOMUXC_GPIO_AD_00_FLEXIO2_D00 0x10C 0x350 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_00_FLEXSPI2_B_SS1_B 0x10C 0x350 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_01_GPIO9_IO00 0x110 0x354 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_01_EMVSIM1_CLK 0x110 0x354 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_01_FLEXCAN2_RX 0x110 0x354 0x49C 0x1 0x0 +#define IOMUXC_GPIO_AD_01_ENET_1G_1588_EVENT1_OUT 0x110 0x354 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_01_GPT2_CAPTURE2 0x110 0x354 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_01_FLEXPWM1_PWM0_B 0x110 0x354 0x50C 0x4 0x1 +#define IOMUXC_GPIO_AD_01_GPIO_MUX3_IO00 0x110 0x354 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_01_LPUART7_RXD 0x110 0x354 0x62C 0x6 0x0 +#define IOMUXC_GPIO_AD_01_FLEXIO2_D01 0x110 0x354 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_01_FLEXSPI2_A_SS1_B 0x110 0x354 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_02_GPIO9_IO01 0x114 0x358 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_02_EMVSIM1_RST 0x114 0x358 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_02_LPUART7_CTS_B 0x114 0x358 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_02_ENET_1G_1588_EVENT2_IN 0x114 0x358 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_02_GPT2_COMPARE1 0x114 0x358 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_02_FLEXPWM1_PWM1_A 0x114 0x358 0x504 0x4 0x1 +#define IOMUXC_GPIO_AD_02_GPIO_MUX3_IO01 0x114 0x358 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_02_LPUART8_TXD 0x114 0x358 0x638 0x6 0x0 +#define IOMUXC_GPIO_AD_02_FLEXIO2_D02 0x114 0x358 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_02_VIDEO_MUX_EXT_DCIC1 0x114 0x358 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_03_GPIO9_IO02 0x118 0x35C 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_03_EMVSIM1_SVEN 0x118 0x35C 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_03_LPUART7_RTS_B 0x118 0x35C 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_03_ENET_1G_1588_EVENT2_OUT 0x118 0x35C 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_03_GPT2_COMPARE2 0x118 0x35C 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_03_FLEXPWM1_PWM1_B 0x118 0x35C 0x510 0x4 0x1 +#define IOMUXC_GPIO_AD_03_GPIO_MUX3_IO02 0x118 0x35C 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_03_LPUART8_RXD 0x118 0x35C 0x634 0x6 0x0 +#define IOMUXC_GPIO_AD_03_FLEXIO2_D03 0x118 0x35C 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_03_VIDEO_MUX_EXT_DCIC2 0x118 0x35C 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_04_EMVSIM1_PD 0x11C 0x360 0x6A0 0x0 0x1 +#define IOMUXC_GPIO_AD_04_LPUART8_CTS_B 0x11C 0x360 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_04_ENET_1G_1588_EVENT3_IN 0x11C 0x360 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_04_GPT2_COMPARE3 0x11C 0x360 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_04_FLEXPWM1_PWM2_A 0x11C 0x360 0x508 0x4 0x1 +#define IOMUXC_GPIO_AD_04_GPIO_MUX3_IO03 0x11C 0x360 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_04_WDOG1_B 0x11C 0x360 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_04_FLEXIO2_D04 0x11C 0x360 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_04_TMR4_TIMER0 0x11C 0x360 0x660 0x9 0x1 +#define IOMUXC_GPIO_AD_04_GPIO9_IO03 0x11C 0x360 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_05_EMVSIM1_POWER_FAIL 0x120 0x364 0x6A4 0x0 0x1 +#define IOMUXC_GPIO_AD_05_LPUART8_RTS_B 0x120 0x364 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_05_ENET_1G_1588_EVENT3_OUT 0x120 0x364 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_05_GPT2_CLK 0x120 0x364 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_05_FLEXPWM1_PWM2_B 0x120 0x364 0x514 0x4 0x1 +#define IOMUXC_GPIO_AD_05_GPIO_MUX3_IO04 0x120 0x364 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_05_WDOG2_B 0x120 0x364 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_05_FLEXIO2_D05 0x120 0x364 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_05_TMR4_TIMER1 0x120 0x364 0x664 0x9 0x1 +#define IOMUXC_GPIO_AD_05_GPIO9_IO04 0x120 0x364 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_06_USB_OTG2_OC 0x124 0x368 0x6B8 0x0 0x0 +#define IOMUXC_GPIO_AD_06_FLEXCAN1_TX 0x124 0x368 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_06_EMVSIM2_IO 0x124 0x368 0x6A8 0x2 0x0 +#define IOMUXC_GPIO_AD_06_GPT3_CAPTURE1 0x124 0x368 0x590 0x3 0x1 +#define IOMUXC_GPIO_AD_06_VIDEO_MUX_CSI_DATA15 0x124 0x368 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_06_GPIO_MUX3_IO05 0x124 0x368 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_06_ENET_1588_EVENT1_IN 0x124 0x368 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_06_FLEXIO2_D06 0x124 0x368 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_06_TMR4_TIMER2 0x124 0x368 0x668 0x9 0x0 +#define IOMUXC_GPIO_AD_06_GPIO9_IO05 0x124 0x368 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_06_FLEXPWM1_PWM0_X 0x124 0x368 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_07_USB_OTG2_PWR 0x128 0x36C 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_07_FLEXCAN1_RX 0x128 0x36C 0x498 0x1 0x0 +#define IOMUXC_GPIO_AD_07_EMVSIM2_CLK 0x128 0x36C 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_07_GPT3_CAPTURE2 0x128 0x36C 0x594 0x3 0x1 +#define IOMUXC_GPIO_AD_07_VIDEO_MUX_CSI_DATA14 0x128 0x36C 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_07_GPIO_MUX3_IO06 0x128 0x36C 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_07_ENET_1588_EVENT1_OUT 0x128 0x36C 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_07_FLEXIO2_D07 0x128 0x36C 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_07_TMR4_TIMER3 0x128 0x36C 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_07_GPIO9_IO06 0x128 0x36C 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_07_FLEXPWM1_PWM1_X 0x128 0x36C 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_08_USBPHY2_OTG_ID 0x12C 0x370 0x6C4 0x0 0x0 +#define IOMUXC_GPIO_AD_08_LPI2C1_SCL 0x12C 0x370 0x5AC 0x1 0x0 +#define IOMUXC_GPIO_AD_08_EMVSIM2_RST 0x12C 0x370 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_08_GPT3_COMPARE1 0x12C 0x370 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_08_VIDEO_MUX_CSI_DATA13 0x12C 0x370 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_08_GPIO_MUX3_IO07 0x12C 0x370 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_08_ENET_1588_EVENT2_IN 0x12C 0x370 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_08_FLEXIO2_D08 0x12C 0x370 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_08_GPIO9_IO07 0x12C 0x370 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_08_FLEXPWM1_PWM2_X 0x12C 0x370 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_09_USBPHY1_OTG_ID 0x130 0x374 0x6C0 0x0 0x0 +#define IOMUXC_GPIO_AD_09_LPI2C1_SDA 0x130 0x374 0x5B0 0x1 0x0 +#define IOMUXC_GPIO_AD_09_EMVSIM2_SVEN 0x130 0x374 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_09_GPT3_COMPARE2 0x130 0x374 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_09_VIDEO_MUX_CSI_DATA12 0x130 0x374 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_09_GPIO_MUX3_IO08 0x130 0x374 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_09_ENET_1588_EVENT2_OUT 0x130 0x374 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_09_FLEXIO2_D09 0x130 0x374 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_09_GPIO9_IO08 0x130 0x374 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_09_FLEXPWM1_PWM3_X 0x130 0x374 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_10_USB_OTG1_PWR 0x134 0x378 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_10_LPI2C1_SCLS 0x134 0x378 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_10_EMVSIM2_PD 0x134 0x378 0x6AC 0x2 0x0 +#define IOMUXC_GPIO_AD_10_GPT3_COMPARE3 0x134 0x378 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_10_VIDEO_MUX_CSI_DATA11 0x134 0x378 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_10_GPIO_MUX3_IO09 0x134 0x378 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_10_ENET_1588_EVENT3_IN 0x134 0x378 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_10_FLEXIO2_D10 0x134 0x378 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_10_GPIO9_IO09 0x134 0x378 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_10_FLEXPWM2_PWM0_X 0x134 0x378 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_11_USB_OTG1_OC 0x138 0x37C 0x6BC 0x0 0x0 +#define IOMUXC_GPIO_AD_11_LPI2C1_SDAS 0x138 0x37C 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_11_EMVSIM2_POWER_FAIL 0x138 0x37C 0x6B0 0x2 0x0 +#define IOMUXC_GPIO_AD_11_GPT3_CLK 0x138 0x37C 0x598 0x3 0x1 +#define IOMUXC_GPIO_AD_11_VIDEO_MUX_CSI_DATA10 0x138 0x37C 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_11_GPIO_MUX3_IO10 0x138 0x37C 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_11_ENET_1588_EVENT3_OUT 0x138 0x37C 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_11_FLEXIO2_D11 0x138 0x37C 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_11_GPIO9_IO10 0x138 0x37C 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_11_FLEXPWM2_PWM1_X 0x138 0x37C 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_12_SPDIF_LOCK 0x13C 0x380 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_12_LPI2C1_HREQ 0x13C 0x380 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_12_GPT1_CAPTURE1 0x13C 0x380 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_12_FLEXSPI1_B_DATA03 0x13C 0x380 0x570 0x3 0x0 +#define IOMUXC_GPIO_AD_12_VIDEO_MUX_CSI_PIXCLK 0x13C 0x380 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_12_GPIO_MUX3_IO11 0x13C 0x380 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_12_ENET_TX_DATA03 0x13C 0x380 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_12_FLEXIO2_D12 0x13C 0x380 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_12_EWM_OUT_B 0x13C 0x380 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_12_GPIO9_IO11 0x13C 0x380 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_12_FLEXPWM2_PWM2_X 0x13C 0x380 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_13_SPDIF_SR_CLK 0x140 0x384 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_13_PIT1_TRIGGER0 0x140 0x384 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_13_GPT1_CAPTURE2 0x140 0x384 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_13_FLEXSPI1_B_DATA02 0x140 0x384 0x56C 0x3 0x0 +#define IOMUXC_GPIO_AD_13_VIDEO_MUX_CSI_MCLK 0x140 0x384 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_13_GPIO_MUX3_IO12 0x140 0x384 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_13_ENET_TX_DATA02 0x140 0x384 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_13_FLEXIO2_D13 0x140 0x384 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_13_REF_CLK_32K 0x140 0x384 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_13_GPIO9_IO12 0x140 0x384 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_13_FLEXPWM2_PWM3_X 0x140 0x384 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_14_SPDIF_EXT_CLK 0x144 0x388 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_14_REF_CLK_24M 0x144 0x388 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_14_GPT1_COMPARE1 0x144 0x388 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_14_FLEXSPI1_B_DATA01 0x144 0x388 0x568 0x3 0x0 +#define IOMUXC_GPIO_AD_14_VIDEO_MUX_CSI_VSYNC 0x144 0x388 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_14_GPIO_MUX3_IO13 0x144 0x388 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_14_ENET_RX_CLK 0x144 0x388 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_14_FLEXIO2_D14 0x144 0x388 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_14_CCM_ENET_REF_CLK_25M 0x144 0x388 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_14_GPIO9_IO13 0x144 0x388 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_14_FLEXPWM3_PWM0_X 0x144 0x388 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_15_GPIO9_IO14 0x148 0x38C 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_15_FLEXPWM3_PWM1_X 0x148 0x38C 0x0 0xB 0x0 +#define IOMUXC_GPIO_AD_15_SPDIF_IN 0x148 0x38C 0x6B4 0x0 0x1 +#define IOMUXC_GPIO_AD_15_LPUART10_TXD 0x148 0x38C 0x628 0x1 0x0 +#define IOMUXC_GPIO_AD_15_GPT1_COMPARE2 0x148 0x38C 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_15_FLEXSPI1_B_DATA00 0x148 0x38C 0x564 0x3 0x0 +#define IOMUXC_GPIO_AD_15_VIDEO_MUX_CSI_HSYNC 0x148 0x38C 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_15_GPIO_MUX3_IO14 0x148 0x38C 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_15_ENET_TX_ER 0x148 0x38C 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_15_FLEXIO2_D15 0x148 0x38C 0x0 0x8 0x0 + +#define IOMUXC_GPIO_AD_16_SPDIF_OUT 0x14C 0x390 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_16_LPUART10_RXD 0x14C 0x390 0x624 0x1 0x0 +#define IOMUXC_GPIO_AD_16_GPT1_COMPARE3 0x14C 0x390 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_16_FLEXSPI1_B_SCLK 0x14C 0x390 0x578 0x3 0x0 +#define IOMUXC_GPIO_AD_16_VIDEO_MUX_CSI_DATA09 0x14C 0x390 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_16_GPIO_MUX3_IO15 0x14C 0x390 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_16_ENET_RX_DATA03 0x14C 0x390 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_16_FLEXIO2_D16 0x14C 0x390 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_16_ENET_1G_MDC 0x14C 0x390 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_16_GPIO9_IO15 0x14C 0x390 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_16_FLEXPWM3_PWM2_X 0x14C 0x390 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_17_SAI1_MCLK 0x150 0x394 0x66C 0x0 0x0 +#define IOMUXC_GPIO_AD_17_ACMP1_OUT 0x150 0x394 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_17_GPT1_CLK 0x150 0x394 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_17_FLEXSPI1_A_DQS 0x150 0x394 0x550 0x3 0x1 +#define IOMUXC_GPIO_AD_17_VIDEO_MUX_CSI_DATA08 0x150 0x394 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_17_GPIO_MUX3_IO16 0x150 0x394 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_17_ENET_RX_DATA02 0x150 0x394 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_17_FLEXIO2_D17 0x150 0x394 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_17_ENET_1G_MDIO 0x150 0x394 0x4C8 0x9 0x2 +#define IOMUXC_GPIO_AD_17_GPIO9_IO16 0x150 0x394 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_17_FLEXPWM3_PWM3_X 0x150 0x394 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_18_GPIO9_IO17 0x154 0x398 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_18_FLEXPWM4_PWM0_X 0x154 0x398 0x0 0xB 0x0 +#define IOMUXC_GPIO_AD_18_SAI1_RX_SYNC 0x154 0x398 0x678 0x0 0x0 +#define IOMUXC_GPIO_AD_18_ACMP2_OUT 0x154 0x398 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_18_LPSPI1_PCS1 0x154 0x398 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_18_FLEXSPI1_A_SS0_B 0x154 0x398 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_18_VIDEO_MUX_CSI_DATA07 0x154 0x398 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_18_GPIO_MUX3_IO17 0x154 0x398 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_18_ENET_CRS 0x154 0x398 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_18_FLEXIO2_D18 0x154 0x398 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_18_LPI2C2_SCL 0x154 0x398 0x5B4 0x9 0x1 + +#define IOMUXC_GPIO_AD_19_SAI1_RX_BCLK 0x158 0x39C 0x670 0x0 0x0 +#define IOMUXC_GPIO_AD_19_ACMP3_OUT 0x158 0x39C 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_19_LPSPI1_PCS2 0x158 0x39C 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_19_FLEXSPI1_A_SCLK 0x158 0x39C 0x574 0x3 0x0 +#define IOMUXC_GPIO_AD_19_VIDEO_MUX_CSI_DATA06 0x158 0x39C 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_19_GPIO_MUX3_IO18 0x158 0x39C 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_19_ENET_COL 0x158 0x39C 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_19_FLEXIO2_D19 0x158 0x39C 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_19_LPI2C2_SDA 0x158 0x39C 0x5B8 0x9 0x1 +#define IOMUXC_GPIO_AD_19_GPIO9_IO18 0x158 0x39C 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_19_FLEXPWM4_PWM1_X 0x158 0x39C 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_20_SAI1_RX_DATA00 0x15C 0x3A0 0x674 0x0 0x0 +#define IOMUXC_GPIO_AD_20_ACMP4_OUT 0x15C 0x3A0 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_20_LPSPI1_PCS3 0x15C 0x3A0 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_20_FLEXSPI1_A_DATA00 0x15C 0x3A0 0x554 0x3 0x0 +#define IOMUXC_GPIO_AD_20_VIDEO_MUX_CSI_DATA05 0x15C 0x3A0 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_20_GPIO_MUX3_IO19 0x15C 0x3A0 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_20_KPP_ROW07 0x15C 0x3A0 0x5A8 0x6 0x0 +#define IOMUXC_GPIO_AD_20_FLEXIO2_D20 0x15C 0x3A0 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_20_ENET_QOS_1588_EVENT2_OUT 0x15C 0x3A0 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_20_GPIO9_IO19 0x15C 0x3A0 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_20_FLEXPWM4_PWM2_X 0x15C 0x3A0 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_21_SAI1_TX_DATA00 0x160 0x3A4 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_21_LPSPI2_PCS1 0x160 0x3A4 0x5E0 0x2 0x0 +#define IOMUXC_GPIO_AD_21_FLEXSPI1_A_DATA01 0x160 0x3A4 0x558 0x3 0x0 +#define IOMUXC_GPIO_AD_21_VIDEO_MUX_CSI_DATA04 0x160 0x3A4 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_21_GPIO_MUX3_IO20 0x160 0x3A4 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_21_KPP_COL07 0x160 0x3A4 0x5A0 0x6 0x0 +#define IOMUXC_GPIO_AD_21_FLEXIO2_D21 0x160 0x3A4 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_21_ENET_QOS_1588_EVENT2_IN 0x160 0x3A4 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_21_GPIO9_IO20 0x160 0x3A4 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_21_FLEXPWM4_PWM3_X 0x160 0x3A4 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_22_GPIO9_IO21 0x164 0x3A8 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_22_SAI1_TX_BCLK 0x164 0x3A8 0x67C 0x0 0x0 +#define IOMUXC_GPIO_AD_22_LPSPI2_PCS2 0x164 0x3A8 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_22_FLEXSPI1_A_DATA02 0x164 0x3A8 0x55C 0x3 0x0 +#define IOMUXC_GPIO_AD_22_VIDEO_MUX_CSI_DATA03 0x164 0x3A8 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_22_GPIO_MUX3_IO21 0x164 0x3A8 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_22_KPP_ROW06 0x164 0x3A8 0x5A4 0x6 0x0 +#define IOMUXC_GPIO_AD_22_FLEXIO2_D22 0x164 0x3A8 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_22_ENET_QOS_1588_EVENT3_OUT 0x164 0x3A8 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_23_SAI1_TX_SYNC 0x168 0x3AC 0x680 0x0 0x0 +#define IOMUXC_GPIO_AD_23_LPSPI2_PCS3 0x168 0x3AC 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_23_FLEXSPI1_A_DATA03 0x168 0x3AC 0x560 0x3 0x0 +#define IOMUXC_GPIO_AD_23_VIDEO_MUX_CSI_DATA02 0x168 0x3AC 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_23_GPIO_MUX3_IO22 0x168 0x3AC 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_23_KPP_COL06 0x168 0x3AC 0x59C 0x6 0x0 +#define IOMUXC_GPIO_AD_23_FLEXIO2_D23 0x168 0x3AC 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_23_ENET_QOS_1588_EVENT3_IN 0x168 0x3AC 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_23_GPIO9_IO22 0x168 0x3AC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_24_LPUART1_TXD 0x16C 0x3B0 0x620 0x0 0x0 +#define IOMUXC_GPIO_AD_24_LPSPI2_SCK 0x16C 0x3B0 0x5E4 0x1 0x0 +#define IOMUXC_GPIO_AD_24_VIDEO_MUX_CSI_DATA00 0x16C 0x3B0 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_24_ENET_RX_EN 0x16C 0x3B0 0x4B8 0x3 0x0 +#define IOMUXC_GPIO_AD_24_FLEXPWM2_PWM0_A 0x16C 0x3B0 0x518 0x4 0x1 +#define IOMUXC_GPIO_AD_24_GPIO_MUX3_IO23 0x16C 0x3B0 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_24_KPP_ROW05 0x16C 0x3B0 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_24_FLEXIO2_D24 0x16C 0x3B0 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_24_LPI2C4_SCL 0x16C 0x3B0 0x5C4 0x9 0x0 +#define IOMUXC_GPIO_AD_24_GPIO9_IO23 0x16C 0x3B0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_25_GPIO9_IO24 0x170 0x3B4 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_25_LPUART1_RXD 0x170 0x3B4 0x61C 0x0 0x0 +#define IOMUXC_GPIO_AD_25_LPSPI2_PCS0 0x170 0x3B4 0x5DC 0x1 0x0 +#define IOMUXC_GPIO_AD_25_VIDEO_MUX_CSI_DATA01 0x170 0x3B4 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_25_ENET_RX_ER 0x170 0x3B4 0x4BC 0x3 0x0 +#define IOMUXC_GPIO_AD_25_FLEXPWM2_PWM0_B 0x170 0x3B4 0x524 0x4 0x1 +#define IOMUXC_GPIO_AD_25_GPIO_MUX3_IO24 0x170 0x3B4 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_25_KPP_COL05 0x170 0x3B4 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_25_FLEXIO2_D25 0x170 0x3B4 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_25_LPI2C4_SDA 0x170 0x3B4 0x5C8 0x9 0x0 + +#define IOMUXC_GPIO_AD_26_LPUART1_CTS_B 0x174 0x3B8 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_26_LPSPI2_SOUT 0x174 0x3B8 0x5EC 0x1 0x0 +#define IOMUXC_GPIO_AD_26_SEMC_CSX01 0x174 0x3B8 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_26_ENET_RX_DATA00 0x174 0x3B8 0x4B0 0x3 0x0 +#define IOMUXC_GPIO_AD_26_FLEXPWM2_PWM1_A 0x174 0x3B8 0x51C 0x4 0x1 +#define IOMUXC_GPIO_AD_26_GPIO_MUX3_IO25 0x174 0x3B8 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_26_KPP_ROW04 0x174 0x3B8 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_26_FLEXIO2_D26 0x174 0x3B8 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_26_ENET_QOS_MDC 0x174 0x3B8 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_26_GPIO9_IO25 0x174 0x3B8 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_26_USDHC2_CD_B 0x174 0x3B8 0x6D0 0xB 0x1 + +#define IOMUXC_GPIO_AD_27_LPUART1_RTS_B 0x178 0x3BC 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_27_LPSPI2_SIN 0x178 0x3BC 0x5E8 0x1 0x0 +#define IOMUXC_GPIO_AD_27_SEMC_CSX02 0x178 0x3BC 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_27_ENET_RX_DATA01 0x178 0x3BC 0x4B4 0x3 0x0 +#define IOMUXC_GPIO_AD_27_FLEXPWM2_PWM1_B 0x178 0x3BC 0x528 0x4 0x1 +#define IOMUXC_GPIO_AD_27_GPIO_MUX3_IO26 0x178 0x3BC 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_27_KPP_COL04 0x178 0x3BC 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_27_FLEXIO2_D27 0x178 0x3BC 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_27_ENET_QOS_MDIO 0x178 0x3BC 0x4EC 0x9 0x1 +#define IOMUXC_GPIO_AD_27_GPIO9_IO26 0x178 0x3BC 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_27_USDHC2_WP 0x178 0x3BC 0x6D4 0xB 0x1 + +#define IOMUXC_GPIO_AD_28_GPIO9_IO27 0x17C 0x3C0 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_28_USDHC2_VSELECT 0x17C 0x3C0 0x0 0xB 0x0 +#define IOMUXC_GPIO_AD_28_LPSPI1_SCK 0x17C 0x3C0 0x5D0 0x0 0x1 +#define IOMUXC_GPIO_AD_28_LPUART5_TXD 0x17C 0x3C0 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_28_SEMC_CSX03 0x17C 0x3C0 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_28_ENET_TX_EN 0x17C 0x3C0 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_28_FLEXPWM2_PWM2_A 0x17C 0x3C0 0x520 0x4 0x1 +#define IOMUXC_GPIO_AD_28_GPIO_MUX3_IO27 0x17C 0x3C0 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_28_KPP_ROW03 0x17C 0x3C0 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_28_FLEXIO2_D28 0x17C 0x3C0 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_28_VIDEO_MUX_EXT_DCIC1 0x17C 0x3C0 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_29_LPSPI1_PCS0 0x180 0x3C4 0x5CC 0x0 0x1 +#define IOMUXC_GPIO_AD_29_LPUART5_RXD 0x180 0x3C4 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_29_ENET_REF_CLK 0x180 0x3C4 0x4A8 0x2 0x0 +#define IOMUXC_GPIO_AD_29_ENET_TX_CLK 0x180 0x3C4 0x4C0 0x3 0x0 +#define IOMUXC_GPIO_AD_29_FLEXPWM2_PWM2_B 0x180 0x3C4 0x52C 0x4 0x1 +#define IOMUXC_GPIO_AD_29_GPIO_MUX3_IO28 0x180 0x3C4 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_29_KPP_COL03 0x180 0x3C4 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_29_FLEXIO2_D29 0x180 0x3C4 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_29_VIDEO_MUX_EXT_DCIC2 0x180 0x3C4 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_29_GPIO9_IO28 0x180 0x3C4 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_29_USDHC2_RESET_B 0x180 0x3C4 0x0 0xB 0x0 + +#define IOMUXC_GPIO_AD_30_LPSPI1_SOUT 0x184 0x3C8 0x5D8 0x0 0x1 +#define IOMUXC_GPIO_AD_30_USB_OTG2_OC 0x184 0x3C8 0x6B8 0x1 0x1 +#define IOMUXC_GPIO_AD_30_FLEXCAN2_TX 0x184 0x3C8 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_30_ENET_TX_DATA00 0x184 0x3C8 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_30_LPUART3_TXD 0x184 0x3C8 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_30_GPIO_MUX3_IO29 0x184 0x3C8 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_30_KPP_ROW02 0x184 0x3C8 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_30_FLEXIO2_D30 0x184 0x3C8 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_30_WDOG2_RESET_B_DEB 0x184 0x3C8 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_30_GPIO9_IO29 0x184 0x3C8 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_31_LPSPI1_SIN 0x188 0x3CC 0x5D4 0x0 0x1 +#define IOMUXC_GPIO_AD_31_USB_OTG2_PWR 0x188 0x3CC 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_31_FLEXCAN2_RX 0x188 0x3CC 0x49C 0x2 0x1 +#define IOMUXC_GPIO_AD_31_ENET_TX_DATA01 0x188 0x3CC 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_31_LPUART3_RXD 0x188 0x3CC 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_31_GPIO_MUX3_IO30 0x188 0x3CC 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_31_KPP_COL02 0x188 0x3CC 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_31_FLEXIO2_D31 0x188 0x3CC 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_31_WDOG1_RESET_B_DEB 0x188 0x3CC 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_31_GPIO9_IO30 0x188 0x3CC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_32_GPIO9_IO31 0x18C 0x3D0 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_32_LPI2C1_SCL 0x18C 0x3D0 0x5AC 0x0 0x1 +#define IOMUXC_GPIO_AD_32_USBPHY2_OTG_ID 0x18C 0x3D0 0x6C4 0x1 0x1 +#define IOMUXC_GPIO_AD_32_PGMC_PMIC_RDY 0x18C 0x3D0 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_32_ENET_MDC 0x18C 0x3D0 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_32_USDHC1_CD_B 0x18C 0x3D0 0x6C8 0x4 0x0 +#define IOMUXC_GPIO_AD_32_GPIO_MUX3_IO31 0x18C 0x3D0 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_32_KPP_ROW01 0x18C 0x3D0 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_32_LPUART10_TXD 0x18C 0x3D0 0x628 0x8 0x1 +#define IOMUXC_GPIO_AD_32_ENET_1G_MDC 0x18C 0x3D0 0x0 0x9 0x0 + +#define IOMUXC_GPIO_AD_33_LPI2C1_SDA 0x190 0x3D4 0x5B0 0x0 0x1 +#define IOMUXC_GPIO_AD_33_USBPHY1_OTG_ID 0x190 0x3D4 0x6C0 0x1 0x1 +#define IOMUXC_GPIO_AD_33_XBAR1_INOUT17 0x190 0x3D4 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_33_ENET_MDIO 0x190 0x3D4 0x4AC 0x3 0x1 +#define IOMUXC_GPIO_AD_33_USDHC1_WP 0x190 0x3D4 0x6CC 0x4 0x0 +#define IOMUXC_GPIO_AD_33_GPIO_MUX4_IO00 0x190 0x3D4 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_33_KPP_COL01 0x190 0x3D4 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_33_LPUART10_RXD 0x190 0x3D4 0x624 0x8 0x1 +#define IOMUXC_GPIO_AD_33_ENET_1G_MDIO 0x190 0x3D4 0x4C8 0x9 0x3 +#define IOMUXC_GPIO_AD_33_GPIO10_IO00 0x190 0x3D4 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_34_ENET_1G_1588_EVENT0_IN 0x194 0x3D8 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_34_USB_OTG1_PWR 0x194 0x3D8 0x0 0x1 0x0 +#define IOMUXC_GPIO_AD_34_XBAR1_INOUT18 0x194 0x3D8 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_34_ENET_1588_EVENT0_IN 0x194 0x3D8 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_34_USDHC1_VSELECT 0x194 0x3D8 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_34_GPIO_MUX4_IO01 0x194 0x3D8 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_34_KPP_ROW00 0x194 0x3D8 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_34_LPUART10_CTS_B 0x194 0x3D8 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_34_WDOG1_ANY 0x194 0x3D8 0x0 0x9 0x0 +#define IOMUXC_GPIO_AD_34_GPIO10_IO01 0x194 0x3D8 0x0 0xA 0x0 + +#define IOMUXC_GPIO_AD_35_GPIO10_IO02 0x198 0x3DC 0x0 0xA 0x0 +#define IOMUXC_GPIO_AD_35_ENET_1G_1588_EVENT0_OUT 0x198 0x3DC 0x0 0x0 0x0 +#define IOMUXC_GPIO_AD_35_USB_OTG1_OC 0x198 0x3DC 0x6BC 0x1 0x1 +#define IOMUXC_GPIO_AD_35_XBAR1_INOUT19 0x198 0x3DC 0x0 0x2 0x0 +#define IOMUXC_GPIO_AD_35_ENET_1588_EVENT0_OUT 0x198 0x3DC 0x0 0x3 0x0 +#define IOMUXC_GPIO_AD_35_USDHC1_RESET_B 0x198 0x3DC 0x0 0x4 0x0 +#define IOMUXC_GPIO_AD_35_GPIO_MUX4_IO02 0x198 0x3DC 0x0 0x5 0x0 +#define IOMUXC_GPIO_AD_35_KPP_COL00 0x198 0x3DC 0x0 0x6 0x0 +#define IOMUXC_GPIO_AD_35_LPUART10_RTS_B 0x198 0x3DC 0x0 0x8 0x0 +#define IOMUXC_GPIO_AD_35_FLEXSPI1_B_SS1_B 0x198 0x3DC 0x0 0x9 0x0 + +#define IOMUXC_GPIO_SD_B1_00_USDHC1_CMD 0x19C 0x3E0 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_00_XBAR1_INOUT20 0x19C 0x3E0 0x6D8 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_00_GPT4_CAPTURE1 0x19C 0x3E0 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_00_GPIO_MUX4_IO03 0x19C 0x3E0 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_00_FLEXSPI2_A_SS0_B 0x19C 0x3E0 0x0 0x6 0x0 +#define IOMUXC_GPIO_SD_B1_00_KPP_ROW07 0x19C 0x3E0 0x5A8 0x8 0x1 +#define IOMUXC_GPIO_SD_B1_00_GPIO10_IO03 0x19C 0x3E0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B1_01_USDHC1_CLK 0x1A0 0x3E4 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_01_XBAR1_INOUT21 0x1A0 0x3E4 0x6DC 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_01_GPT4_CAPTURE2 0x1A0 0x3E4 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_01_GPIO_MUX4_IO04 0x1A0 0x3E4 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_01_FLEXSPI2_A_SCLK 0x1A0 0x3E4 0x58C 0x6 0x1 +#define IOMUXC_GPIO_SD_B1_01_KPP_COL07 0x1A0 0x3E4 0x5A0 0x8 0x1 +#define IOMUXC_GPIO_SD_B1_01_GPIO10_IO04 0x1A0 0x3E4 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B1_02_GPIO10_IO05 0x1A4 0x3E8 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B1_02_USDHC1_DATA0 0x1A4 0x3E8 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_02_XBAR1_INOUT22 0x1A4 0x3E8 0x6E0 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_02_GPT4_COMPARE1 0x1A4 0x3E8 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_02_GPIO_MUX4_IO05 0x1A4 0x3E8 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_02_FLEXSPI2_A_DATA00 0x1A4 0x3E8 0x57C 0x6 0x1 +#define IOMUXC_GPIO_SD_B1_02_KPP_ROW06 0x1A4 0x3E8 0x5A4 0x8 0x1 +#define IOMUXC_GPIO_SD_B1_02_FLEXSPI1_A_SS1_B 0x1A4 0x3E8 0x0 0x9 0x0 + +#define IOMUXC_GPIO_SD_B1_03_USDHC1_DATA1 0x1A8 0x3EC 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_03_XBAR1_INOUT23 0x1A8 0x3EC 0x6E4 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_03_GPT4_COMPARE2 0x1A8 0x3EC 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_03_GPIO_MUX4_IO06 0x1A8 0x3EC 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_03_FLEXSPI2_A_DATA01 0x1A8 0x3EC 0x580 0x6 0x1 +#define IOMUXC_GPIO_SD_B1_03_KPP_COL06 0x1A8 0x3EC 0x59C 0x8 0x1 +#define IOMUXC_GPIO_SD_B1_03_FLEXSPI1_B_SS1_B 0x1A8 0x3EC 0x0 0x9 0x0 +#define IOMUXC_GPIO_SD_B1_03_GPIO10_IO06 0x1A8 0x3EC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B1_04_USDHC1_DATA2 0x1AC 0x3F0 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_04_XBAR1_INOUT24 0x1AC 0x3F0 0x6E8 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_04_GPT4_COMPARE3 0x1AC 0x3F0 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_04_GPIO_MUX4_IO07 0x1AC 0x3F0 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_04_FLEXSPI2_A_DATA02 0x1AC 0x3F0 0x584 0x6 0x1 +#define IOMUXC_GPIO_SD_B1_04_FLEXSPI1_B_SS0_B 0x1AC 0x3F0 0x0 0x8 0x0 +#define IOMUXC_GPIO_SD_B1_04_ENET_QOS_1588_EVENT2_AUX_IN 0x1AC 0x3F0 0x0 0x9 0x0 +#define IOMUXC_GPIO_SD_B1_04_GPIO10_IO07 0x1AC 0x3F0 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B1_05_GPIO10_IO08 0x1B0 0x3F4 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B1_05_USDHC1_DATA3 0x1B0 0x3F4 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B1_05_XBAR1_INOUT25 0x1B0 0x3F4 0x6EC 0x2 0x1 +#define IOMUXC_GPIO_SD_B1_05_GPT4_CLK 0x1B0 0x3F4 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B1_05_GPIO_MUX4_IO08 0x1B0 0x3F4 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B1_05_FLEXSPI2_A_DATA03 0x1B0 0x3F4 0x588 0x6 0x1 +#define IOMUXC_GPIO_SD_B1_05_FLEXSPI1_B_DQS 0x1B0 0x3F4 0x0 0x8 0x0 +#define IOMUXC_GPIO_SD_B1_05_ENET_QOS_1588_EVENT3_AUX_IN 0x1B0 0x3F4 0x0 0x9 0x0 + +#define IOMUXC_GPIO_SD_B2_00_GPIO10_IO09 0x1B4 0x3F8 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_00_USDHC2_DATA3 0x1B4 0x3F8 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_00_FLEXSPI1_B_DATA03 0x1B4 0x3F8 0x570 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_00_ENET_1G_RX_EN 0x1B4 0x3F8 0x4E0 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_00_LPUART9_TXD 0x1B4 0x3F8 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_00_LPSPI4_SCK 0x1B4 0x3F8 0x610 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_00_GPIO_MUX4_IO09 0x1B4 0x3F8 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SD_B2_01_USDHC2_DATA2 0x1B8 0x3FC 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_01_FLEXSPI1_B_DATA02 0x1B8 0x3FC 0x56C 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_01_ENET_1G_RX_CLK 0x1B8 0x3FC 0x4CC 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_01_LPUART9_RXD 0x1B8 0x3FC 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_01_LPSPI4_PCS0 0x1B8 0x3FC 0x60C 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_01_GPIO_MUX4_IO10 0x1B8 0x3FC 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_01_GPIO10_IO10 0x1B8 0x3FC 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B2_02_GPIO10_IO11 0x1BC 0x400 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_02_USDHC2_DATA1 0x1BC 0x400 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_02_FLEXSPI1_B_DATA01 0x1BC 0x400 0x568 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_02_ENET_1G_RX_DATA00 0x1BC 0x400 0x4D0 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_02_LPUART9_CTS_B 0x1BC 0x400 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_02_LPSPI4_SOUT 0x1BC 0x400 0x618 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_02_GPIO_MUX4_IO11 0x1BC 0x400 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SD_B2_03_GPIO10_IO12 0x1C0 0x404 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_03_USDHC2_DATA0 0x1C0 0x404 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_03_FLEXSPI1_B_DATA00 0x1C0 0x404 0x564 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_03_ENET_1G_RX_DATA01 0x1C0 0x404 0x4D4 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_03_LPUART9_RTS_B 0x1C0 0x404 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_03_LPSPI4_SIN 0x1C0 0x404 0x614 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_03_GPIO_MUX4_IO12 0x1C0 0x404 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SD_B2_04_USDHC2_CLK 0x1C4 0x408 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_04_FLEXSPI1_B_SCLK 0x1C4 0x408 0x578 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_04_ENET_1G_RX_DATA02 0x1C4 0x408 0x4D8 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_04_FLEXSPI1_A_SS1_B 0x1C4 0x408 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_04_LPSPI4_PCS1 0x1C4 0x408 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_04_GPIO_MUX4_IO13 0x1C4 0x408 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_04_GPIO10_IO13 0x1C4 0x408 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B2_05_GPIO10_IO14 0x1C8 0x40C 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_05_USDHC2_CMD 0x1C8 0x40C 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS 0x1C8 0x40C 0x550 0x1 0x2 +#define IOMUXC_GPIO_SD_B2_05_ENET_1G_RX_DATA03 0x1C8 0x40C 0x4DC 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_05_FLEXSPI1_B_SS0_B 0x1C8 0x40C 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_05_LPSPI4_PCS2 0x1C8 0x40C 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_05_GPIO_MUX4_IO14 0x1C8 0x40C 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SD_B2_06_GPIO10_IO15 0x1CC 0x410 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_06_USDHC2_RESET_B 0x1CC 0x410 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B 0x1CC 0x410 0x0 0x1 0x0 +#define IOMUXC_GPIO_SD_B2_06_ENET_1G_TX_DATA03 0x1CC 0x410 0x0 0x2 0x0 +#define IOMUXC_GPIO_SD_B2_06_LPSPI4_PCS3 0x1CC 0x410 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_06_GPT6_CAPTURE1 0x1CC 0x410 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_06_GPIO_MUX4_IO15 0x1CC 0x410 0x0 0x5 0x0 + +#define IOMUXC_GPIO_SD_B2_07_USDHC2_STROBE 0x1D0 0x414 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK 0x1D0 0x414 0x574 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_07_ENET_1G_TX_DATA02 0x1D0 0x414 0x0 0x2 0x0 +#define IOMUXC_GPIO_SD_B2_07_LPUART3_CTS_B 0x1D0 0x414 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_07_GPT6_CAPTURE2 0x1D0 0x414 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_07_GPIO_MUX4_IO16 0x1D0 0x414 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_07_LPSPI2_SCK 0x1D0 0x414 0x5E4 0x6 0x1 +#define IOMUXC_GPIO_SD_B2_07_ENET_TX_ER 0x1D0 0x414 0x0 0x8 0x0 +#define IOMUXC_GPIO_SD_B2_07_ENET_QOS_REF_CLK 0x1D0 0x414 0x4A0 0x9 0x1 +#define IOMUXC_GPIO_SD_B2_07_GPIO10_IO16 0x1D0 0x414 0x0 0xA 0x0 + +#define IOMUXC_GPIO_SD_B2_08_GPIO10_IO17 0x1D4 0x418 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_08_USDHC2_DATA4 0x1D4 0x418 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00 0x1D4 0x418 0x554 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_08_ENET_1G_TX_DATA01 0x1D4 0x418 0x0 0x2 0x0 +#define IOMUXC_GPIO_SD_B2_08_LPUART3_RTS_B 0x1D4 0x418 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_08_GPT6_COMPARE1 0x1D4 0x418 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_08_GPIO_MUX4_IO17 0x1D4 0x418 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_08_LPSPI2_PCS0 0x1D4 0x418 0x5DC 0x6 0x1 + +#define IOMUXC_GPIO_SD_B2_09_GPIO10_IO18 0x1D8 0x41C 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_09_USDHC2_DATA5 0x1D8 0x41C 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01 0x1D8 0x41C 0x558 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_09_ENET_1G_TX_DATA00 0x1D8 0x41C 0x0 0x2 0x0 +#define IOMUXC_GPIO_SD_B2_09_LPUART5_CTS_B 0x1D8 0x41C 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_09_GPT6_COMPARE2 0x1D8 0x41C 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_09_GPIO_MUX4_IO18 0x1D8 0x41C 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_09_LPSPI2_SOUT 0x1D8 0x41C 0x5EC 0x6 0x1 + +#define IOMUXC_GPIO_SD_B2_10_GPIO10_IO19 0x1DC 0x420 0x0 0xA 0x0 +#define IOMUXC_GPIO_SD_B2_10_USDHC2_DATA6 0x1DC 0x420 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02 0x1DC 0x420 0x55C 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_10_ENET_1G_TX_EN 0x1DC 0x420 0x0 0x2 0x0 +#define IOMUXC_GPIO_SD_B2_10_LPUART5_RTS_B 0x1DC 0x420 0x0 0x3 0x0 +#define IOMUXC_GPIO_SD_B2_10_GPT6_COMPARE3 0x1DC 0x420 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_10_GPIO_MUX4_IO19 0x1DC 0x420 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_10_LPSPI2_SIN 0x1DC 0x420 0x5E8 0x6 0x1 + +#define IOMUXC_GPIO_SD_B2_11_USDHC2_DATA7 0x1E0 0x424 0x0 0x0 0x0 +#define IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03 0x1E0 0x424 0x560 0x1 0x1 +#define IOMUXC_GPIO_SD_B2_11_ENET_1G_TX_CLK_IO 0x1E0 0x424 0x4E8 0x2 0x1 +#define IOMUXC_GPIO_SD_B2_11_ENET_1G_REF_CLK 0x1E0 0x424 0x4C4 0x3 0x1 +#define IOMUXC_GPIO_SD_B2_11_GPT6_CLK 0x1E0 0x424 0x0 0x4 0x0 +#define IOMUXC_GPIO_SD_B2_11_GPIO_MUX4_IO20 0x1E0 0x424 0x0 0x5 0x0 +#define IOMUXC_GPIO_SD_B2_11_LPSPI2_PCS1 0x1E0 0x424 0x5E0 0x6 0x1 +#define IOMUXC_GPIO_SD_B2_11_GPIO10_IO20 0x1E0 0x424 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_00_VIDEO_MUX_LCDIF_CLK 0x1E4 0x428 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_00_ENET_1G_RX_EN 0x1E4 0x428 0x4E0 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_00_TMR1_TIMER0 0x1E4 0x428 0x63C 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_00_XBAR1_INOUT26 0x1E4 0x428 0x6F0 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_00_GPIO_MUX4_IO21 0x1E4 0x428 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_00_ENET_QOS_RX_EN 0x1E4 0x428 0x4F8 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_00_GPIO10_IO21 0x1E4 0x428 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_01_VIDEO_MUX_LCDIF_ENABLE 0x1E8 0x42C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_01_ENET_1G_RX_CLK 0x1E8 0x42C 0x4CC 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_01_ENET_1G_RX_ER 0x1E8 0x42C 0x4E4 0x2 0x1 +#define IOMUXC_GPIO_DISP_B1_01_TMR1_TIMER1 0x1E8 0x42C 0x640 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_01_XBAR1_INOUT27 0x1E8 0x42C 0x6F4 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_01_GPIO_MUX4_IO22 0x1E8 0x42C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_01_ENET_QOS_RX_CLK 0x1E8 0x42C 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_01_ENET_QOS_RX_ER 0x1E8 0x42C 0x4FC 0x9 0x0 +#define IOMUXC_GPIO_DISP_B1_01_GPIO10_IO22 0x1E8 0x42C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_02_GPIO10_IO23 0x1EC 0x430 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B1_02_VIDEO_MUX_LCDIF_HSYNC 0x1EC 0x430 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_02_ENET_1G_RX_DATA00 0x1EC 0x430 0x4D0 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_02_LPI2C3_SCL 0x1EC 0x430 0x5BC 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_02_TMR1_TIMER2 0x1EC 0x430 0x644 0x3 0x1 +#define IOMUXC_GPIO_DISP_B1_02_XBAR1_INOUT28 0x1EC 0x430 0x6F8 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_02_GPIO_MUX4_IO23 0x1EC 0x430 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_02_ENET_QOS_RX_DATA00 0x1EC 0x430 0x4F0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_02_LPUART1_TXD 0x1EC 0x430 0x620 0x9 0x1 + +#define IOMUXC_GPIO_DISP_B1_03_VIDEO_MUX_LCDIF_VSYNC 0x1F0 0x434 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_03_ENET_1G_RX_DATA01 0x1F0 0x434 0x4D4 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_03_LPI2C3_SDA 0x1F0 0x434 0x5C0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_03_TMR2_TIMER0 0x1F0 0x434 0x648 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_03_XBAR1_INOUT29 0x1F0 0x434 0x6FC 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_03_GPIO_MUX4_IO24 0x1F0 0x434 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_03_ENET_QOS_RX_DATA01 0x1F0 0x434 0x4F4 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_03_LPUART1_RXD 0x1F0 0x434 0x61C 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_03_GPIO10_IO24 0x1F0 0x434 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_04_VIDEO_MUX_LCDIF_DATA00 0x1F4 0x438 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_04_ENET_1G_RX_DATA02 0x1F4 0x438 0x4D8 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_04_LPUART4_RXD 0x1F4 0x438 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_04_TMR2_TIMER1 0x1F4 0x438 0x64C 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_04_XBAR1_INOUT30 0x1F4 0x438 0x700 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_04_GPIO_MUX4_IO25 0x1F4 0x438 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_04_ENET_QOS_RX_DATA02 0x1F4 0x438 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_04_LPSPI3_SCK 0x1F4 0x438 0x600 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_04_GPIO10_IO25 0x1F4 0x438 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_05_GPIO10_IO26 0x1F8 0x43C 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B1_05_VIDEO_MUX_LCDIF_DATA01 0x1F8 0x43C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_05_ENET_1G_RX_DATA03 0x1F8 0x43C 0x4DC 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_05_LPUART4_CTS_B 0x1F8 0x43C 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_05_TMR2_TIMER2 0x1F8 0x43C 0x650 0x3 0x1 +#define IOMUXC_GPIO_DISP_B1_05_XBAR1_INOUT31 0x1F8 0x43C 0x704 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_05_GPIO_MUX4_IO26 0x1F8 0x43C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_05_ENET_QOS_RX_DATA03 0x1F8 0x43C 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_05_LPSPI3_SIN 0x1F8 0x43C 0x604 0x9 0x1 + +#define IOMUXC_GPIO_DISP_B1_06_VIDEO_MUX_LCDIF_DATA02 0x1FC 0x440 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_06_ENET_1G_TX_DATA03 0x1FC 0x440 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B1_06_LPUART4_TXD 0x1FC 0x440 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_06_TMR3_TIMER0 0x1FC 0x440 0x654 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_06_XBAR1_INOUT32 0x1FC 0x440 0x708 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_06_GPIO_MUX4_IO27 0x1FC 0x440 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_06_SRC_BT_CFG00 0x1FC 0x440 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_06_ENET_QOS_TX_DATA03 0x1FC 0x440 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_06_LPSPI3_SOUT 0x1FC 0x440 0x608 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_06_GPIO10_IO27 0x1FC 0x440 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_07_VIDEO_MUX_LCDIF_DATA03 0x200 0x444 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_07_ENET_1G_TX_DATA02 0x200 0x444 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B1_07_LPUART4_RTS_B 0x200 0x444 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_07_TMR3_TIMER1 0x200 0x444 0x658 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_07_XBAR1_INOUT33 0x200 0x444 0x70C 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_07_GPIO_MUX4_IO28 0x200 0x444 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_07_SRC_BT_CFG01 0x200 0x444 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_07_ENET_QOS_TX_DATA02 0x200 0x444 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_07_LPSPI3_PCS0 0x200 0x444 0x5F0 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_07_GPIO10_IO28 0x200 0x444 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_08_GPIO10_IO29 0x204 0x448 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B1_08_VIDEO_MUX_LCDIF_DATA04 0x204 0x448 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_08_ENET_1G_TX_DATA01 0x204 0x448 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B1_08_USDHC1_CD_B 0x204 0x448 0x6C8 0x2 0x1 +#define IOMUXC_GPIO_DISP_B1_08_TMR3_TIMER2 0x204 0x448 0x65C 0x3 0x1 +#define IOMUXC_GPIO_DISP_B1_08_XBAR1_INOUT34 0x204 0x448 0x710 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_08_GPIO_MUX4_IO29 0x204 0x448 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_08_SRC_BT_CFG02 0x204 0x448 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_08_ENET_QOS_TX_DATA01 0x204 0x448 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_08_LPSPI3_PCS1 0x204 0x448 0x5F4 0x9 0x1 + +#define IOMUXC_GPIO_DISP_B1_09_VIDEO_MUX_LCDIF_DATA05 0x208 0x44C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_09_ENET_1G_TX_DATA00 0x208 0x44C 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B1_09_USDHC1_WP 0x208 0x44C 0x6CC 0x2 0x1 +#define IOMUXC_GPIO_DISP_B1_09_TMR4_TIMER0 0x208 0x44C 0x660 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_09_XBAR1_INOUT35 0x208 0x44C 0x714 0x4 0x1 +#define IOMUXC_GPIO_DISP_B1_09_GPIO_MUX4_IO30 0x208 0x44C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_09_SRC_BT_CFG03 0x208 0x44C 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_09_ENET_QOS_TX_DATA00 0x208 0x44C 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_09_LPSPI3_PCS2 0x208 0x44C 0x5F8 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_09_GPIO10_IO30 0x208 0x44C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_10_VIDEO_MUX_LCDIF_DATA06 0x20C 0x450 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_10_ENET_1G_TX_EN 0x20C 0x450 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B1_10_USDHC1_RESET_B 0x20C 0x450 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B1_10_TMR4_TIMER1 0x20C 0x450 0x664 0x3 0x2 +#define IOMUXC_GPIO_DISP_B1_10_XBAR1_INOUT36 0x20C 0x450 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B1_10_GPIO_MUX4_IO31 0x20C 0x450 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_10_SRC_BT_CFG04 0x20C 0x450 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_10_ENET_QOS_TX_EN 0x20C 0x450 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_10_LPSPI3_PCS3 0x20C 0x450 0x5FC 0x9 0x1 +#define IOMUXC_GPIO_DISP_B1_10_GPIO10_IO31 0x20C 0x450 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B1_11_VIDEO_MUX_LCDIF_DATA07 0x210 0x454 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B1_11_ENET_1G_TX_CLK_IO 0x210 0x454 0x4E8 0x1 0x2 +#define IOMUXC_GPIO_DISP_B1_11_ENET_1G_REF_CLK 0x210 0x454 0x4C4 0x2 0x2 +#define IOMUXC_GPIO_DISP_B1_11_TMR4_TIMER2 0x210 0x454 0x668 0x3 0x1 +#define IOMUXC_GPIO_DISP_B1_11_XBAR1_INOUT37 0x210 0x454 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B1_11_GPIO_MUX5_IO00 0x210 0x454 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B1_11_SRC_BT_CFG05 0x210 0x454 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B1_11_ENET_QOS_TX_CLK 0x210 0x454 0x4A4 0x8 0x0 +#define IOMUXC_GPIO_DISP_B1_11_ENET_QOS_REF_CLK 0x210 0x454 0x4A0 0x9 0x2 +#define IOMUXC_GPIO_DISP_B1_11_GPIO11_IO00 0x210 0x454 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B2_00_GPIO11_IO01 0x214 0x458 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_00_VIDEO_MUX_LCDIF_DATA08 0x214 0x458 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_00_WDOG1_B 0x214 0x458 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_00_MQS_RIGHT 0x214 0x458 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_00_ENET_1G_TX_ER 0x214 0x458 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_00_SAI1_TX_DATA03 0x214 0x458 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_00_GPIO_MUX5_IO01 0x214 0x458 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_00_SRC_BT_CFG06 0x214 0x458 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_00_ENET_QOS_TX_ER 0x214 0x458 0x0 0x8 0x0 + +#define IOMUXC_GPIO_DISP_B2_01_VIDEO_MUX_LCDIF_DATA09 0x218 0x45C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_01_USDHC1_VSELECT 0x218 0x45C 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_01_MQS_LEFT 0x218 0x45C 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_01_WDOG2_B 0x218 0x45C 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_01_SAI1_TX_DATA02 0x218 0x45C 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_01_GPIO_MUX5_IO02 0x218 0x45C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_01_SRC_BT_CFG07 0x218 0x45C 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_01_EWM_OUT_B 0x218 0x45C 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_01_CCM_ENET_REF_CLK_25M 0x218 0x45C 0x0 0x9 0x0 +#define IOMUXC_GPIO_DISP_B2_01_GPIO11_IO02 0x218 0x45C 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B2_02_GPIO11_IO03 0x21C 0x460 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_02_VIDEO_MUX_LCDIF_DATA10 0x21C 0x460 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_02_ENET_TX_DATA00 0x21C 0x460 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_02_PIT1_TRIGGER3 0x21C 0x460 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_02_ARM_TRACE00 0x21C 0x460 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_02_SAI1_TX_DATA01 0x21C 0x460 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_02_GPIO_MUX5_IO03 0x21C 0x460 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_02_SRC_BT_CFG08 0x21C 0x460 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_02_ENET_QOS_TX_DATA00 0x21C 0x460 0x0 0x8 0x0 + +#define IOMUXC_GPIO_DISP_B2_03_GPIO11_IO04 0x220 0x464 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_03_VIDEO_MUX_LCDIF_DATA11 0x220 0x464 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_03_ENET_TX_DATA01 0x220 0x464 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_03_PIT1_TRIGGER2 0x220 0x464 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_03_ARM_TRACE01 0x220 0x464 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_03_SAI1_MCLK 0x220 0x464 0x66C 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_03_GPIO_MUX5_IO04 0x220 0x464 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_03_SRC_BT_CFG09 0x220 0x464 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_03_ENET_QOS_TX_DATA01 0x220 0x464 0x0 0x8 0x0 + +#define IOMUXC_GPIO_DISP_B2_04_VIDEO_MUX_LCDIF_DATA12 0x224 0x468 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_04_ENET_TX_EN 0x224 0x468 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_04_PIT1_TRIGGER1 0x224 0x468 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_04_ARM_TRACE02 0x224 0x468 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_04_SAI1_RX_SYNC 0x224 0x468 0x678 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_04_GPIO_MUX5_IO05 0x224 0x468 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_04_SRC_BT_CFG10 0x224 0x468 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_04_ENET_QOS_TX_EN 0x224 0x468 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_04_GPIO11_IO05 0x224 0x468 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B2_05_GPIO11_IO06 0x228 0x46C 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_05_VIDEO_MUX_LCDIF_DATA13 0x228 0x46C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_05_ENET_TX_CLK 0x228 0x46C 0x4C0 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_05_ENET_REF_CLK 0x228 0x46C 0x4A8 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_05_ARM_TRACE03 0x228 0x46C 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_05_SAI1_RX_BCLK 0x228 0x46C 0x670 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_05_GPIO_MUX5_IO06 0x228 0x46C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_05_SRC_BT_CFG11 0x228 0x46C 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_05_ENET_QOS_TX_CLK 0x228 0x46C 0x4A4 0x8 0x1 + +#define IOMUXC_GPIO_DISP_B2_06_GPIO11_IO07 0x22C 0x470 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_06_VIDEO_MUX_LCDIF_DATA14 0x22C 0x470 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_06_ENET_RX_DATA00 0x22C 0x470 0x4B0 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_06_LPUART7_TXD 0x22C 0x470 0x630 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_06_ARM_TRACE_CLK 0x22C 0x470 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_06_SAI1_RX_DATA00 0x22C 0x470 0x674 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_06_GPIO_MUX5_IO07 0x22C 0x470 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_06_ENET_QOS_RX_DATA00 0x22C 0x470 0x4F0 0x8 0x1 + +#define IOMUXC_GPIO_DISP_B2_07_VIDEO_MUX_LCDIF_DATA15 0x230 0x474 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_07_ENET_RX_DATA01 0x230 0x474 0x4B4 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_07_LPUART7_RXD 0x230 0x474 0x62C 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_07_ARM_TRACE_SWO 0x230 0x474 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_07_SAI1_TX_DATA00 0x230 0x474 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_07_GPIO_MUX5_IO08 0x230 0x474 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_07_ENET_QOS_RX_DATA01 0x230 0x474 0x4F4 0x8 0x1 +#define IOMUXC_GPIO_DISP_B2_07_GPIO11_IO08 0x230 0x474 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B2_08_GPIO11_IO09 0x234 0x478 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_08_VIDEO_MUX_LCDIF_DATA16 0x234 0x478 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_08_ENET_RX_EN 0x234 0x478 0x4B8 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_08_LPUART8_TXD 0x234 0x478 0x638 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_08_ARM_CM7_EVENTO 0x234 0x478 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_08_SAI1_TX_BCLK 0x234 0x478 0x67C 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_08_GPIO_MUX5_IO09 0x234 0x478 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_08_ENET_QOS_RX_EN 0x234 0x478 0x4F8 0x8 0x1 +#define IOMUXC_GPIO_DISP_B2_08_LPUART1_TXD 0x234 0x478 0x620 0x9 0x2 + +#define IOMUXC_GPIO_DISP_B2_09_GPIO11_IO10 0x238 0x47C 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_09_VIDEO_MUX_LCDIF_DATA17 0x238 0x47C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_09_ENET_RX_ER 0x238 0x47C 0x4BC 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_09_LPUART8_RXD 0x238 0x47C 0x634 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_09_ARM_CM7_EVENTI 0x238 0x47C 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_09_SAI1_TX_SYNC 0x238 0x47C 0x680 0x4 0x1 +#define IOMUXC_GPIO_DISP_B2_09_GPIO_MUX5_IO10 0x238 0x47C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_09_ENET_QOS_RX_ER 0x238 0x47C 0x4FC 0x8 0x1 +#define IOMUXC_GPIO_DISP_B2_09_LPUART1_RXD 0x238 0x47C 0x61C 0x9 0x2 + +#define IOMUXC_GPIO_DISP_B2_10_GPIO11_IO11 0x23C 0x480 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_10_VIDEO_MUX_LCDIF_DATA18 0x23C 0x480 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_10_EMVSIM2_IO 0x23C 0x480 0x6A8 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_10_LPUART2_TXD 0x23C 0x480 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_10_WDOG2_RESET_B_DEB 0x23C 0x480 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_10_XBAR1_INOUT38 0x23C 0x480 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_10_GPIO_MUX5_IO11 0x23C 0x480 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_10_LPI2C3_SCL 0x23C 0x480 0x5BC 0x6 0x1 +#define IOMUXC_GPIO_DISP_B2_10_ENET_QOS_RX_ER 0x23C 0x480 0x4FC 0x8 0x2 +#define IOMUXC_GPIO_DISP_B2_10_SPDIF_IN 0x23C 0x480 0x6B4 0x9 0x2 + +#define IOMUXC_GPIO_DISP_B2_11_VIDEO_MUX_LCDIF_DATA19 0x240 0x484 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_11_EMVSIM2_CLK 0x240 0x484 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_11_LPUART2_RXD 0x240 0x484 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_11_WDOG1_RESET_B_DEB 0x240 0x484 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_11_XBAR1_INOUT39 0x240 0x484 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_11_GPIO_MUX5_IO12 0x240 0x484 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_11_LPI2C3_SDA 0x240 0x484 0x5C0 0x6 0x1 +#define IOMUXC_GPIO_DISP_B2_11_ENET_QOS_CRS 0x240 0x484 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_11_SPDIF_OUT 0x240 0x484 0x0 0x9 0x0 +#define IOMUXC_GPIO_DISP_B2_11_GPIO11_IO12 0x240 0x484 0x0 0xA 0x0 + +#define IOMUXC_GPIO_DISP_B2_12_GPIO11_IO13 0x244 0x488 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_12_VIDEO_MUX_LCDIF_DATA20 0x244 0x488 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_12_EMVSIM2_RST 0x244 0x488 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_12_FLEXCAN1_TX 0x244 0x488 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_12_LPUART2_CTS_B 0x244 0x488 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_12_XBAR1_INOUT40 0x244 0x488 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_12_GPIO_MUX5_IO13 0x244 0x488 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_12_LPI2C4_SCL 0x244 0x488 0x5C4 0x6 0x1 +#define IOMUXC_GPIO_DISP_B2_12_ENET_QOS_COL 0x244 0x488 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_12_LPSPI4_SCK 0x244 0x488 0x610 0x9 0x1 + +#define IOMUXC_GPIO_DISP_B2_13_GPIO11_IO14 0x248 0x48C 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_13_VIDEO_MUX_LCDIF_DATA21 0x248 0x48C 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_13_EMVSIM2_SVEN 0x248 0x48C 0x0 0x1 0x0 +#define IOMUXC_GPIO_DISP_B2_13_FLEXCAN1_RX 0x248 0x48C 0x498 0x2 0x1 +#define IOMUXC_GPIO_DISP_B2_13_LPUART2_RTS_B 0x248 0x48C 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_13_ENET_REF_CLK 0x248 0x48C 0x4A8 0x4 0x2 +#define IOMUXC_GPIO_DISP_B2_13_GPIO_MUX5_IO14 0x248 0x48C 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_13_LPI2C4_SDA 0x248 0x48C 0x5C8 0x6 0x1 +#define IOMUXC_GPIO_DISP_B2_13_ENET_QOS_1588_EVENT0_OUT 0x248 0x48C 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_13_LPSPI4_SIN 0x248 0x48C 0x614 0x9 0x1 + +#define IOMUXC_GPIO_DISP_B2_14_GPIO_MUX5_IO15 0x24C 0x490 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_14_FLEXCAN1_TX 0x24C 0x490 0x0 0x6 0x0 +#define IOMUXC_GPIO_DISP_B2_14_ENET_QOS_1588_EVENT0_IN 0x24C 0x490 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_14_LPSPI4_SOUT 0x24C 0x490 0x618 0x9 0x1 +#define IOMUXC_GPIO_DISP_B2_14_GPIO11_IO15 0x24C 0x490 0x0 0xA 0x0 +#define IOMUXC_GPIO_DISP_B2_14_VIDEO_MUX_LCDIF_DATA22 0x24C 0x490 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_14_EMVSIM2_PD 0x24C 0x490 0x6AC 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_14_WDOG2_B 0x24C 0x490 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_14_VIDEO_MUX_EXT_DCIC1 0x24C 0x490 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_14_ENET_1G_REF_CLK 0x24C 0x490 0x4C4 0x4 0x3 + +#define IOMUXC_GPIO_DISP_B2_15_VIDEO_MUX_LCDIF_DATA23 0x250 0x494 0x0 0x0 0x0 +#define IOMUXC_GPIO_DISP_B2_15_EMVSIM2_POWER_FAIL 0x250 0x494 0x6B0 0x1 0x1 +#define IOMUXC_GPIO_DISP_B2_15_WDOG1_B 0x250 0x494 0x0 0x2 0x0 +#define IOMUXC_GPIO_DISP_B2_15_VIDEO_MUX_EXT_DCIC2 0x250 0x494 0x0 0x3 0x0 +#define IOMUXC_GPIO_DISP_B2_15_PIT1_TRIGGER0 0x250 0x494 0x0 0x4 0x0 +#define IOMUXC_GPIO_DISP_B2_15_GPIO_MUX5_IO16 0x250 0x494 0x0 0x5 0x0 +#define IOMUXC_GPIO_DISP_B2_15_FLEXCAN1_RX 0x250 0x494 0x498 0x6 0x2 +#define IOMUXC_GPIO_DISP_B2_15_ENET_QOS_1588_EVENT0_AUX_IN 0x250 0x494 0x0 0x8 0x0 +#define IOMUXC_GPIO_DISP_B2_15_LPSPI4_PCS0 0x250 0x494 0x60C 0x9 0x1 +#define IOMUXC_GPIO_DISP_B2_15_GPIO11_IO16 0x250 0x494 0x0 0xA 0x0 + +#endif /* _DT_BINDINGS_PINCTRL_IMXRT1170_PINFUNC_H */ diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 70e634b37aae..6cdadba6a3ac 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -1389,7 +1389,6 @@ <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; clocks = <&apb0_gates 0>, <&osc24M>, <&rtc CLK_OSC32K>; clock-names = "apb", "hosc", "losc"; - resets = <&apb0_rst 0>; gpio-controller; interrupt-controller; #interrupt-cells = <3>; diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi b/arch/arm/boot/dts/sun8i-a23-a33.dtsi index 1a262a05fdcb..f630ab55bb6a 100644 --- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi +++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi @@ -814,7 +814,6 @@ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>; clocks = <&apb0_gates 0>, <&osc24M>, <&rtc CLK_OSC32K>; clock-names = "apb", "hosc", "losc"; - resets = <&apb0_rst 0>; gpio-controller; interrupt-controller; #interrupt-cells = <3>; diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index ce4fa6706d06..7d3f3300f431 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -1218,7 +1218,6 @@ <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; clocks = <&apbs_gates 0>, <&osc24M>, <&osc32k>; clock-names = "apb", "hosc", "losc"; - resets = <&apbs_rst 0>; gpio-controller; interrupt-controller; #interrupt-cells = <3>; diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c index fc4f710e9820..5f9379b3c8c8 100644 --- a/arch/arm64/mm/flush.c +++ b/arch/arm64/mm/flush.c @@ -76,17 +76,10 @@ EXPORT_SYMBOL_GPL(__sync_icache_dcache); void flush_dcache_page(struct page *page) { /* - * Only the head page's flags of HugeTLB can be cleared since the tail - * vmemmap pages associated with each HugeTLB page are mapped with - * read-only when CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP is enabled (more - * details can refer to vmemmap_remap_pte()). Although - * __sync_icache_dcache() only set PG_dcache_clean flag on the head - * page struct, there is more than one page struct with PG_dcache_clean - * associated with the HugeTLB page since the head vmemmap page frame - * is reused (more details can refer to the comments above - * page_fixed_fake_head()). + * HugeTLB pages are always fully mapped and only head page will be + * set PG_dcache_clean (see comments in __sync_icache_dcache()). */ - if (hugetlb_optimize_vmemmap_enabled() && PageHuge(page)) + if (PageHuge(page)) page = compound_head(page); if (test_bit(PG_dcache_clean, &page->flags)) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 7ca8779ae34f..389623ae5a91 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -1496,7 +1496,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) memset(&ctx, 0, sizeof(ctx)); ctx.prog = prog; - ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); + ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); if (ctx.offset == NULL) { prog = orig_prog; goto out_off; @@ -1601,7 +1601,7 @@ skip_init_ctx: ctx.offset[i] *= AARCH64_INSN_SIZE; bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); out_off: - kfree(ctx.offset); + kvfree(ctx.offset); kfree(jit_data); prog->aux->jit_data = NULL; } @@ -1643,7 +1643,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, int args_off, int retval_off, int run_ctx_off, bool save_ret) { - u32 *branch; + __le32 *branch; u64 enter_prog; u64 exit_prog; struct bpf_prog *p = l->link.prog; @@ -1698,7 +1698,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, if (ctx->image) { int offset = &ctx->image[ctx->idx] - branch; - *branch = A64_CBZ(1, A64_R(0), offset); + *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); } /* arg1: prog */ @@ -1713,7 +1713,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, int args_off, int retval_off, int run_ctx_off, - u32 **branches) + __le32 **branches) { int i; @@ -1784,7 +1784,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; bool save_ret; - u32 **branches = NULL; + __le32 **branches = NULL; /* trampoline stack layout: * [ parent ip ] @@ -1892,7 +1892,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, flags & BPF_TRAMP_F_RET_FENTRY_RET); if (fmod_ret->nr_links) { - branches = kcalloc(fmod_ret->nr_links, sizeof(u32 *), + branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), GFP_KERNEL); if (!branches) return -ENOMEM; @@ -1916,7 +1916,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, /* update the branches saved in invoke_bpf_mod_ret with cbnz */ for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { int offset = &ctx->image[ctx->idx] - branches[i]; - *branches[i] = A64_CBNZ(1, A64_R(10), offset); + *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); } for (i = 0; i < fexit->nr_links; i++) diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c index 4218750414bb..7dab46728aed 100644 --- a/arch/m68k/coldfire/device.c +++ b/arch/m68k/coldfire/device.c @@ -581,7 +581,7 @@ static struct platform_device mcf_esdhc = { }; #endif /* MCFSDHC_BASE */ -#if IS_ENABLED(CONFIG_CAN_FLEXCAN) +#ifdef MCFFLEXCAN_SIZE #include <linux/can/platform/flexcan.h> @@ -620,7 +620,7 @@ static struct platform_device mcf_flexcan0 = { .resource = mcf5441x_flexcan0_resource, .dev.platform_data = &mcf5441x_flexcan_info, }; -#endif /* IS_ENABLED(CONFIG_CAN_FLEXCAN) */ +#endif /* MCFFLEXCAN_SIZE */ static struct platform_device *mcf_devices[] __initdata = { &mcf_uart, @@ -657,7 +657,7 @@ static struct platform_device *mcf_devices[] __initdata = { #ifdef MCFSDHC_BASE &mcf_esdhc, #endif -#if IS_ENABLED(CONFIG_CAN_FLEXCAN) +#ifdef MCFFLEXCAN_SIZE &mcf_flexcan0, #endif }; diff --git a/arch/m68k/coldfire/intc-2.c b/arch/m68k/coldfire/intc-2.c index 995093357c59..f74f0e473119 100644 --- a/arch/m68k/coldfire/intc-2.c +++ b/arch/m68k/coldfire/intc-2.c @@ -7,7 +7,7 @@ * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such * controllers, and the 547x and 548x families which have only one of them. * - * The external 7 fixed interrupts are part the the Edge Port unit of these + * The external 7 fixed interrupts are part of the Edge Port unit of these * ColdFire parts. They can be configured as level or edge triggered. * * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com> diff --git a/arch/m68k/coldfire/m523x.c b/arch/m68k/coldfire/m523x.c index 193c178162c1..83a997313393 100644 --- a/arch/m68k/coldfire/m523x.c +++ b/arch/m68k/coldfire/m523x.c @@ -28,7 +28,7 @@ DEFINE_CLK(pll, "pll.0", MCF_CLK); DEFINE_CLK(sys, "sys.0", MCF_BUSCLK); -struct clk_lookup m523x_clk_lookup[] = { +static struct clk_lookup m523x_clk_lookup[] = { CLKDEV_INIT(NULL, "pll.0", &clk_pll), CLKDEV_INIT(NULL, "sys.0", &clk_sys), CLKDEV_INIT("mcfpit.0", NULL, &clk_pll), diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 7b0d286bf9ba..01772e79fd93 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -55,6 +55,7 @@ int memory_add_physaddr_to_nid(u64 start) { return hot_add_scn_to_nid(start); } +EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid); #endif int __weak create_section_mapping(unsigned long start, unsigned long end, diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index b5aecb524a8a..ffec8bb01ba8 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -103,7 +103,7 @@ $(obj)/zoffset.h: $(obj)/compressed/vmlinux FORCE AFLAGS_header.o += -I$(objtree)/$(obj) $(obj)/header.o: $(obj)/zoffset.h -LDFLAGS_setup.elf := -m elf_i386 -T +LDFLAGS_setup.elf := -m elf_i386 -z noexecstack -T $(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE $(call if_changed,ld) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 19e1905dcbf6..35ce1a64068b 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -69,6 +69,10 @@ LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker) ifdef CONFIG_LD_ORPHAN_WARN LDFLAGS_vmlinux += --orphan-handling=warn endif +LDFLAGS_vmlinux += -z noexecstack +ifeq ($(CONFIG_LD_IS_BFD),y) +LDFLAGS_vmlinux += $(call ld-option,--no-warn-rwx-segments) +endif LDFLAGS_vmlinux += -T hostprogs := mkpiggy diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 76cd790ed0bd..12f6c4d714cd 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -180,7 +180,7 @@ quiet_cmd_vdso = VDSO $@ sh $(srctree)/$(src)/checkundef.sh '$(NM)' '$@' VDSO_LDFLAGS = -shared --hash-style=both --build-id=sha1 \ - $(call ld-option, --eh-frame-hdr) -Bsymbolic + $(call ld-option, --eh-frame-hdr) -Bsymbolic -z noexecstack GCOV_PROFILE := n quiet_cmd_vdso_and_check = VDSO $@ diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 14ed039dff55..235dc85c91c3 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -303,6 +303,7 @@ #define X86_FEATURE_RETHUNK (11*32+14) /* "" Use REturn THUNK */ #define X86_FEATURE_UNRET (11*32+15) /* "" AMD BTB untrain return */ #define X86_FEATURE_USE_IBPB_FW (11*32+16) /* "" Use IBPB during runtime firmware calls */ +#define X86_FEATURE_RSB_VMEXIT_LITE (11*32+17) /* "" Fill RSB on VM exit when EIBRS is enabled */ /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ @@ -457,5 +458,6 @@ #define X86_BUG_SRBDS X86_BUG(24) /* CPU may leak RNG bits if not mitigated */ #define X86_BUG_MMIO_STALE_DATA X86_BUG(25) /* CPU is affected by Processor MMIO Stale Data vulnerabilities */ #define X86_BUG_RETBLEED X86_BUG(26) /* CPU is affected by RETBleed */ +#define X86_BUG_EIBRS_PBRSB X86_BUG(27) /* EIBRS is vulnerable to Post Barrier RSB Predictions */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index e8281d64a431..5ffa578cafe1 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1704,7 +1704,7 @@ static inline int kvm_arch_flush_remote_tlb(struct kvm *kvm) #define kvm_arch_pmi_in_guest(vcpu) \ ((vcpu) && (vcpu)->arch.handling_intr_from_guest) -void kvm_mmu_x86_module_init(void); +void __init kvm_mmu_x86_module_init(void); int kvm_mmu_vendor_module_init(void); void kvm_mmu_vendor_module_exit(void); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 182b2a1f71fe..6674bdb096f3 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -150,6 +150,10 @@ * are restricted to targets in * kernel. */ +#define ARCH_CAP_PBRSB_NO BIT(24) /* + * Not susceptible to Post-Barrier + * Return Stack Buffer Predictions. + */ #define MSR_IA32_FLUSH_CMD 0x0000010b #define L1D_FLUSH BIT(0) /* diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index cba942006ffe..e64fd20778b6 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -60,7 +60,9 @@ 774: \ add $(BITS_PER_LONG/8) * 2, sp; \ dec reg; \ - jnz 771b; + jnz 771b; \ + /* barrier for jnz misprediction */ \ + lfence; #ifdef __ASSEMBLY__ @@ -130,13 +132,28 @@ #endif .endm +.macro ISSUE_UNBALANCED_RET_GUARD + ANNOTATE_INTRA_FUNCTION_CALL + call .Lunbalanced_ret_guard_\@ + int3 +.Lunbalanced_ret_guard_\@: + add $(BITS_PER_LONG/8), %_ASM_SP + lfence +.endm + /* * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP * monstrosity above, manually. */ -.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req +.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req ftr2 +.ifb \ftr2 ALTERNATIVE "jmp .Lskip_rsb_\@", "", \ftr +.else + ALTERNATIVE_2 "jmp .Lskip_rsb_\@", "", \ftr, "jmp .Lunbalanced_\@", \ftr2 +.endif __FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP) +.Lunbalanced_\@: + ISSUE_UNBALANCED_RET_GUARD .Lskip_rsb_\@: .endm diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 6761668100b9..9f7e751b91df 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -1335,6 +1335,53 @@ static void __init spec_ctrl_disable_kernel_rrsba(void) } } +static void __init spectre_v2_determine_rsb_fill_type_at_vmexit(enum spectre_v2_mitigation mode) +{ + /* + * Similar to context switches, there are two types of RSB attacks + * after VM exit: + * + * 1) RSB underflow + * + * 2) Poisoned RSB entry + * + * When retpoline is enabled, both are mitigated by filling/clearing + * the RSB. + * + * When IBRS is enabled, while #1 would be mitigated by the IBRS branch + * prediction isolation protections, RSB still needs to be cleared + * because of #2. Note that SMEP provides no protection here, unlike + * user-space-poisoned RSB entries. + * + * eIBRS should protect against RSB poisoning, but if the EIBRS_PBRSB + * bug is present then a LITE version of RSB protection is required, + * just a single call needs to retire before a RET is executed. + */ + switch (mode) { + case SPECTRE_V2_NONE: + return; + + case SPECTRE_V2_EIBRS_LFENCE: + case SPECTRE_V2_EIBRS: + if (boot_cpu_has_bug(X86_BUG_EIBRS_PBRSB)) { + setup_force_cpu_cap(X86_FEATURE_RSB_VMEXIT_LITE); + pr_info("Spectre v2 / PBRSB-eIBRS: Retire a single CALL on VMEXIT\n"); + } + return; + + case SPECTRE_V2_EIBRS_RETPOLINE: + case SPECTRE_V2_RETPOLINE: + case SPECTRE_V2_LFENCE: + case SPECTRE_V2_IBRS: + setup_force_cpu_cap(X86_FEATURE_RSB_VMEXIT); + pr_info("Spectre v2 / SpectreRSB : Filling RSB on VMEXIT\n"); + return; + } + + pr_warn_once("Unknown Spectre v2 mode, disabling RSB mitigation at VM exit"); + dump_stack(); +} + static void __init spectre_v2_select_mitigation(void) { enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline(); @@ -1485,28 +1532,7 @@ static void __init spectre_v2_select_mitigation(void) setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW); pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n"); - /* - * Similar to context switches, there are two types of RSB attacks - * after vmexit: - * - * 1) RSB underflow - * - * 2) Poisoned RSB entry - * - * When retpoline is enabled, both are mitigated by filling/clearing - * the RSB. - * - * When IBRS is enabled, while #1 would be mitigated by the IBRS branch - * prediction isolation protections, RSB still needs to be cleared - * because of #2. Note that SMEP provides no protection here, unlike - * user-space-poisoned RSB entries. - * - * eIBRS, on the other hand, has RSB-poisoning protections, so it - * doesn't need RSB clearing after vmexit. - */ - if (boot_cpu_has(X86_FEATURE_RETPOLINE) || - boot_cpu_has(X86_FEATURE_KERNEL_IBRS)) - setup_force_cpu_cap(X86_FEATURE_RSB_VMEXIT); + spectre_v2_determine_rsb_fill_type_at_vmexit(mode); /* * Retpoline protects the kernel, but doesn't protect firmware. IBRS @@ -2292,6 +2318,19 @@ static char *ibpb_state(void) return ""; } +static char *pbrsb_eibrs_state(void) +{ + if (boot_cpu_has_bug(X86_BUG_EIBRS_PBRSB)) { + if (boot_cpu_has(X86_FEATURE_RSB_VMEXIT_LITE) || + boot_cpu_has(X86_FEATURE_RSB_VMEXIT)) + return ", PBRSB-eIBRS: SW sequence"; + else + return ", PBRSB-eIBRS: Vulnerable"; + } else { + return ", PBRSB-eIBRS: Not affected"; + } +} + static ssize_t spectre_v2_show_state(char *buf) { if (spectre_v2_enabled == SPECTRE_V2_LFENCE) @@ -2304,12 +2343,13 @@ static ssize_t spectre_v2_show_state(char *buf) spectre_v2_enabled == SPECTRE_V2_EIBRS_LFENCE) return sprintf(buf, "Vulnerable: eIBRS+LFENCE with unprivileged eBPF and SMT\n"); - return sprintf(buf, "%s%s%s%s%s%s\n", + return sprintf(buf, "%s%s%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], ibpb_state(), boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "", stibp_state(), boot_cpu_has(X86_FEATURE_RSB_CTXSW) ? ", RSB filling" : "", + pbrsb_eibrs_state(), spectre_v2_module_string()); } diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 736262a76a12..64a73f415f03 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1135,6 +1135,7 @@ static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c) #define NO_SWAPGS BIT(6) #define NO_ITLB_MULTIHIT BIT(7) #define NO_SPECTRE_V2 BIT(8) +#define NO_EIBRS_PBRSB BIT(9) #define VULNWL(vendor, family, model, whitelist) \ X86_MATCH_VENDOR_FAM_MODEL(vendor, family, model, whitelist) @@ -1177,7 +1178,7 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT), VULNWL_INTEL(ATOM_GOLDMONT_D, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT), - VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT), + VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT | NO_EIBRS_PBRSB), /* * Technically, swapgs isn't serializing on AMD (despite it previously @@ -1187,7 +1188,9 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { * good enough for our purposes. */ - VULNWL_INTEL(ATOM_TREMONT_D, NO_ITLB_MULTIHIT), + VULNWL_INTEL(ATOM_TREMONT, NO_EIBRS_PBRSB), + VULNWL_INTEL(ATOM_TREMONT_L, NO_EIBRS_PBRSB), + VULNWL_INTEL(ATOM_TREMONT_D, NO_ITLB_MULTIHIT | NO_EIBRS_PBRSB), /* AMD Family 0xf - 0x12 */ VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT), @@ -1365,6 +1368,11 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) setup_force_cpu_bug(X86_BUG_RETBLEED); } + if (cpu_has(c, X86_FEATURE_IBRS_ENHANCED) && + !cpu_matches(cpu_vuln_whitelist, NO_EIBRS_PBRSB) && + !(ia32_cap & ARCH_CAP_PBRSB_NO)) + setup_force_cpu_bug(X86_BUG_EIBRS_PBRSB); + if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN)) return; diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 047c583596bb..b4eeb7c75dfa 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -4578,6 +4578,10 @@ static const struct mode_dual mode_dual_63 = { N, I(DstReg | SrcMem32 | ModRM | Mov, em_movsxd) }; +static const struct instr_dual instr_dual_8d = { + D(DstReg | SrcMem | ModRM | NoAccess), N +}; + static const struct opcode opcode_table[256] = { /* 0x00 - 0x07 */ F6ALU(Lock, em_add), @@ -4634,7 +4638,7 @@ static const struct opcode opcode_table[256] = { I2bv(DstMem | SrcReg | ModRM | Mov | PageTable, em_mov), I2bv(DstReg | SrcMem | ModRM | Mov, em_mov), I(DstMem | SrcNone | ModRM | Mov | PageTable, em_mov_rm_sreg), - D(ModRM | SrcMem | NoAccess | DstReg), + ID(0, &instr_dual_8d), I(ImplicitOps | SrcMem16 | ModRM, em_mov_sreg_rm), G(0, group1A), /* 0x90 - 0x97 */ diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index e2ce3556915e..9dda989a1cf0 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2284,10 +2284,12 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) struct kvm_lapic *apic = vcpu->arch.apic; u64 val; - if (apic_x2apic_mode(apic)) - kvm_lapic_msr_read(apic, offset, &val); - else + if (apic_x2apic_mode(apic)) { + if (KVM_BUG_ON(kvm_lapic_msr_read(apic, offset, &val), vcpu->kvm)) + return; + } else { val = kvm_lapic_get_reg(apic, offset); + } /* * ICR is a single 64-bit register when x2APIC is enabled. For legacy diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index a99acec925eb..6bdaacb6faa0 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -6,6 +6,8 @@ #include "kvm_cache_regs.h" #include "cpuid.h" +extern bool __read_mostly enable_mmio_caching; + #define PT_WRITABLE_SHIFT 1 #define PT_USER_SHIFT 2 diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 06ac8c7cef67..eccddb136954 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4164,7 +4164,7 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) if (!fault->prefetch && kvm_can_do_async_pf(vcpu)) { trace_kvm_try_async_get_page(fault->addr, fault->gfn); if (kvm_find_async_pf_gfn(vcpu, fault->gfn)) { - trace_kvm_async_pf_doublefault(fault->addr, fault->gfn); + trace_kvm_async_pf_repeated_fault(fault->addr, fault->gfn); kvm_make_request(KVM_REQ_APF_HALT, vcpu); return RET_PF_RETRY; } else if (kvm_arch_setup_async_pf(vcpu, fault->addr, fault->gfn)) { @@ -6697,11 +6697,15 @@ static int set_nx_huge_pages(const char *val, const struct kernel_param *kp) /* * nx_huge_pages needs to be resolved to true/false when kvm.ko is loaded, as * its default value of -1 is technically undefined behavior for a boolean. + * Forward the module init call to SPTE code so that it too can handle module + * params that need to be resolved/snapshot. */ -void kvm_mmu_x86_module_init(void) +void __init kvm_mmu_x86_module_init(void) { if (nx_huge_pages == -1) __set_nx_huge_pages(get_nx_auto_mode()); + + kvm_mmu_spte_module_init(); } /* diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 7314d27d57a4..2e08b2a45361 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -20,7 +20,9 @@ #include <asm/vmx.h> bool __read_mostly enable_mmio_caching = true; +static bool __ro_after_init allow_mmio_caching; module_param_named(mmio_caching, enable_mmio_caching, bool, 0444); +EXPORT_SYMBOL_GPL(enable_mmio_caching); u64 __read_mostly shadow_host_writable_mask; u64 __read_mostly shadow_mmu_writable_mask; @@ -43,6 +45,18 @@ u64 __read_mostly shadow_nonpresent_or_rsvd_lower_gfn_mask; u8 __read_mostly shadow_phys_bits; +void __init kvm_mmu_spte_module_init(void) +{ + /* + * Snapshot userspace's desire to allow MMIO caching. Whether or not + * KVM can actually enable MMIO caching depends on vendor-specific + * hardware capabilities and other module params that can't be resolved + * until the vendor module is loaded, i.e. enable_mmio_caching can and + * will change when the vendor module is (re)loaded. + */ + allow_mmio_caching = enable_mmio_caching; +} + static u64 generation_mmio_spte_mask(u64 gen) { u64 mask; @@ -340,10 +354,24 @@ void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 mmio_mask, u64 access_mask) BUG_ON((u64)(unsigned)access_mask != access_mask); WARN_ON(mmio_value & shadow_nonpresent_or_rsvd_lower_gfn_mask); + /* + * Reset to the original module param value to honor userspace's desire + * to (dis)allow MMIO caching. Update the param itself so that + * userspace can see whether or not KVM is actually using MMIO caching. + */ + enable_mmio_caching = allow_mmio_caching; if (!enable_mmio_caching) mmio_value = 0; /* + * The mask must contain only bits that are carved out specifically for + * the MMIO SPTE mask, e.g. to ensure there's no overlap with the MMIO + * generation. + */ + if (WARN_ON(mmio_mask & ~SPTE_MMIO_ALLOWED_MASK)) + mmio_value = 0; + + /* * Disable MMIO caching if the MMIO value collides with the bits that * are used to hold the relocated GFN when the L1TF mitigation is * enabled. This should never fire as there is no known hardware that diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index cabe3fbb4f39..f3744eea45f5 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -5,8 +5,6 @@ #include "mmu_internal.h" -extern bool __read_mostly enable_mmio_caching; - /* * A MMU present SPTE is backed by actual memory and may or may not be present * in hardware. E.g. MMIO SPTEs are not considered present. Use bit 11, as it @@ -125,6 +123,20 @@ static_assert(!(EPT_SPTE_MMU_WRITABLE & SHADOW_ACC_TRACK_SAVED_MASK)); static_assert(!(SPTE_MMU_PRESENT_MASK & (MMIO_SPTE_GEN_LOW_MASK | MMIO_SPTE_GEN_HIGH_MASK))); +/* + * The SPTE MMIO mask must NOT overlap the MMIO generation bits or the + * MMU-present bit. The generation obviously co-exists with the magic MMIO + * mask/value, and MMIO SPTEs are considered !MMU-present. + * + * The SPTE MMIO mask is allowed to use hardware "present" bits (i.e. all EPT + * RWX bits), all physical address bits (legal PA bits are used for "fast" MMIO + * and so they're off-limits for generation; additional checks ensure the mask + * doesn't overlap legal PA bits), and bit 63 (carved out for future usage). + */ +#define SPTE_MMIO_ALLOWED_MASK (BIT_ULL(63) | GENMASK_ULL(51, 12) | GENMASK_ULL(2, 0)) +static_assert(!(SPTE_MMIO_ALLOWED_MASK & + (SPTE_MMU_PRESENT_MASK | MMIO_SPTE_GEN_LOW_MASK | MMIO_SPTE_GEN_HIGH_MASK))); + #define MMIO_SPTE_GEN_LOW_BITS (MMIO_SPTE_GEN_LOW_END - MMIO_SPTE_GEN_LOW_START + 1) #define MMIO_SPTE_GEN_HIGH_BITS (MMIO_SPTE_GEN_HIGH_END - MMIO_SPTE_GEN_HIGH_START + 1) @@ -450,6 +462,7 @@ static inline u64 restore_acc_track_spte(u64 spte) u64 kvm_mmu_changed_pte_notifier_make_spte(u64 old_spte, kvm_pfn_t new_pfn); +void __init kvm_mmu_spte_module_init(void); void kvm_mmu_reset_all_pte_masks(void); #endif diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index b0e793e7d85c..28064060413a 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -22,6 +22,7 @@ #include <asm/trapnr.h> #include <asm/fpu/xcr.h> +#include "mmu.h" #include "x86.h" #include "svm.h" #include "svm_ops.h" @@ -2221,6 +2222,15 @@ void __init sev_hardware_setup(void) if (!sev_es_enabled) goto out; + /* + * SEV-ES requires MMIO caching as KVM doesn't have access to the guest + * instruction stream, i.e. can't emulate in response to a #NPF and + * instead relies on #NPF(RSVD) being reflected into the guest as #VC + * (the guest can then do a #VMGEXIT to request MMIO emulation). + */ + if (!enable_mmio_caching) + goto out; + /* Does the CPU support SEV-ES? */ if (!boot_cpu_has(X86_FEATURE_SEV_ES)) goto out; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 38f873cb6f2c..f3813dbacb9f 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5034,13 +5034,16 @@ static __init int svm_hardware_setup(void) /* Setup shadow_me_value and shadow_me_mask */ kvm_mmu_set_me_spte_mask(sme_me_mask, sme_me_mask); - /* Note, SEV setup consumes npt_enabled. */ + svm_adjust_mmio_mask(); + + /* + * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which + * may be modified by svm_adjust_mmio_mask()). + */ sev_hardware_setup(); svm_hv_hardware_setup(); - svm_adjust_mmio_mask(); - for_each_possible_cpu(cpu) { r = svm_cpu_init(cpu); if (r) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 862c1a4d971b..c399637a3a79 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -171,13 +171,6 @@ static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) return get_gp_pmc(pmu, msr, MSR_IA32_PMC0); } -bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu) -{ - struct x86_pmu_lbr *lbr = vcpu_to_lbr_records(vcpu); - - return lbr->nr && (vcpu_get_perf_capabilities(vcpu) & PMU_CAP_LBR_FMT); -} - static bool intel_pmu_is_valid_lbr_msr(struct kvm_vcpu *vcpu, u32 index) { struct x86_pmu_lbr *records = vcpu_to_lbr_records(vcpu); @@ -592,7 +585,9 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters); - if (cpuid_model_is_consistent(vcpu)) + perf_capabilities = vcpu_get_perf_capabilities(vcpu); + if (cpuid_model_is_consistent(vcpu) && + (perf_capabilities & PMU_CAP_LBR_FMT)) x86_perf_get_lbr(&lbr_desc->records); else lbr_desc->records.nr = 0; @@ -600,7 +595,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) if (lbr_desc->records.nr) bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); - perf_capabilities = vcpu_get_perf_capabilities(vcpu); if (perf_capabilities & PERF_CAP_PEBS_FORMAT) { if (perf_capabilities & PERF_CAP_PEBS_BASELINE) { pmu->pebs_enable_mask = counter_mask; diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 4182c7ffc909..6de96b943804 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -227,11 +227,13 @@ SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL) * entries and (in some cases) RSB underflow. * * eIBRS has its own protection against poisoned RSB, so it doesn't - * need the RSB filling sequence. But it does need to be enabled - * before the first unbalanced RET. + * need the RSB filling sequence. But it does need to be enabled, and a + * single call to retire, before the first unbalanced RET. */ - FILL_RETURN_BUFFER %_ASM_CX, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT + FILL_RETURN_BUFFER %_ASM_CX, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT,\ + X86_FEATURE_RSB_VMEXIT_LITE + pop %_ASM_ARG2 /* @flags */ pop %_ASM_ARG1 /* @vmx */ diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index fb8e3480a9d7..24d58c2ffaa3 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -6,6 +6,7 @@ #include <asm/kvm.h> #include <asm/intel_pt.h> +#include <asm/perf_event.h> #include "capabilities.h" #include "../kvm_cache_regs.h" @@ -104,15 +105,6 @@ static inline bool intel_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) return pmu->version > 1; } -#define vcpu_to_lbr_desc(vcpu) (&to_vmx(vcpu)->lbr_desc) -#define vcpu_to_lbr_records(vcpu) (&to_vmx(vcpu)->lbr_desc.records) - -void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu); -bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu); - -int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); -void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); - struct lbr_desc { /* Basic info about guest LBR records. */ struct x86_pmu_lbr records; @@ -542,6 +534,25 @@ static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) return container_of(vcpu, struct vcpu_vmx, vcpu); } +static inline struct lbr_desc *vcpu_to_lbr_desc(struct kvm_vcpu *vcpu) +{ + return &to_vmx(vcpu)->lbr_desc; +} + +static inline struct x86_pmu_lbr *vcpu_to_lbr_records(struct kvm_vcpu *vcpu) +{ + return &vcpu_to_lbr_desc(vcpu)->records; +} + +static inline bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu) +{ + return !!vcpu_to_lbr_records(vcpu)->nr; +} + +void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu); +int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); +void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); + static inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 79a8a74b6b2a..205ebdc2b11b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3413,6 +3413,7 @@ static void record_steal_time(struct kvm_vcpu *vcpu) struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache; struct kvm_steal_time __user *st; struct kvm_memslots *slots; + gpa_t gpa = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; u64 steal; u32 version; @@ -3430,13 +3431,12 @@ static void record_steal_time(struct kvm_vcpu *vcpu) slots = kvm_memslots(vcpu->kvm); if (unlikely(slots->generation != ghc->generation || + gpa != ghc->gpa || kvm_is_error_hva(ghc->hva) || !ghc->memslot)) { - gfn_t gfn = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; - /* We rely on the fact that it fits in a single page. */ BUILD_BUG_ON((sizeof(*st) - 1) & KVM_STEAL_VALID_BITS); - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gfn, sizeof(*st)) || + if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gpa, sizeof(*st)) || kvm_is_error_hva(ghc->hva) || !ghc->memslot) return; } @@ -3545,9 +3545,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; vcpu->arch.perf_capabilities = data; - + kvm_pmu_refresh(vcpu); return 0; - } + } case MSR_EFER: return set_efer(vcpu, msr_info); case MSR_K7_HWCR: @@ -4714,6 +4714,7 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu) struct kvm_steal_time __user *st; struct kvm_memslots *slots; static const u8 preempted = KVM_VCPU_PREEMPTED; + gpa_t gpa = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; /* * The vCPU can be marked preempted if and only if the VM-Exit was on @@ -4741,6 +4742,7 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu) slots = kvm_memslots(vcpu->kvm); if (unlikely(slots->generation != ghc->generation || + gpa != ghc->gpa || kvm_is_error_hva(ghc->hva) || !ghc->memslot)) return; @@ -13019,6 +13021,7 @@ void kvm_fixup_and_inject_pf_error(struct kvm_vcpu *vcpu, gva_t gva, u16 error_c fault.error_code = error_code; fault.nested_page_fault = false; fault.address = gva; + fault.async_page_fault = false; } vcpu->arch.walk_mmu->inject_page_fault(vcpu, &fault); } diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index a0c05ccbf4b1..280cb5dc7341 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -707,23 +707,24 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; case KVM_XEN_VCPU_ATTR_TYPE_TIMER: - if (data->u.timer.port) { - if (data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) { - r = -EINVAL; - break; - } - vcpu->arch.xen.timer_virq = data->u.timer.port; + if (data->u.timer.port && + data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) { + r = -EINVAL; + break; + } + + if (!vcpu->arch.xen.timer.function) kvm_xen_init_timer(vcpu); - /* Restart the timer if it's set */ - if (data->u.timer.expires_ns) - kvm_xen_start_timer(vcpu, data->u.timer.expires_ns, - data->u.timer.expires_ns - - get_kvmclock_ns(vcpu->kvm)); - } else if (kvm_xen_timer_enabled(vcpu)) { - kvm_xen_stop_timer(vcpu); - vcpu->arch.xen.timer_virq = 0; - } + /* Stop the timer (if it's running) before changing the vector */ + kvm_xen_stop_timer(vcpu); + vcpu->arch.xen.timer_virq = data->u.timer.port; + + /* Start the timer if the new value has a valid vector+expiry. */ + if (data->u.timer.port && data->u.timer.expires_ns) + kvm_xen_start_timer(vcpu, data->u.timer.expires_ns, + data->u.timer.expires_ns - + get_kvmclock_ns(vcpu->kvm)); r = 0; break; diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 509408da0da1..6b3033845c6d 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -30,9 +30,15 @@ int pmd_huge(pmd_t pmd) (pmd_val(pmd) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT; } +/* + * pud_huge() returns 1 if @pud is hugetlb related entry, that is normal + * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry. + * Otherwise, returns 0. + */ int pud_huge(pud_t pud) { - return !!(pud_val(pud) & _PAGE_PSE); + return !pud_none(pud) && + (pud_val(pud) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT; } #ifdef CONFIG_HUGETLB_PAGE diff --git a/block/bio.c b/block/bio.c index d6eb90d9b20b..3d3a2678fea2 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1200,7 +1200,7 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) struct page **pages = (struct page **)bv; ssize_t size, left; unsigned len, i = 0; - size_t offset; + size_t offset, trim; int ret = 0; /* @@ -1218,16 +1218,19 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) * result to ensure the bio's total size is correct. The remainder of * the iov data will be picked up in the next bio iteration. */ - size = iov_iter_get_pages(iter, pages, UINT_MAX - bio->bi_iter.bi_size, + size = iov_iter_get_pages2(iter, pages, UINT_MAX - bio->bi_iter.bi_size, nr_pages, &offset); - if (size > 0) { - nr_pages = DIV_ROUND_UP(offset + size, PAGE_SIZE); - size = ALIGN_DOWN(size, bdev_logical_block_size(bio->bi_bdev)); - } else - nr_pages = 0; - - if (unlikely(size <= 0)) { - ret = size ? size : -EFAULT; + if (unlikely(size <= 0)) + return size ? size : -EFAULT; + + nr_pages = DIV_ROUND_UP(offset + size, PAGE_SIZE); + + trim = size & (bdev_logical_block_size(bio->bi_bdev) - 1); + iov_iter_revert(iter, trim); + + size -= trim; + if (unlikely(!size)) { + ret = -EFAULT; goto out; } @@ -1246,7 +1249,7 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) offset = 0; } - iov_iter_advance(iter, size - left); + iov_iter_revert(iter, left); out: while (i < nr_pages) put_page(pages[i++]); diff --git a/block/blk-map.c b/block/blk-map.c index df8b066cd548..7196a6b64c80 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -254,7 +254,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, size_t offs, added = 0; int npages; - bytes = iov_iter_get_pages_alloc(iter, &pages, LONG_MAX, &offs); + bytes = iov_iter_get_pages_alloc2(iter, &pages, LONG_MAX, &offs); if (unlikely(bytes <= 0)) { ret = bytes ? bytes : -EFAULT; goto out_unmap; @@ -284,7 +284,6 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, bytes -= n; offs = 0; } - iov_iter_advance(iter, added); } /* * release the pages we didn't map into the bio, if any @@ -293,8 +292,10 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, put_page(pages[j++]); kvfree(pages); /* couldn't stuff something into bio? */ - if (bytes) + if (bytes) { + iov_iter_revert(iter, bytes); break; + } } ret = blk_rq_append_bio(rq, bio); diff --git a/block/fops.c b/block/fops.c index a564cd81340c..b90742595317 100644 --- a/block/fops.c +++ b/block/fops.c @@ -75,7 +75,7 @@ static ssize_t __blkdev_direct_IO_simple(struct kiocb *iocb, if (iov_iter_rw(iter) == READ) { bio_init(&bio, bdev, vecs, nr_pages, REQ_OP_READ); - if (iter_is_iovec(iter)) + if (user_backed_iter(iter)) should_dirty = true; } else { bio_init(&bio, bdev, vecs, nr_pages, dio_bio_write_op(iocb)); @@ -204,7 +204,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, } dio->size = 0; - if (is_read && iter_is_iovec(iter)) + if (is_read && user_backed_iter(iter)) dio->flags |= DIO_SHOULD_DIRTY; blk_start_plug(&plug); @@ -335,7 +335,7 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb, dio->size = bio->bi_iter.bi_size; if (is_read) { - if (iter_is_iovec(iter)) { + if (user_backed_iter(iter)) { dio->flags |= DIO_SHOULD_DIRTY; bio_set_pages_dirty(bio); } diff --git a/certs/Makefile b/certs/Makefile index 88a73b28d254..9486ed924731 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -4,24 +4,22 @@ # obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o -obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o +obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o blacklist_hashes.o obj-$(CONFIG_SYSTEM_REVOCATION_LIST) += revocation_certificates.o -ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),) $(obj)/blacklist_hashes.o: $(obj)/blacklist_hash_list CFLAGS_blacklist_hashes.o := -I $(obj) quiet_cmd_check_and_copy_blacklist_hash_list = GEN $@ cmd_check_and_copy_blacklist_hash_list = \ - $(AWK) -f $(srctree)/scripts/check-blacklist-hashes.awk $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST) >&2; \ - cat $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST) > $@ + $(if $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST), \ + $(AWK) -f $(srctree)/$(src)/check-blacklist-hashes.awk $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST) >&2; \ + { cat $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST); echo $(comma) NULL; } > $@, \ + echo NULL > $@) $(obj)/blacklist_hash_list: $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST) FORCE $(call if_changed,check_and_copy_blacklist_hash_list) -obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o -else -obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o -endif + targets += blacklist_hash_list quiet_cmd_extract_certs = CERT $@ diff --git a/certs/blacklist_hashes.c b/certs/blacklist_hashes.c index 86d66fe11348..0c5476abebd9 100644 --- a/certs/blacklist_hashes.c +++ b/certs/blacklist_hashes.c @@ -3,5 +3,4 @@ const char __initconst *const blacklist_hashes[] = { #include "blacklist_hash_list" - , NULL }; diff --git a/certs/blacklist_nohashes.c b/certs/blacklist_nohashes.c deleted file mode 100644 index 753b703ef0ef..000000000000 --- a/certs/blacklist_nohashes.c +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "blacklist.h" - -const char __initconst *const blacklist_hashes[] = { - NULL -}; diff --git a/scripts/check-blacklist-hashes.awk b/certs/check-blacklist-hashes.awk index 107c1d3204d4..107c1d3204d4 100755 --- a/scripts/check-blacklist-hashes.awk +++ b/certs/check-blacklist-hashes.awk diff --git a/crypto/Makefile b/crypto/Makefile index 167c004dbf4f..a6f94e04e1da 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_CRYPTO_STREEBOG) += streebog_generic.o obj-$(CONFIG_CRYPTO_WP512) += wp512.o CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149 obj-$(CONFIG_CRYPTO_BLAKE2B) += blake2b_generic.o +CFLAGS_blake2b_generic.o := -Wframe-larger-than=4096 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105930 obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o obj-$(CONFIG_CRYPTO_ECB) += ecb.o obj-$(CONFIG_CRYPTO_CBC) += cbc.o diff --git a/crypto/af_alg.c b/crypto/af_alg.c index c8289b7a85ba..e893c0f6c879 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -404,7 +404,7 @@ int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len) ssize_t n; int npages, i; - n = iov_iter_get_pages(iter, sgl->pages, len, ALG_MAX_PAGES, &off); + n = iov_iter_get_pages2(iter, sgl->pages, len, ALG_MAX_PAGES, &off); if (n < 0) return n; @@ -1191,7 +1191,6 @@ int af_alg_get_rsgl(struct sock *sk, struct msghdr *msg, int flags, len += err; atomic_add(err, &ctx->rcvused); rsgl->sg_num_bytes = err; - iov_iter_advance(&msg->msg_iter, err); } *outlen = len; diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 50f7b22f1b48..1d017ec5c63c 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -102,11 +102,12 @@ static int hash_sendmsg(struct socket *sock, struct msghdr *msg, err = crypto_wait_req(crypto_ahash_update(&ctx->req), &ctx->wait); af_alg_free_sg(&ctx->sgl); - if (err) + if (err) { + iov_iter_revert(&msg->msg_iter, len); goto unlock; + } copied += len; - iov_iter_advance(&msg->msg_iter, len); } err = 0; diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index e764f9ac9cf8..7b3ad8ed2f4e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -55,14 +55,19 @@ static const guid_t ads_guid = GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6, 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b); +static const guid_t buffer_prop_guid = + GUID_INIT(0xedb12dd0, 0x363d, 0x4085, + 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4); + static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent); -static bool acpi_extract_properties(const union acpi_object *desc, +static bool acpi_extract_properties(acpi_handle handle, + union acpi_object *desc, struct acpi_device_data *data); -static bool acpi_nondev_subnode_extract(const union acpi_object *desc, +static bool acpi_nondev_subnode_extract(union acpi_object *desc, acpi_handle handle, const union acpi_object *link, struct list_head *list, @@ -81,7 +86,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc, INIT_LIST_HEAD(&dn->data.properties); INIT_LIST_HEAD(&dn->data.subnodes); - result = acpi_extract_properties(desc, &dn->data); + result = acpi_extract_properties(handle, desc, &dn->data); if (handle) { acpi_handle scope; @@ -155,16 +160,16 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, return acpi_nondev_subnode_data_ok(handle, link, list, parent); } -static int acpi_add_nondev_subnodes(acpi_handle scope, - const union acpi_object *links, - struct list_head *list, - struct fwnode_handle *parent) +static bool acpi_add_nondev_subnodes(acpi_handle scope, + union acpi_object *links, + struct list_head *list, + struct fwnode_handle *parent) { bool ret = false; int i; for (i = 0; i < links->package.count; i++) { - const union acpi_object *link, *desc; + union acpi_object *link, *desc; acpi_handle handle; bool result; @@ -204,7 +209,7 @@ static int acpi_add_nondev_subnodes(acpi_handle scope, } static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent) { @@ -212,7 +217,8 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, /* Look for the ACPI data subnodes GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *links; + const union acpi_object *guid; + union acpi_object *links; guid = &desc->package.elements[i]; links = &desc->package.elements[i + 1]; @@ -325,7 +331,7 @@ static bool acpi_is_property_guid(const guid_t *guid) struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, - const union acpi_object *properties) + union acpi_object *properties) { struct acpi_device_properties *props; @@ -340,7 +346,141 @@ acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, return props; } -static bool acpi_extract_properties(const union acpi_object *desc, +static void acpi_nondev_subnode_tag(acpi_handle handle, void *context) +{ +} + +static void acpi_untie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_detach_data(dn->handle, acpi_nondev_subnode_tag); + + acpi_untie_nondev_subnodes(&dn->data); + } +} + +static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_status status; + bool ret; + + status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn); + if (ACPI_FAILURE(status)) { + acpi_handle_err(dn->handle, "Can't tag data node\n"); + return false; + } + + ret = acpi_tie_nondev_subnodes(&dn->data); + if (!ret) + return ret; + } + + return true; +} + +static void acpi_data_add_buffer_props(acpi_handle handle, + struct acpi_device_data *data, + union acpi_object *properties) +{ + struct acpi_device_properties *props; + union acpi_object *package; + size_t alloc_size; + unsigned int i; + u32 *count; + + if (check_mul_overflow((size_t)properties->package.count, + sizeof(*package) + sizeof(void *), + &alloc_size) || + check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size, + &alloc_size)) { + acpi_handle_warn(handle, + "can't allocate memory for %u buffer props", + properties->package.count); + return; + } + + props = kvzalloc(alloc_size, GFP_KERNEL); + if (!props) + return; + + props->guid = &buffer_prop_guid; + props->bufs = (void *)(props + 1); + props->properties = (void *)(props->bufs + properties->package.count); + + /* Outer package */ + package = props->properties; + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = package + 1; + count = &package->package.count; + *count = 0; + + /* Inner packages */ + package++; + + for (i = 0; i < properties->package.count; i++) { + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + union acpi_object *property = &properties->package.elements[i]; + union acpi_object *prop, *obj, *buf_obj; + acpi_status status; + + if (property->type != ACPI_TYPE_PACKAGE || + property->package.count != 2) { + acpi_handle_warn(handle, + "buffer property %u has %u entries\n", + i, property->package.count); + continue; + } + + prop = &property->package.elements[0]; + obj = &property->package.elements[1]; + + if (prop->type != ACPI_TYPE_STRING || + obj->type != ACPI_TYPE_STRING) { + acpi_handle_warn(handle, + "wrong object types %u and %u\n", + prop->type, obj->type); + continue; + } + + status = acpi_evaluate_object_typed(handle, obj->string.pointer, + NULL, &buf, + ACPI_TYPE_BUFFER); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "can't evaluate \"%*pE\" as buffer\n", + obj->string.length, + obj->string.pointer); + continue; + } + + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = prop; + package->package.count = 2; + + buf_obj = buf.pointer; + + /* Replace the string object with a buffer object */ + obj->type = ACPI_TYPE_BUFFER; + obj->buffer.length = buf_obj->buffer.length; + obj->buffer.pointer = buf_obj->buffer.pointer; + + props->bufs[i] = buf.pointer; + package++; + (*count)++; + } + + if (*count) + list_add(&props->list, &data->properties); + else + kvfree(props); +} + +static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc, struct acpi_device_data *data) { int i; @@ -350,7 +490,8 @@ static bool acpi_extract_properties(const union acpi_object *desc, /* Look for the device properties GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *properties; + const union acpi_object *guid; + union acpi_object *properties; guid = &desc->package.elements[i]; properties = &desc->package.elements[i + 1]; @@ -364,6 +505,12 @@ static bool acpi_extract_properties(const union acpi_object *desc, properties->type != ACPI_TYPE_PACKAGE) break; + if (guid_equal((guid_t *)guid->buffer.pointer, + &buffer_prop_guid)) { + acpi_data_add_buffer_props(scope, data, properties); + continue; + } + if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer)) continue; @@ -410,7 +557,7 @@ void acpi_init_properties(struct acpi_device *adev) if (ACPI_FAILURE(status)) goto out; - if (acpi_extract_properties(buf.pointer, &adev->data)) { + if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) { adev->data.pointer = buf.pointer; if (acpi_of) acpi_init_of_compatible(adev); @@ -422,6 +569,9 @@ void acpi_init_properties(struct acpi_device *adev) if (!adev->data.pointer) { acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); ACPI_FREE(buf.pointer); + } else { + if (!acpi_tie_nondev_subnodes(&adev->data)) + acpi_untie_nondev_subnodes(&adev->data); } out: @@ -438,8 +588,14 @@ static void acpi_free_device_properties(struct list_head *list) struct acpi_device_properties *props, *tmp; list_for_each_entry_safe(props, tmp, list, list) { + u32 i; + list_del(&props->list); - kfree(props); + /* Buffer data properties were separately allocated */ + if (props->bufs) + for (i = 0; i < props->properties->package.count; i++) + ACPI_FREE(props->bufs[i]); + kvfree(props); } } @@ -462,6 +618,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) void acpi_free_properties(struct acpi_device *adev) { + acpi_untie_nondev_subnodes(&adev->data); acpi_destroy_nondev_subnodes(&adev->data.subnodes); ACPI_FREE((void *)adev->data.pointer); adev->data.of_compatible = NULL; @@ -633,6 +790,58 @@ acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode, return NULL; } +static int acpi_get_ref_args(struct fwnode_reference_args *args, + struct fwnode_handle *ref_fwnode, + const union acpi_object **element, + const union acpi_object *end, size_t num_args) +{ + u32 nargs = 0, i; + + /* + * Find the referred data extension node under the + * referred device node. + */ + for (; *element < end && (*element)->type == ACPI_TYPE_STRING; + (*element)++) { + const char *child_name = (*element)->string.pointer; + + ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name); + if (!ref_fwnode) + return -EINVAL; + } + + /* + * Assume the following integer elements are all args. Stop counting on + * the first reference or end of the package arguments. In case of + * neither reference, nor integer, return an error, we can't parse it. + */ + for (i = 0; (*element) + i < end && i < num_args; i++) { + acpi_object_type type = (*element)[i].type; + + if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; + + if (type == ACPI_TYPE_INTEGER) + nargs++; + else + return -EINVAL; + } + + if (nargs > NR_FWNODE_REFERENCE_ARGS) + return -EINVAL; + + if (args) { + args->fwnode = ref_fwnode; + args->nargs = nargs; + for (i = 0; i < nargs; i++) + args->args[i] = (*element)[i].integer.value; + } + + (*element) += nargs; + + return 0; +} + /** * __acpi_node_get_property_reference - returns handle to the referenced object * @fwnode: Firmware node to get the property from @@ -686,11 +895,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (ret) return ret == -EINVAL ? -ENOENT : -EINVAL; - /* - * The simplest case is when the value is a single reference. Just - * return that reference then. - */ - if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { + switch (obj->type) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* Plain single reference without arguments. */ if (index) return -ENOENT; @@ -701,19 +908,21 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, args->fwnode = acpi_fwnode_handle(device); args->nargs = 0; return 0; + case ACPI_TYPE_PACKAGE: + /* + * If it is not a single reference, then it is a package of + * references followed by number of ints as follows: + * + * Package () { REF, INT, REF, INT, INT } + * + * The index argument is then used to determine which reference + * the caller wants (along with the arguments). + */ + break; + default: + return -EINVAL; } - /* - * If it is not a single reference, then it is a package of - * references followed by number of ints as follows: - * - * Package () { REF, INT, REF, INT, INT } - * - * The index argument is then used to determine which reference - * the caller wants (along with the arguments). - */ - if (obj->type != ACPI_TYPE_PACKAGE) - return -EINVAL; if (index >= obj->package.count) return -ENOENT; @@ -721,66 +930,30 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, end = element + obj->package.count; while (element < end) { - u32 nargs, i; - - if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { - struct fwnode_handle *ref_fwnode; - + switch (element->type) { + case ACPI_TYPE_LOCAL_REFERENCE: device = acpi_fetch_acpi_dev(element->reference.handle); if (!device) return -EINVAL; - nargs = 0; element++; - /* - * Find the referred data extension node under the - * referred device node. - */ - for (ref_fwnode = acpi_fwnode_handle(device); - element < end && element->type == ACPI_TYPE_STRING; - element++) { - ref_fwnode = acpi_fwnode_get_named_child_node( - ref_fwnode, element->string.pointer); - if (!ref_fwnode) - return -EINVAL; - } - - /* - * Assume the following integer elements are all args. - * Stop counting on the first reference or end of the - * package arguments. In case of neither reference, - * nor integer, return an error, we can't parse it. - */ - for (i = 0; element + i < end && i < num_args; i++) { - int type = element[i].type; - - if (type == ACPI_TYPE_LOCAL_REFERENCE) - break; - if (type == ACPI_TYPE_INTEGER) - nargs++; - else - return -EINVAL; - } - - if (nargs > NR_FWNODE_REFERENCE_ARGS) - return -EINVAL; - - if (idx == index) { - args->fwnode = ref_fwnode; - args->nargs = nargs; - for (i = 0; i < nargs; i++) - args->args[i] = element[i].integer.value; + ret = acpi_get_ref_args(idx == index ? args : NULL, + acpi_fwnode_handle(device), + &element, end, num_args); + if (ret < 0) + return ret; + if (idx == index) return 0; - } - element += nargs; - } else if (element->type == ACPI_TYPE_INTEGER) { + break; + case ACPI_TYPE_INTEGER: if (idx == index) return -ENOENT; element++; - } else { + break; + default: return -EINVAL; } @@ -852,67 +1025,37 @@ static int acpi_data_prop_read_single(const struct acpi_device_data *data, return ret; } -static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, - size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U8_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u16(const union acpi_object *items, - u16 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U16_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u32(const union acpi_object *items, - u32 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U32_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u64(const union acpi_object *items, - u64 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - - val[i] = items[i].integer.value; - } - return 0; -} +#define acpi_copy_property_array_uint(items, val, nval) \ + ({ \ + typeof(items) __items = items; \ + typeof(val) __val = val; \ + typeof(nval) __nval = nval; \ + size_t i; \ + int ret = 0; \ + \ + for (i = 0; i < __nval; i++) { \ + if (__items->type == ACPI_TYPE_BUFFER) { \ + __val[i] = __items->buffer.pointer[i]; \ + continue; \ + } \ + if (__items[i].type != ACPI_TYPE_INTEGER) { \ + ret = -EPROTO; \ + break; \ + } \ + if (__items[i].integer.value > _Generic(__val, \ + u8: U8_MAX, \ + u16: U16_MAX, \ + u32: U32_MAX, \ + u64: U64_MAX, \ + default: 0U)) { \ + ret = -EOVERFLOW; \ + break; \ + } \ + \ + __val[i] = __items[i].integer.value; \ + } \ + ret; \ + }) static int acpi_copy_property_array_string(const union acpi_object *items, char **val, size_t nval) @@ -954,31 +1097,54 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, } ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj); + if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) + ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER, + &obj); if (ret) return ret; - if (!val) + if (!val) { + if (obj->type == ACPI_TYPE_BUFFER) + return obj->buffer.length; + return obj->package.count; + } - if (proptype != DEV_PROP_STRING && nval > obj->package.count) - return -EOVERFLOW; + switch (proptype) { + case DEV_PROP_STRING: + break; + case DEV_PROP_U8 ... DEV_PROP_U64: + if (obj->type == ACPI_TYPE_BUFFER) { + if (nval > obj->buffer.length) + return -EOVERFLOW; + break; + } + fallthrough; + default: + if (nval > obj->package.count) + return -EOVERFLOW; + break; + } if (nval == 0) return -EINVAL; - items = obj->package.elements; + if (obj->type != ACPI_TYPE_BUFFER) + items = obj->package.elements; + else + items = obj; switch (proptype) { case DEV_PROP_U8: - ret = acpi_copy_property_array_u8(items, (u8 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u8 *)val, nval); break; case DEV_PROP_U16: - ret = acpi_copy_property_array_u16(items, (u16 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u16 *)val, nval); break; case DEV_PROP_U32: - ret = acpi_copy_property_array_u32(items, (u32 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u32 *)val, nval); break; case DEV_PROP_U64: - ret = acpi_copy_property_array_u64(items, (u64 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u64 *)val, nval); break; case DEV_PROP_STRING: ret = acpi_copy_property_array_string( diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c index 647f11cf165d..6132092dab2a 100644 --- a/drivers/acpi/viot.c +++ b/drivers/acpi/viot.c @@ -88,7 +88,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, return -ENODEV; } - fwnode = pdev->dev.fwnode; + fwnode = dev_fwnode(&pdev->dev); if (!fwnode) { /* * PCI devices aren't necessarily described by ACPI. Create a @@ -101,7 +101,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, } set_primary_fwnode(&pdev->dev, fwnode); } - viommu->fwnode = pdev->dev.fwnode; + viommu->fwnode = dev_fwnode(&pdev->dev); pci_dev_put(pdev); return 0; } @@ -314,7 +314,7 @@ static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu, return -ENODEV; /* We're not translating ourself */ - if (viommu->fwnode == dev->fwnode) + if (device_match_fwnode(dev, viommu->fwnode)) return -EINVAL; ops = iommu_ops_from_fwnode(viommu->fwnode); diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 81ce81a75fc6..681cb3786794 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -3752,6 +3752,7 @@ static void __exit idt77252_exit(void) card = idt77252_chain; dev = card->atmdev; idt77252_chain = card->next; + del_timer_sync(&card->tst_timer); if (dev->phy->stop) dev->phy->stop(dev); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0d8ec2fe5740..f9e39301c4af 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1297,7 +1297,7 @@ static void rbd_osd_submit(struct ceph_osd_request *osd_req) dout("%s osd_req %p for obj_req %p objno %llu %llu~%llu\n", __func__, osd_req, obj_req, obj_req->ex.oe_objno, obj_req->ex.oe_off, obj_req->ex.oe_len); - ceph_osdc_start_request(osd_req->r_osdc, osd_req, false); + ceph_osdc_start_request(osd_req->r_osdc, osd_req); } /* @@ -2081,7 +2081,7 @@ static int rbd_object_map_update(struct rbd_obj_request *obj_req, u64 snap_id, if (ret) return ret; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); return 0; } @@ -4768,7 +4768,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, if (ret) goto out_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) ceph_copy_from_page_vector(pages, buf, 0, ret); diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 8fcaba541539..d69d13a26414 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -29,9 +29,9 @@ struct private_data { cpumask_var_t cpus; struct device *cpu_dev; - struct opp_table *opp_table; struct cpufreq_frequency_table *freq_table; bool have_static_opps; + int opp_token; }; static LIST_HEAD(priv_list); @@ -193,7 +193,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) struct private_data *priv; struct device *cpu_dev; bool fallback = false; - const char *reg_name; + const char *reg_name[] = { NULL, NULL }; int ret; /* Check if this CPU is already covered by some other policy */ @@ -218,12 +218,11 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) * OPP layer will be taking care of regulators now, but it needs to know * the name of the regulator first. */ - reg_name = find_supply_name(cpu_dev); - if (reg_name) { - priv->opp_table = dev_pm_opp_set_regulators(cpu_dev, ®_name, - 1); - if (IS_ERR(priv->opp_table)) { - ret = PTR_ERR(priv->opp_table); + reg_name[0] = find_supply_name(cpu_dev); + if (reg_name[0]) { + priv->opp_token = dev_pm_opp_set_regulators(cpu_dev, reg_name); + if (priv->opp_token < 0) { + ret = priv->opp_token; if (ret != -EPROBE_DEFER) dev_err(cpu_dev, "failed to set regulators: %d\n", ret); @@ -295,7 +294,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) out: if (priv->have_static_opps) dev_pm_opp_of_cpumask_remove_table(priv->cpus); - dev_pm_opp_put_regulators(priv->opp_table); + dev_pm_opp_put_regulators(priv->opp_token); free_cpumask: free_cpumask_var(priv->cpus); return ret; @@ -309,7 +308,7 @@ static void dt_cpufreq_release(void) dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table); if (priv->have_static_opps) dev_pm_opp_of_cpumask_remove_table(priv->cpus); - dev_pm_opp_put_regulators(priv->opp_table); + dev_pm_opp_put_regulators(priv->opp_token); free_cpumask_var(priv->cpus); list_del(&priv->node); } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 954eef26685f..7820c4e74289 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1348,15 +1348,15 @@ static int cpufreq_online(unsigned int cpu) } if (!new_policy && cpufreq_driver->online) { + /* Recover policy->cpus using related_cpus */ + cpumask_copy(policy->cpus, policy->related_cpus); + ret = cpufreq_driver->online(policy); if (ret) { pr_debug("%s: %d: initialization failed\n", __func__, __LINE__); goto out_exit_policy; } - - /* Recover policy->cpus using related_cpus */ - cpumask_copy(policy->cpus, policy->related_cpus); } else { cpumask_copy(policy->cpus, cpumask_of(cpu)); diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 3fe9125156b4..76e553af2071 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -31,8 +31,8 @@ /* cpufreq-dt device registered by imx-cpufreq-dt */ static struct platform_device *cpufreq_dt_pdev; -static struct opp_table *cpufreq_opp_table; static struct device *cpu_dev; +static int cpufreq_opp_token; enum IMX7ULP_CPUFREQ_CLKS { ARM, @@ -153,9 +153,9 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) dev_info(&pdev->dev, "cpu speed grade %d mkt segment %d supported-hw %#x %#x\n", speed_grade, mkt_segment, supported_hw[0], supported_hw[1]); - cpufreq_opp_table = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2); - if (IS_ERR(cpufreq_opp_table)) { - ret = PTR_ERR(cpufreq_opp_table); + cpufreq_opp_token = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2); + if (cpufreq_opp_token < 0) { + ret = cpufreq_opp_token; dev_err(&pdev->dev, "Failed to set supported opp: %d\n", ret); return ret; } @@ -163,7 +163,7 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) cpufreq_dt_pdev = platform_device_register_data( &pdev->dev, "cpufreq-dt", -1, NULL, 0); if (IS_ERR(cpufreq_dt_pdev)) { - dev_pm_opp_put_supported_hw(cpufreq_opp_table); + dev_pm_opp_put_supported_hw(cpufreq_opp_token); ret = PTR_ERR(cpufreq_dt_pdev); dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret); return ret; @@ -176,7 +176,7 @@ static int imx_cpufreq_dt_remove(struct platform_device *pdev) { platform_device_unregister(cpufreq_dt_pdev); if (!of_machine_is_compatible("fsl,imx7ulp")) - dev_pm_opp_put_supported_hw(cpufreq_opp_table); + dev_pm_opp_put_supported_hw(cpufreq_opp_token); else clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks); diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 76f6b3884e6b..7f2680bc9a0f 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -478,6 +478,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) if (info->soc_data->ccifreq_supported) { info->vproc_on_boot = regulator_get_voltage(info->proc_reg); if (info->vproc_on_boot < 0) { + ret = info->vproc_on_boot; dev_err(info->cpu_dev, "invalid Vproc value: %d\n", info->vproc_on_boot); goto out_disable_inter_clock; diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 36c79580fba2..d5ef3c66c762 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -15,6 +15,7 @@ #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/units.h> #define LUT_MAX_ENTRIES 40U #define LUT_SRC GENMASK(31, 30) @@ -26,8 +27,6 @@ #define GT_IRQ_STATUS BIT(2) -#define HZ_PER_KHZ 1000 - struct qcom_cpufreq_soc_data { u32 reg_enable; u32 reg_domain_state; @@ -428,7 +427,7 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) return 0; } - ret = irq_set_affinity_hint(data->throttle_irq, policy->cpus); + ret = irq_set_affinity_and_hint(data->throttle_irq, policy->cpus); if (ret) dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n", data->irq_name, data->throttle_irq); @@ -445,7 +444,11 @@ static int qcom_cpufreq_hw_cpu_online(struct cpufreq_policy *policy) if (data->throttle_irq <= 0) return 0; - ret = irq_set_affinity_hint(data->throttle_irq, policy->cpus); + mutex_lock(&data->throttle_lock); + data->cancel_throttle = false; + mutex_unlock(&data->throttle_lock); + + ret = irq_set_affinity_and_hint(data->throttle_irq, policy->cpus); if (ret) dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n", data->irq_name, data->throttle_irq); @@ -465,7 +468,8 @@ static int qcom_cpufreq_hw_cpu_offline(struct cpufreq_policy *policy) mutex_unlock(&data->throttle_lock); cancel_delayed_work_sync(&data->throttle_work); - irq_set_affinity_hint(data->throttle_irq, NULL); + irq_set_affinity_and_hint(data->throttle_irq, NULL); + disable_irq_nosync(data->throttle_irq); return 0; } diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index 6dfa86971a75..863548f59c3e 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -55,9 +55,7 @@ struct qcom_cpufreq_match_data { }; struct qcom_cpufreq_drv { - struct opp_table **names_opp_tables; - struct opp_table **hw_opp_tables; - struct opp_table **genpd_opp_tables; + int *opp_tokens; u32 versions; const struct qcom_cpufreq_match_data *data; }; @@ -315,72 +313,43 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) } of_node_put(np); - drv->names_opp_tables = kcalloc(num_possible_cpus(), - sizeof(*drv->names_opp_tables), + drv->opp_tokens = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tokens), GFP_KERNEL); - if (!drv->names_opp_tables) { + if (!drv->opp_tokens) { ret = -ENOMEM; goto free_drv; } - drv->hw_opp_tables = kcalloc(num_possible_cpus(), - sizeof(*drv->hw_opp_tables), - GFP_KERNEL); - if (!drv->hw_opp_tables) { - ret = -ENOMEM; - goto free_opp_names; - } - - drv->genpd_opp_tables = kcalloc(num_possible_cpus(), - sizeof(*drv->genpd_opp_tables), - GFP_KERNEL); - if (!drv->genpd_opp_tables) { - ret = -ENOMEM; - goto free_opp; - } for_each_possible_cpu(cpu) { + struct dev_pm_opp_config config = { + .supported_hw = NULL, + }; + cpu_dev = get_cpu_device(cpu); if (NULL == cpu_dev) { ret = -ENODEV; - goto free_genpd_opp; + goto free_opp; } if (drv->data->get_version) { + config.supported_hw = &drv->versions; + config.supported_hw_count = 1; - if (pvs_name) { - drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name( - cpu_dev, - pvs_name); - if (IS_ERR(drv->names_opp_tables[cpu])) { - ret = PTR_ERR(drv->names_opp_tables[cpu]); - dev_err(cpu_dev, "Failed to add OPP name %s\n", - pvs_name); - goto free_opp; - } - } - - drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw( - cpu_dev, &drv->versions, 1); - if (IS_ERR(drv->hw_opp_tables[cpu])) { - ret = PTR_ERR(drv->hw_opp_tables[cpu]); - dev_err(cpu_dev, - "Failed to set supported hardware\n"); - goto free_genpd_opp; - } + if (pvs_name) + config.prop_name = pvs_name; } if (drv->data->genpd_names) { - drv->genpd_opp_tables[cpu] = - dev_pm_opp_attach_genpd(cpu_dev, - drv->data->genpd_names, - NULL); - if (IS_ERR(drv->genpd_opp_tables[cpu])) { - ret = PTR_ERR(drv->genpd_opp_tables[cpu]); - if (ret != -EPROBE_DEFER) - dev_err(cpu_dev, - "Could not attach to pm_domain: %d\n", - ret); - goto free_genpd_opp; + config.genpd_names = drv->data->genpd_names; + config.virt_devs = NULL; + } + + if (config.supported_hw || config.genpd_names) { + drv->opp_tokens[cpu] = dev_pm_opp_set_config(cpu_dev, &config); + if (drv->opp_tokens[cpu] < 0) { + ret = drv->opp_tokens[cpu]; + dev_err(cpu_dev, "Failed to set OPP config\n"); + goto free_opp; } } } @@ -395,27 +364,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) ret = PTR_ERR(cpufreq_dt_pdev); dev_err(cpu_dev, "Failed to register platform device\n"); -free_genpd_opp: - for_each_possible_cpu(cpu) { - if (IS_ERR(drv->genpd_opp_tables[cpu])) - break; - dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); - } - kfree(drv->genpd_opp_tables); free_opp: - for_each_possible_cpu(cpu) { - if (IS_ERR(drv->names_opp_tables[cpu])) - break; - dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]); - } - for_each_possible_cpu(cpu) { - if (IS_ERR(drv->hw_opp_tables[cpu])) - break; - dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); - } - kfree(drv->hw_opp_tables); -free_opp_names: - kfree(drv->names_opp_tables); + for_each_possible_cpu(cpu) + dev_pm_opp_clear_config(drv->opp_tokens[cpu]); + kfree(drv->opp_tokens); free_drv: kfree(drv); @@ -429,15 +381,10 @@ static int qcom_cpufreq_remove(struct platform_device *pdev) platform_device_unregister(cpufreq_dt_pdev); - for_each_possible_cpu(cpu) { - dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]); - dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); - dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); - } + for_each_possible_cpu(cpu) + dev_pm_opp_clear_config(drv->opp_tokens[cpu]); - kfree(drv->names_opp_tables); - kfree(drv->hw_opp_tables); - kfree(drv->genpd_opp_tables); + kfree(drv->opp_tokens); kfree(drv); return 0; diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index fdb0a722d881..a67df90848c2 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -156,9 +156,13 @@ static int sti_cpufreq_set_opp_info(void) unsigned int hw_info_offset; unsigned int version[VERSION_ELEMENTS]; int pcode, substrate, major, minor; - int ret; + int opp_token, ret; char name[MAX_PCODE_NAME_LEN]; - struct opp_table *opp_table; + struct dev_pm_opp_config config = { + .supported_hw = version, + .supported_hw_count = ARRAY_SIZE(version), + .prop_name = name, + }; reg_fields = sti_cpufreq_match(); if (!reg_fields) { @@ -210,21 +214,14 @@ use_defaults: snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode); - opp_table = dev_pm_opp_set_prop_name(dev, name); - if (IS_ERR(opp_table)) { - dev_err(dev, "Failed to set prop name\n"); - return PTR_ERR(opp_table); - } - version[0] = BIT(major); version[1] = BIT(minor); version[2] = BIT(substrate); - opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS); - if (IS_ERR(opp_table)) { - dev_err(dev, "Failed to set supported hardware\n"); - ret = PTR_ERR(opp_table); - goto err_put_prop_name; + opp_token = dev_pm_opp_set_config(dev, &config); + if (opp_token < 0) { + dev_err(dev, "Failed to set OPP config\n"); + return opp_token; } dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n", @@ -233,10 +230,6 @@ use_defaults: version[0], version[1], version[2]); return 0; - -err_put_prop_name: - dev_pm_opp_put_prop_name(opp_table); - return ret; } static int sti_cpufreq_fetch_syscon_registers(void) diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c index 75e1bf3a08f7..a4922580ce06 100644 --- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c +++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c @@ -86,20 +86,20 @@ static int sun50i_cpufreq_get_efuse(u32 *versions) static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) { - struct opp_table **opp_tables; + int *opp_tokens; char name[MAX_NAME_LEN]; unsigned int cpu; u32 speed = 0; int ret; - opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), + opp_tokens = kcalloc(num_possible_cpus(), sizeof(*opp_tokens), GFP_KERNEL); - if (!opp_tables) + if (!opp_tokens) return -ENOMEM; ret = sun50i_cpufreq_get_efuse(&speed); if (ret) { - kfree(opp_tables); + kfree(opp_tokens); return ret; } @@ -113,9 +113,9 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) goto free_opp; } - opp_tables[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name); - if (IS_ERR(opp_tables[cpu])) { - ret = PTR_ERR(opp_tables[cpu]); + opp_tokens[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name); + if (opp_tokens[cpu] < 0) { + ret = opp_tokens[cpu]; pr_err("Failed to set prop name\n"); goto free_opp; } @@ -124,7 +124,7 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); if (!IS_ERR(cpufreq_dt_pdev)) { - platform_set_drvdata(pdev, opp_tables); + platform_set_drvdata(pdev, opp_tokens); return 0; } @@ -132,27 +132,24 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) pr_err("Failed to register platform device\n"); free_opp: - for_each_possible_cpu(cpu) { - if (IS_ERR_OR_NULL(opp_tables[cpu])) - break; - dev_pm_opp_put_prop_name(opp_tables[cpu]); - } - kfree(opp_tables); + for_each_possible_cpu(cpu) + dev_pm_opp_put_prop_name(opp_tokens[cpu]); + kfree(opp_tokens); return ret; } static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev) { - struct opp_table **opp_tables = platform_get_drvdata(pdev); + int *opp_tokens = platform_get_drvdata(pdev); unsigned int cpu; platform_device_unregister(cpufreq_dt_pdev); for_each_possible_cpu(cpu) - dev_pm_opp_put_prop_name(opp_tables[cpu]); + dev_pm_opp_put_prop_name(opp_tokens[cpu]); - kfree(opp_tables); + kfree(opp_tokens); return 0; } diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c index 2a6a98764a8c..1216046cf4c2 100644 --- a/drivers/cpufreq/tegra194-cpufreq.c +++ b/drivers/cpufreq/tegra194-cpufreq.c @@ -162,7 +162,7 @@ static struct tegra_cpufreq_ops tegra234_cpufreq_ops = { .set_cpu_ndiv = tegra234_set_cpu_ndiv, }; -const struct tegra_cpufreq_soc tegra234_cpufreq_soc = { +static const struct tegra_cpufreq_soc tegra234_cpufreq_soc = { .ops = &tegra234_cpufreq_ops, .actmon_cntr_base = 0x9000, .maxcpus_per_cluster = 4, @@ -430,7 +430,7 @@ static struct tegra_cpufreq_ops tegra194_cpufreq_ops = { .set_cpu_ndiv = tegra194_set_cpu_ndiv, }; -const struct tegra_cpufreq_soc tegra194_cpufreq_soc = { +static const struct tegra_cpufreq_soc tegra194_cpufreq_soc = { .ops = &tegra194_cpufreq_ops, .maxcpus_per_cluster = 2, }; diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c index e8db3d75be25..ab7ac7df9e62 100644 --- a/drivers/cpufreq/tegra20-cpufreq.c +++ b/drivers/cpufreq/tegra20-cpufreq.c @@ -32,9 +32,9 @@ static bool cpu0_node_has_opp_v2_prop(void) return ret; } -static void tegra20_cpufreq_put_supported_hw(void *opp_table) +static void tegra20_cpufreq_put_supported_hw(void *opp_token) { - dev_pm_opp_put_supported_hw(opp_table); + dev_pm_opp_put_supported_hw((unsigned long) opp_token); } static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt) @@ -45,7 +45,6 @@ static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt) static int tegra20_cpufreq_probe(struct platform_device *pdev) { struct platform_device *cpufreq_dt; - struct opp_table *opp_table; struct device *cpu_dev; u32 versions[2]; int err; @@ -71,16 +70,15 @@ static int tegra20_cpufreq_probe(struct platform_device *pdev) if (WARN_ON(!cpu_dev)) return -ENODEV; - opp_table = dev_pm_opp_set_supported_hw(cpu_dev, versions, 2); - err = PTR_ERR_OR_ZERO(opp_table); - if (err) { + err = dev_pm_opp_set_supported_hw(cpu_dev, versions, 2); + if (err < 0) { dev_err(&pdev->dev, "failed to set supported hw: %d\n", err); return err; } err = devm_add_action_or_reset(&pdev->dev, tegra20_cpufreq_put_supported_hw, - opp_table); + (void *)((unsigned long) err)); if (err) return err; diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 8f9fdd864391..df85a77d476b 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -60,7 +60,6 @@ struct ti_cpufreq_data { struct device_node *opp_node; struct regmap *syscon; const struct ti_cpufreq_soc_data *soc_data; - struct opp_table *opp_table; }; static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data, @@ -173,7 +172,7 @@ static struct ti_cpufreq_soc_data omap34xx_soc_data = { * seems to always read as 0). */ -static const char * const omap3_reg_names[] = {"cpu0", "vbb"}; +static const char * const omap3_reg_names[] = {"cpu0", "vbb", NULL}; static struct ti_cpufreq_soc_data omap36xx_soc_data = { .reg_names = omap3_reg_names, @@ -324,10 +323,13 @@ static int ti_cpufreq_probe(struct platform_device *pdev) { u32 version[VERSION_COUNT]; const struct of_device_id *match; - struct opp_table *ti_opp_table; struct ti_cpufreq_data *opp_data; - const char * const default_reg_names[] = {"vdd", "vbb"}; + const char * const default_reg_names[] = {"vdd", "vbb", NULL}; int ret; + struct dev_pm_opp_config config = { + .supported_hw = version, + .supported_hw_count = ARRAY_SIZE(version), + }; match = dev_get_platdata(&pdev->dev); if (!match) @@ -370,33 +372,21 @@ static int ti_cpufreq_probe(struct platform_device *pdev) if (ret) goto fail_put_node; - ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev, - version, VERSION_COUNT); - if (IS_ERR(ti_opp_table)) { - dev_err(opp_data->cpu_dev, - "Failed to set supported hardware\n"); - ret = PTR_ERR(ti_opp_table); - goto fail_put_node; - } - - opp_data->opp_table = ti_opp_table; - if (opp_data->soc_data->multi_regulator) { - const char * const *reg_names = default_reg_names; - if (opp_data->soc_data->reg_names) - reg_names = opp_data->soc_data->reg_names; - ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev, - reg_names, - ARRAY_SIZE(default_reg_names)); - if (IS_ERR(ti_opp_table)) { - dev_pm_opp_put_supported_hw(opp_data->opp_table); - ret = PTR_ERR(ti_opp_table); - goto fail_put_node; - } + config.regulator_names = opp_data->soc_data->reg_names; + else + config.regulator_names = default_reg_names; + } + + ret = dev_pm_opp_set_config(opp_data->cpu_dev, &config); + if (ret < 0) { + dev_err(opp_data->cpu_dev, "Failed to set OPP config\n"); + goto fail_put_node; } of_node_put(opp_data->opp_node); + register_cpufreq_dt: platform_device_register_simple("cpufreq-dt", -1, NULL, 0); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 62dd956025f3..6eceb1988243 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -8,6 +8,7 @@ * This code is licenced under the GPL. */ +#include "linux/percpu-defs.h" #include <linux/clockchips.h> #include <linux/kernel.h> #include <linux/mutex.h> @@ -279,6 +280,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, /* Shallower states are enabled, so update. */ dev->states_usage[entered_state].above++; + trace_cpu_idle_miss(dev->cpu, entered_state, false); break; } } else if (diff > delay) { @@ -290,8 +292,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, * Update if a deeper state would have been a * better match for the observed idle duration. */ - if (diff - delay >= drv->states[i].target_residency_ns) + if (diff - delay >= drv->states[i].target_residency_ns) { dev->states_usage[entered_state].below++; + trace_cpu_idle_miss(dev->cpu, entered_state, true); + } break; } diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index f64e3984689f..768ced3d6fe8 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -2,6 +2,7 @@ menuconfig CXL_BUS tristate "CXL (Compute Express Link) Devices Support" depends on PCI + select PCI_DOE help CXL is a bus that is electrically compatible with PCI Express, but layers three protocols on that signalling (CXL.io, CXL.cache, and @@ -102,4 +103,12 @@ config CXL_SUSPEND def_bool y depends on SUSPEND && CXL_MEM +config CXL_REGION + bool + default CXL_BUS + # For MAX_PHYSMEM_BITS + depends on SPARSEMEM + select MEMREGION + select GET_FREE_REGION + endif diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 40286f5df812..fb649683dd3a 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -9,10 +9,6 @@ #include "cxlpci.h" #include "cxl.h" -/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ -#define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways) -#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8) - static unsigned long cfmws_to_decoder_flags(int restrictions) { unsigned long flags = CXL_DECODER_F_ENABLE; @@ -34,7 +30,8 @@ static unsigned long cfmws_to_decoder_flags(int restrictions) static int cxl_acpi_cfmws_verify(struct device *dev, struct acpi_cedt_cfmws *cfmws) { - int expected_len; + int rc, expected_len; + unsigned int ways; if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) { dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n"); @@ -51,14 +48,14 @@ static int cxl_acpi_cfmws_verify(struct device *dev, return -EINVAL; } - if (CFMWS_INTERLEAVE_WAYS(cfmws) > CXL_DECODER_MAX_INTERLEAVE) { - dev_err(dev, "CFMWS Interleave Ways (%d) too large\n", - CFMWS_INTERLEAVE_WAYS(cfmws)); + rc = cxl_to_ways(cfmws->interleave_ways, &ways); + if (rc) { + dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n", + cfmws->interleave_ways); return -EINVAL; } - expected_len = struct_size((cfmws), interleave_targets, - CFMWS_INTERLEAVE_WAYS(cfmws)); + expected_len = struct_size(cfmws, interleave_targets, ways); if (cfmws->header.length < expected_len) { dev_err(dev, "CFMWS length %d less than expected %d\n", @@ -76,6 +73,8 @@ static int cxl_acpi_cfmws_verify(struct device *dev, struct cxl_cfmws_context { struct device *dev; struct cxl_port *root_port; + struct resource *cxl_res; + int id; }; static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, @@ -84,10 +83,14 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, int target_map[CXL_DECODER_MAX_INTERLEAVE]; struct cxl_cfmws_context *ctx = arg; struct cxl_port *root_port = ctx->root_port; + struct resource *cxl_res = ctx->cxl_res; + struct cxl_root_decoder *cxlrd; struct device *dev = ctx->dev; struct acpi_cedt_cfmws *cfmws; struct cxl_decoder *cxld; - int rc, i; + unsigned int ways, i, ig; + struct resource *res; + int rc; cfmws = (struct acpi_cedt_cfmws *) header; @@ -99,19 +102,51 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, return 0; } - for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++) + rc = cxl_to_ways(cfmws->interleave_ways, &ways); + if (rc) + return rc; + rc = cxl_to_granularity(cfmws->granularity, &ig); + if (rc) + return rc; + for (i = 0; i < ways; i++) target_map[i] = cfmws->interleave_targets[i]; - cxld = cxl_root_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws)); - if (IS_ERR(cxld)) + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++); + if (!res->name) + goto err_name; + + res->start = cfmws->base_hpa; + res->end = cfmws->base_hpa + cfmws->window_size - 1; + res->flags = IORESOURCE_MEM; + + /* add to the local resource tracking to establish a sort order */ + rc = insert_resource(cxl_res, res); + if (rc) + goto err_insert; + + cxlrd = cxl_root_decoder_alloc(root_port, ways); + if (IS_ERR(cxlrd)) return 0; + cxld = &cxlrd->cxlsd.cxld; cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); cxld->target_type = CXL_DECODER_EXPANDER; - cxld->platform_res = (struct resource)DEFINE_RES_MEM(cfmws->base_hpa, - cfmws->window_size); - cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws); - cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws); + cxld->hpa_range = (struct range) { + .start = res->start, + .end = res->end, + }; + cxld->interleave_ways = ways; + /* + * Minimize the x1 granularity to advertise support for any + * valid region granularity + */ + if (ways == 1) + ig = CXL_DECODER_MIN_GRANULARITY; + cxld->interleave_granularity = ig; rc = cxl_decoder_add(cxld, target_map); if (rc) @@ -119,15 +154,22 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, else rc = cxl_decoder_autoremove(dev, cxld); if (rc) { - dev_err(dev, "Failed to add decoder for %pr\n", - &cxld->platform_res); + dev_err(dev, "Failed to add decode range [%#llx - %#llx]\n", + cxld->hpa_range.start, cxld->hpa_range.end); return 0; } - dev_dbg(dev, "add: %s node: %d range %pr\n", dev_name(&cxld->dev), - phys_to_target_node(cxld->platform_res.start), - &cxld->platform_res); + dev_dbg(dev, "add: %s node: %d range [%#llx - %#llx]\n", + dev_name(&cxld->dev), + phys_to_target_node(cxld->hpa_range.start), + cxld->hpa_range.start, cxld->hpa_range.end); return 0; + +err_insert: + kfree(res->name); +err_name: + kfree(res); + return -ENOMEM; } __mock struct acpi_device *to_cxl_host_bridge(struct device *host, @@ -175,8 +217,7 @@ static int add_host_bridge_uport(struct device *match, void *arg) if (rc) return rc; - port = devm_cxl_add_port(host, match, dport->component_reg_phys, - root_port); + port = devm_cxl_add_port(host, match, dport->component_reg_phys, dport); if (IS_ERR(port)) return PTR_ERR(port); dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev)); @@ -282,9 +323,127 @@ static void cxl_acpi_lock_reset_class(void *dev) device_lock_reset_class(dev); } +static void del_cxl_resource(struct resource *res) +{ + kfree(res->name); + kfree(res); +} + +static void cxl_set_public_resource(struct resource *priv, struct resource *pub) +{ + priv->desc = (unsigned long) pub; +} + +static struct resource *cxl_get_public_resource(struct resource *priv) +{ + return (struct resource *) priv->desc; +} + +static void remove_cxl_resources(void *data) +{ + struct resource *res, *next, *cxl = data; + + for (res = cxl->child; res; res = next) { + struct resource *victim = cxl_get_public_resource(res); + + next = res->sibling; + remove_resource(res); + + if (victim) { + remove_resource(victim); + kfree(victim); + } + + del_cxl_resource(res); + } +} + +/** + * add_cxl_resources() - reflect CXL fixed memory windows in iomem_resource + * @cxl_res: A standalone resource tree where each CXL window is a sibling + * + * Walk each CXL window in @cxl_res and add it to iomem_resource potentially + * expanding its boundaries to ensure that any conflicting resources become + * children. If a window is expanded it may then conflict with a another window + * entry and require the window to be truncated or trimmed. Consider this + * situation: + * + * |-- "CXL Window 0" --||----- "CXL Window 1" -----| + * |--------------- "System RAM" -------------| + * + * ...where platform firmware has established as System RAM resource across 2 + * windows, but has left some portion of window 1 for dynamic CXL region + * provisioning. In this case "Window 0" will span the entirety of the "System + * RAM" span, and "CXL Window 1" is truncated to the remaining tail past the end + * of that "System RAM" resource. + */ +static int add_cxl_resources(struct resource *cxl_res) +{ + struct resource *res, *new, *next; + + for (res = cxl_res->child; res; res = next) { + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return -ENOMEM; + new->name = res->name; + new->start = res->start; + new->end = res->end; + new->flags = IORESOURCE_MEM; + new->desc = IORES_DESC_CXL; + + /* + * Record the public resource in the private cxl_res tree for + * later removal. + */ + cxl_set_public_resource(res, new); + + insert_resource_expand_to_fit(&iomem_resource, new); + + next = res->sibling; + while (next && resource_overlaps(new, next)) { + if (resource_contains(new, next)) { + struct resource *_next = next->sibling; + + remove_resource(next); + del_cxl_resource(next); + next = _next; + } else + next->start = new->end + 1; + } + } + return 0; +} + +static int pair_cxl_resource(struct device *dev, void *data) +{ + struct resource *cxl_res = data; + struct resource *p; + + if (!is_root_decoder(dev)) + return 0; + + for (p = cxl_res->child; p; p = p->sibling) { + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + struct resource res = { + .start = cxld->hpa_range.start, + .end = cxld->hpa_range.end, + .flags = IORESOURCE_MEM, + }; + + if (resource_contains(p, &res)) { + cxlrd->res = cxl_get_public_resource(p); + break; + } + } + + return 0; +} + static int cxl_acpi_probe(struct platform_device *pdev) { int rc; + struct resource *cxl_res; struct cxl_port *root_port; struct device *host = &pdev->dev; struct acpi_device *adev = ACPI_COMPANION(host); @@ -296,6 +455,14 @@ static int cxl_acpi_probe(struct platform_device *pdev) if (rc) return rc; + cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL); + if (!cxl_res) + return -ENOMEM; + cxl_res->name = "CXL mem"; + cxl_res->start = 0; + cxl_res->end = -1; + cxl_res->flags = IORESOURCE_MEM; + root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); if (IS_ERR(root_port)) return PTR_ERR(root_port); @@ -306,11 +473,28 @@ static int cxl_acpi_probe(struct platform_device *pdev) if (rc < 0) return rc; + rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res); + if (rc) + return rc; + ctx = (struct cxl_cfmws_context) { .dev = host, .root_port = root_port, + .cxl_res = cxl_res, }; - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx); + rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx); + if (rc < 0) + return -ENXIO; + + rc = add_cxl_resources(cxl_res); + if (rc) + return rc; + + /* + * Populate the root decoders with their related iomem resource, + * if present + */ + device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource); /* * Root level scanned with host-bridge as dports, now scan host-bridges @@ -337,12 +521,19 @@ static const struct acpi_device_id cxl_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids); +static const struct platform_device_id cxl_test_ids[] = { + { "cxl_acpi" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, cxl_test_ids); + static struct platform_driver cxl_acpi_driver = { .probe = cxl_acpi_probe, .driver = { .name = KBUILD_MODNAME, .acpi_match_table = cxl_acpi_ids, }, + .id_table = cxl_test_ids, }; module_platform_driver(cxl_acpi_driver); diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 9d35085d25af..79c7257f4107 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -10,3 +10,4 @@ cxl_core-y += memdev.o cxl_core-y += mbox.o cxl_core-y += pci.o cxl_core-y += hdm.o +cxl_core-$(CONFIG_CXL_REGION) += region.o diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 1a50c0fc399c..1d8f87be283f 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -9,6 +9,36 @@ extern const struct device_type cxl_nvdimm_type; extern struct attribute_group cxl_base_attribute_group; +#ifdef CONFIG_CXL_REGION +extern struct device_attribute dev_attr_create_pmem_region; +extern struct device_attribute dev_attr_delete_region; +extern struct device_attribute dev_attr_region; +extern const struct device_type cxl_pmem_region_type; +extern const struct device_type cxl_region_type; +void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled); +#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr) +#define CXL_REGION_TYPE(x) (&cxl_region_type) +#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr), +#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type) +int cxl_region_init(void); +void cxl_region_exit(void); +#else +static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled) +{ +} +static inline int cxl_region_init(void) +{ + return 0; +} +static inline void cxl_region_exit(void) +{ +} +#define CXL_REGION_ATTR(x) NULL +#define CXL_REGION_TYPE(x) NULL +#define SET_CXL_REGION_ATTR(x) +#define CXL_PMEM_REGION_TYPE(x) NULL +#endif + struct cxl_send_command; struct cxl_mem_query_commands; int cxl_query_cmd(struct cxl_memdev *cxlmd, @@ -17,9 +47,28 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s); void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr, resource_size_t length); +struct dentry *cxl_debugfs_create_dir(const char *dir); +int cxl_dpa_set_mode(struct cxl_endpoint_decoder *cxled, + enum cxl_decoder_mode mode); +int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, unsigned long long size); +int cxl_dpa_free(struct cxl_endpoint_decoder *cxled); +resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled); +resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled); +extern struct rw_semaphore cxl_dpa_rwsem; + +bool is_switch_decoder(struct device *dev); +struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev); +static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port, + struct cxl_memdev *cxlmd) +{ + if (!port) + return NULL; + + return xa_load(&port->endpoints, (unsigned long)&cxlmd->dev); +} + int cxl_memdev_init(void); void cxl_memdev_exit(void); void cxl_mbox_init(void); -void cxl_mbox_exit(void); #endif /* __CXL_CORE_H__ */ diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index bfc8ee876278..d1d2caea5c62 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ #include <linux/io-64-nonatomic-hi-lo.h> +#include <linux/seq_file.h> #include <linux/device.h> #include <linux/delay.h> @@ -16,6 +17,8 @@ * for enumerating these registers and capabilities. */ +DECLARE_RWSEM(cxl_dpa_rwsem); + static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, int *target_map) { @@ -46,20 +49,22 @@ static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, */ int devm_cxl_add_passthrough_decoder(struct cxl_port *port) { - struct cxl_decoder *cxld; - struct cxl_dport *dport; + struct cxl_switch_decoder *cxlsd; + struct cxl_dport *dport = NULL; int single_port_map[1]; + unsigned long index; - cxld = cxl_switch_decoder_alloc(port, 1); - if (IS_ERR(cxld)) - return PTR_ERR(cxld); + cxlsd = cxl_switch_decoder_alloc(port, 1); + if (IS_ERR(cxlsd)) + return PTR_ERR(cxlsd); device_lock_assert(&port->dev); - dport = list_first_entry(&port->dports, typeof(*dport), list); + xa_for_each(&port->dports, index, dport) + break; single_port_map[0] = dport->port_id; - return add_hdm_decoder(port, cxld, single_port_map); + return add_hdm_decoder(port, &cxlsd->cxld, single_port_map); } EXPORT_SYMBOL_NS_GPL(devm_cxl_add_passthrough_decoder, CXL); @@ -124,47 +129,577 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port) return ERR_PTR(-ENXIO); } + dev_set_drvdata(dev, cxlhdm); + return cxlhdm; } EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_hdm, CXL); -static int to_interleave_granularity(u32 ctrl) +static void __cxl_dpa_debug(struct seq_file *file, struct resource *r, int depth) +{ + unsigned long long start = r->start, end = r->end; + + seq_printf(file, "%*s%08llx-%08llx : %s\n", depth * 2, "", start, end, + r->name); +} + +void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds) +{ + struct resource *p1, *p2; + + down_read(&cxl_dpa_rwsem); + for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) { + __cxl_dpa_debug(file, p1, 0); + for (p2 = p1->child; p2; p2 = p2->sibling) + __cxl_dpa_debug(file, p2, 1); + } + up_read(&cxl_dpa_rwsem); +} +EXPORT_SYMBOL_NS_GPL(cxl_dpa_debug, CXL); + +/* + * Must be called in a context that synchronizes against this decoder's + * port ->remove() callback (like an endpoint decoder sysfs attribute) + */ +static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_port *port = cxled_to_port(cxled); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct resource *res = cxled->dpa_res; + resource_size_t skip_start; + + lockdep_assert_held_write(&cxl_dpa_rwsem); + + /* save @skip_start, before @res is released */ + skip_start = res->start - cxled->skip; + __release_region(&cxlds->dpa_res, res->start, resource_size(res)); + if (cxled->skip) + __release_region(&cxlds->dpa_res, skip_start, cxled->skip); + cxled->skip = 0; + cxled->dpa_res = NULL; + put_device(&cxled->cxld.dev); + port->hdm_end--; +} + +static void cxl_dpa_release(void *cxled) +{ + down_write(&cxl_dpa_rwsem); + __cxl_dpa_release(cxled); + up_write(&cxl_dpa_rwsem); +} + +/* + * Must be called from context that will not race port device + * unregistration, like decoder sysfs attribute methods + */ +static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_port *port = cxled_to_port(cxled); + + lockdep_assert_held_write(&cxl_dpa_rwsem); + devm_remove_action(&port->dev, cxl_dpa_release, cxled); + __cxl_dpa_release(cxled); +} + +static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled, + resource_size_t base, resource_size_t len, + resource_size_t skipped) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_port *port = cxled_to_port(cxled); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct device *dev = &port->dev; + struct resource *res; + + lockdep_assert_held_write(&cxl_dpa_rwsem); + + if (!len) + goto success; + + if (cxled->dpa_res) { + dev_dbg(dev, "decoder%d.%d: existing allocation %pr assigned\n", + port->id, cxled->cxld.id, cxled->dpa_res); + return -EBUSY; + } + + if (port->hdm_end + 1 != cxled->cxld.id) { + /* + * Assumes alloc and commit order is always in hardware instance + * order per expectations from 8.2.5.12.20 Committing Decoder + * Programming that enforce decoder[m] committed before + * decoder[m+1] commit start. + */ + dev_dbg(dev, "decoder%d.%d: expected decoder%d.%d\n", port->id, + cxled->cxld.id, port->id, port->hdm_end + 1); + return -EBUSY; + } + + if (skipped) { + res = __request_region(&cxlds->dpa_res, base - skipped, skipped, + dev_name(&cxled->cxld.dev), 0); + if (!res) { + dev_dbg(dev, + "decoder%d.%d: failed to reserve skipped space\n", + port->id, cxled->cxld.id); + return -EBUSY; + } + } + res = __request_region(&cxlds->dpa_res, base, len, + dev_name(&cxled->cxld.dev), 0); + if (!res) { + dev_dbg(dev, "decoder%d.%d: failed to reserve allocation\n", + port->id, cxled->cxld.id); + if (skipped) + __release_region(&cxlds->dpa_res, base - skipped, + skipped); + return -EBUSY; + } + cxled->dpa_res = res; + cxled->skip = skipped; + + if (resource_contains(&cxlds->pmem_res, res)) + cxled->mode = CXL_DECODER_PMEM; + else if (resource_contains(&cxlds->ram_res, res)) + cxled->mode = CXL_DECODER_RAM; + else { + dev_dbg(dev, "decoder%d.%d: %pr mixed\n", port->id, + cxled->cxld.id, cxled->dpa_res); + cxled->mode = CXL_DECODER_MIXED; + } + +success: + port->hdm_end++; + get_device(&cxled->cxld.dev); + return 0; +} + +static int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled, + resource_size_t base, resource_size_t len, + resource_size_t skipped) { - int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl); + struct cxl_port *port = cxled_to_port(cxled); + int rc; + + down_write(&cxl_dpa_rwsem); + rc = __cxl_dpa_reserve(cxled, base, len, skipped); + up_write(&cxl_dpa_rwsem); + + if (rc) + return rc; - return 256 << val; + return devm_add_action_or_reset(&port->dev, cxl_dpa_release, cxled); } -static int to_interleave_ways(u32 ctrl) +resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled) { - int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl); + resource_size_t size = 0; + + down_read(&cxl_dpa_rwsem); + if (cxled->dpa_res) + size = resource_size(cxled->dpa_res); + up_read(&cxl_dpa_rwsem); - switch (val) { - case 0 ... 4: - return 1 << val; - case 8 ... 10: - return 3 << (val - 8); + return size; +} + +resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled) +{ + resource_size_t base = -1; + + down_read(&cxl_dpa_rwsem); + if (cxled->dpa_res) + base = cxled->dpa_res->start; + up_read(&cxl_dpa_rwsem); + + return base; +} + +int cxl_dpa_free(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_port *port = cxled_to_port(cxled); + struct device *dev = &cxled->cxld.dev; + int rc; + + down_write(&cxl_dpa_rwsem); + if (!cxled->dpa_res) { + rc = 0; + goto out; + } + if (cxled->cxld.region) { + dev_dbg(dev, "decoder assigned to: %s\n", + dev_name(&cxled->cxld.region->dev)); + rc = -EBUSY; + goto out; + } + if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) { + dev_dbg(dev, "decoder enabled\n"); + rc = -EBUSY; + goto out; + } + if (cxled->cxld.id != port->hdm_end) { + dev_dbg(dev, "expected decoder%d.%d\n", port->id, + port->hdm_end); + rc = -EBUSY; + goto out; + } + devm_cxl_dpa_release(cxled); + rc = 0; +out: + up_write(&cxl_dpa_rwsem); + return rc; +} + +int cxl_dpa_set_mode(struct cxl_endpoint_decoder *cxled, + enum cxl_decoder_mode mode) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct device *dev = &cxled->cxld.dev; + int rc; + + switch (mode) { + case CXL_DECODER_RAM: + case CXL_DECODER_PMEM: + break; default: + dev_dbg(dev, "unsupported mode: %d\n", mode); + return -EINVAL; + } + + down_write(&cxl_dpa_rwsem); + if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) { + rc = -EBUSY; + goto out; + } + + /* + * Only allow modes that are supported by the current partition + * configuration + */ + if (mode == CXL_DECODER_PMEM && !resource_size(&cxlds->pmem_res)) { + dev_dbg(dev, "no available pmem capacity\n"); + rc = -ENXIO; + goto out; + } + if (mode == CXL_DECODER_RAM && !resource_size(&cxlds->ram_res)) { + dev_dbg(dev, "no available ram capacity\n"); + rc = -ENXIO; + goto out; + } + + cxled->mode = mode; + rc = 0; +out: + up_write(&cxl_dpa_rwsem); + + return rc; +} + +int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, unsigned long long size) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + resource_size_t free_ram_start, free_pmem_start; + struct cxl_port *port = cxled_to_port(cxled); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct device *dev = &cxled->cxld.dev; + resource_size_t start, avail, skip; + struct resource *p, *last; + int rc; + + down_write(&cxl_dpa_rwsem); + if (cxled->cxld.region) { + dev_dbg(dev, "decoder attached to %s\n", + dev_name(&cxled->cxld.region->dev)); + rc = -EBUSY; + goto out; + } + + if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) { + dev_dbg(dev, "decoder enabled\n"); + rc = -EBUSY; + goto out; + } + + for (p = cxlds->ram_res.child, last = NULL; p; p = p->sibling) + last = p; + if (last) + free_ram_start = last->end + 1; + else + free_ram_start = cxlds->ram_res.start; + + for (p = cxlds->pmem_res.child, last = NULL; p; p = p->sibling) + last = p; + if (last) + free_pmem_start = last->end + 1; + else + free_pmem_start = cxlds->pmem_res.start; + + if (cxled->mode == CXL_DECODER_RAM) { + start = free_ram_start; + avail = cxlds->ram_res.end - start + 1; + skip = 0; + } else if (cxled->mode == CXL_DECODER_PMEM) { + resource_size_t skip_start, skip_end; + + start = free_pmem_start; + avail = cxlds->pmem_res.end - start + 1; + skip_start = free_ram_start; + + /* + * If some pmem is already allocated, then that allocation + * already handled the skip. + */ + if (cxlds->pmem_res.child && + skip_start == cxlds->pmem_res.child->start) + skip_end = skip_start - 1; + else + skip_end = start - 1; + skip = skip_end - skip_start + 1; + } else { + dev_dbg(dev, "mode not set\n"); + rc = -EINVAL; + goto out; + } + + if (size > avail) { + dev_dbg(dev, "%pa exceeds available %s capacity: %pa\n", &size, + cxled->mode == CXL_DECODER_RAM ? "ram" : "pmem", + &avail); + rc = -ENOSPC; + goto out; + } + + rc = __cxl_dpa_reserve(cxled, start, size, skip); +out: + up_write(&cxl_dpa_rwsem); + + if (rc) + return rc; + + return devm_add_action_or_reset(&port->dev, cxl_dpa_release, cxled); +} + +static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl) +{ + u16 eig; + u8 eiw; + + /* + * Input validation ensures these warns never fire, but otherwise + * suppress unititalized variable usage warnings. + */ + if (WARN_ONCE(ways_to_cxl(cxld->interleave_ways, &eiw), + "invalid interleave_ways: %d\n", cxld->interleave_ways)) + return; + if (WARN_ONCE(granularity_to_cxl(cxld->interleave_granularity, &eig), + "invalid interleave_granularity: %d\n", + cxld->interleave_granularity)) + return; + + u32p_replace_bits(ctrl, eig, CXL_HDM_DECODER0_CTRL_IG_MASK); + u32p_replace_bits(ctrl, eiw, CXL_HDM_DECODER0_CTRL_IW_MASK); + *ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT; +} + +static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl) +{ + u32p_replace_bits(ctrl, !!(cxld->target_type == 3), + CXL_HDM_DECODER0_CTRL_TYPE); +} + +static int cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt) +{ + struct cxl_dport **t = &cxlsd->target[0]; + int ways = cxlsd->cxld.interleave_ways; + + if (dev_WARN_ONCE(&cxlsd->cxld.dev, + ways > 8 || ways > cxlsd->nr_targets, + "ways: %d overflows targets: %d\n", ways, + cxlsd->nr_targets)) + return -ENXIO; + + *tgt = FIELD_PREP(GENMASK(7, 0), t[0]->port_id); + if (ways > 1) + *tgt |= FIELD_PREP(GENMASK(15, 8), t[1]->port_id); + if (ways > 2) + *tgt |= FIELD_PREP(GENMASK(23, 16), t[2]->port_id); + if (ways > 3) + *tgt |= FIELD_PREP(GENMASK(31, 24), t[3]->port_id); + if (ways > 4) + *tgt |= FIELD_PREP(GENMASK_ULL(39, 32), t[4]->port_id); + if (ways > 5) + *tgt |= FIELD_PREP(GENMASK_ULL(47, 40), t[5]->port_id); + if (ways > 6) + *tgt |= FIELD_PREP(GENMASK_ULL(55, 48), t[6]->port_id); + if (ways > 7) + *tgt |= FIELD_PREP(GENMASK_ULL(63, 56), t[7]->port_id); + + return 0; +} + +/* + * Per CXL 2.0 8.2.5.12.20 Committing Decoder Programming, hardware must set + * committed or error within 10ms, but just be generous with 20ms to account for + * clock skew and other marginal behavior + */ +#define COMMIT_TIMEOUT_MS 20 +static int cxld_await_commit(void __iomem *hdm, int id) +{ + u32 ctrl; + int i; + + for (i = 0; i < COMMIT_TIMEOUT_MS; i++) { + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMIT_ERROR, ctrl)) { + ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT; + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); + return -EIO; + } + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl)) + return 0; + fsleep(1000); + } + + return -ETIMEDOUT; +} + +static int cxl_decoder_commit(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); + void __iomem *hdm = cxlhdm->regs.hdm_decoder; + int id = cxld->id, rc; + u64 base, size; + u32 ctrl; + + if (cxld->flags & CXL_DECODER_F_ENABLE) + return 0; + + if (port->commit_end + 1 != id) { + dev_dbg(&port->dev, + "%s: out of order commit, expected decoder%d.%d\n", + dev_name(&cxld->dev), port->id, port->commit_end + 1); + return -EBUSY; + } + + down_read(&cxl_dpa_rwsem); + /* common decoder settings */ + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id)); + cxld_set_interleave(cxld, &ctrl); + cxld_set_type(cxld, &ctrl); + base = cxld->hpa_range.start; + size = range_len(&cxld->hpa_range); + + writel(upper_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id)); + writel(lower_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id)); + writel(upper_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id)); + writel(lower_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id)); + + if (is_switch_decoder(&cxld->dev)) { + struct cxl_switch_decoder *cxlsd = + to_cxl_switch_decoder(&cxld->dev); + void __iomem *tl_hi = hdm + CXL_HDM_DECODER0_TL_HIGH(id); + void __iomem *tl_lo = hdm + CXL_HDM_DECODER0_TL_LOW(id); + u64 targets; + + rc = cxlsd_set_targets(cxlsd, &targets); + if (rc) { + dev_dbg(&port->dev, "%s: target configuration error\n", + dev_name(&cxld->dev)); + goto err; + } + + writel(upper_32_bits(targets), tl_hi); + writel(lower_32_bits(targets), tl_lo); + } else { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + void __iomem *sk_hi = hdm + CXL_HDM_DECODER0_SKIP_HIGH(id); + void __iomem *sk_lo = hdm + CXL_HDM_DECODER0_SKIP_LOW(id); + + writel(upper_32_bits(cxled->skip), sk_hi); + writel(lower_32_bits(cxled->skip), sk_lo); + } + + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); + up_read(&cxl_dpa_rwsem); + + port->commit_end++; + rc = cxld_await_commit(hdm, cxld->id); +err: + if (rc) { + dev_dbg(&port->dev, "%s: error %d committing decoder\n", + dev_name(&cxld->dev), rc); + cxld->reset(cxld); + return rc; + } + cxld->flags |= CXL_DECODER_F_ENABLE; + + return 0; +} + +static int cxl_decoder_reset(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); + void __iomem *hdm = cxlhdm->regs.hdm_decoder; + int id = cxld->id; + u32 ctrl; + + if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0) return 0; + + if (port->commit_end != id) { + dev_dbg(&port->dev, + "%s: out of order reset, expected decoder%d.%d\n", + dev_name(&cxld->dev), port->id, port->commit_end); + return -EBUSY; } + + down_read(&cxl_dpa_rwsem); + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); + ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT; + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); + + writel(0, hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id)); + writel(0, hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id)); + writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id)); + writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id)); + up_read(&cxl_dpa_rwsem); + + port->commit_end--; + cxld->flags &= ~CXL_DECODER_F_ENABLE; + + return 0; } static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, - int *target_map, void __iomem *hdm, int which) + int *target_map, void __iomem *hdm, int which, + u64 *dpa_base) { - u64 size, base; + struct cxl_endpoint_decoder *cxled = NULL; + u64 size, base, skip, dpa_size; + bool committed; + u32 remainder; + int i, rc; u32 ctrl; - int i; union { u64 value; unsigned char target_id[8]; } target_list; + if (is_endpoint_decoder(&cxld->dev)) + cxled = to_cxl_endpoint_decoder(&cxld->dev); + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which)); base = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(which)); size = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(which)); + committed = !!(ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED); + cxld->commit = cxl_decoder_commit; + cxld->reset = cxl_decoder_reset; - if (!(ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED)) + if (!committed) size = 0; if (base == U64_MAX || size == U64_MAX) { dev_warn(&port->dev, "decoder%d.%d: Invalid resource range\n", @@ -172,39 +707,77 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld, return -ENXIO; } - cxld->decoder_range = (struct range) { + cxld->hpa_range = (struct range) { .start = base, .end = base + size - 1, }; - /* switch decoders are always enabled if committed */ - if (ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED) { + /* decoders are enabled if committed */ + if (committed) { cxld->flags |= CXL_DECODER_F_ENABLE; if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK) cxld->flags |= CXL_DECODER_F_LOCK; + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl)) + cxld->target_type = CXL_DECODER_EXPANDER; + else + cxld->target_type = CXL_DECODER_ACCELERATOR; + if (cxld->id != port->commit_end + 1) { + dev_warn(&port->dev, + "decoder%d.%d: Committed out of order\n", + port->id, cxld->id); + return -ENXIO; + } + port->commit_end = cxld->id; + } else { + /* unless / until type-2 drivers arrive, assume type-3 */ + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0) { + ctrl |= CXL_HDM_DECODER0_CTRL_TYPE; + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which)); + } + cxld->target_type = CXL_DECODER_EXPANDER; } - cxld->interleave_ways = to_interleave_ways(ctrl); - if (!cxld->interleave_ways) { + rc = cxl_to_ways(FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl), + &cxld->interleave_ways); + if (rc) { dev_warn(&port->dev, "decoder%d.%d: Invalid interleave ways (ctrl: %#x)\n", port->id, cxld->id, ctrl); - return -ENXIO; + return rc; } - cxld->interleave_granularity = to_interleave_granularity(ctrl); + rc = cxl_to_granularity(FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl), + &cxld->interleave_granularity); + if (rc) + return rc; - if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl)) - cxld->target_type = CXL_DECODER_EXPANDER; - else - cxld->target_type = CXL_DECODER_ACCELERATOR; + if (!cxled) { + target_list.value = + ioread64_hi_lo(hdm + CXL_HDM_DECODER0_TL_LOW(which)); + for (i = 0; i < cxld->interleave_ways; i++) + target_map[i] = target_list.target_id[i]; - if (is_endpoint_decoder(&cxld->dev)) return 0; + } - target_list.value = - ioread64_hi_lo(hdm + CXL_HDM_DECODER0_TL_LOW(which)); - for (i = 0; i < cxld->interleave_ways; i++) - target_map[i] = target_list.target_id[i]; + if (!committed) + return 0; + dpa_size = div_u64_rem(size, cxld->interleave_ways, &remainder); + if (remainder) { + dev_err(&port->dev, + "decoder%d.%d: invalid committed configuration size: %#llx ways: %d\n", + port->id, cxld->id, size, cxld->interleave_ways); + return -ENXIO; + } + skip = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_SKIP_LOW(which)); + rc = devm_cxl_dpa_reserve(cxled, *dpa_base + skip, dpa_size, skip); + if (rc) { + dev_err(&port->dev, + "decoder%d.%d: Failed to reserve DPA range %#llx - %#llx\n (%d)", + port->id, cxld->id, *dpa_base, + *dpa_base + dpa_size + skip - 1, rc); + return rc; + } + *dpa_base += dpa_size + skip; return 0; } @@ -216,7 +789,8 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm) { void __iomem *hdm = cxlhdm->regs.hdm_decoder; struct cxl_port *port = cxlhdm->port; - int i, committed, failed; + int i, committed; + u64 dpa_base = 0; u32 ctrl; /* @@ -236,27 +810,37 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm) if (committed != cxlhdm->decoder_count) msleep(20); - for (i = 0, failed = 0; i < cxlhdm->decoder_count; i++) { + for (i = 0; i < cxlhdm->decoder_count; i++) { int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 }; int rc, target_count = cxlhdm->target_count; struct cxl_decoder *cxld; - if (is_cxl_endpoint(port)) - cxld = cxl_endpoint_decoder_alloc(port); - else - cxld = cxl_switch_decoder_alloc(port, target_count); - if (IS_ERR(cxld)) { - dev_warn(&port->dev, - "Failed to allocate the decoder\n"); - return PTR_ERR(cxld); + if (is_cxl_endpoint(port)) { + struct cxl_endpoint_decoder *cxled; + + cxled = cxl_endpoint_decoder_alloc(port); + if (IS_ERR(cxled)) { + dev_warn(&port->dev, + "Failed to allocate the decoder\n"); + return PTR_ERR(cxled); + } + cxld = &cxled->cxld; + } else { + struct cxl_switch_decoder *cxlsd; + + cxlsd = cxl_switch_decoder_alloc(port, target_count); + if (IS_ERR(cxlsd)) { + dev_warn(&port->dev, + "Failed to allocate the decoder\n"); + return PTR_ERR(cxlsd); + } + cxld = &cxlsd->cxld; } - rc = init_hdm_decoder(port, cxld, target_map, - cxlhdm->regs.hdm_decoder, i); + rc = init_hdm_decoder(port, cxld, target_map, hdm, i, &dpa_base); if (rc) { put_device(&cxld->dev); - failed++; - continue; + return rc; } rc = add_hdm_decoder(port, cxld, target_map); if (rc) { @@ -266,11 +850,6 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm) } } - if (failed == cxlhdm->decoder_count) { - dev_err(&port->dev, "No valid decoders found\n"); - return -ENXIO; - } - return 0; } EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_decoders, CXL); diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index cbf23beebebe..16176b9278b4 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -718,12 +718,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL); */ static int cxl_mem_get_partition_info(struct cxl_dev_state *cxlds) { - struct cxl_mbox_get_partition_info { - __le64 active_volatile_cap; - __le64 active_persistent_cap; - __le64 next_volatile_cap; - __le64 next_persistent_cap; - } __packed pi; + struct cxl_mbox_get_partition_info pi; int rc; rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_PARTITION_INFO, NULL, 0, @@ -773,15 +768,6 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds) cxlds->partition_align_bytes = le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER; - dev_dbg(cxlds->dev, - "Identify Memory Device\n" - " total_bytes = %#llx\n" - " volatile_only_bytes = %#llx\n" - " persistent_only_bytes = %#llx\n" - " partition_align_bytes = %#llx\n", - cxlds->total_bytes, cxlds->volatile_only_bytes, - cxlds->persistent_only_bytes, cxlds->partition_align_bytes); - cxlds->lsa_size = le32_to_cpu(id.lsa_size); memcpy(cxlds->firmware_version, id.fw_revision, sizeof(id.fw_revision)); @@ -789,42 +775,63 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds) } EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL); -int cxl_mem_create_range_info(struct cxl_dev_state *cxlds) +static int add_dpa_res(struct device *dev, struct resource *parent, + struct resource *res, resource_size_t start, + resource_size_t size, const char *type) { int rc; - if (cxlds->partition_align_bytes == 0) { - cxlds->ram_range.start = 0; - cxlds->ram_range.end = cxlds->volatile_only_bytes - 1; - cxlds->pmem_range.start = cxlds->volatile_only_bytes; - cxlds->pmem_range.end = cxlds->volatile_only_bytes + - cxlds->persistent_only_bytes - 1; + res->name = type; + res->start = start; + res->end = start + size - 1; + res->flags = IORESOURCE_MEM; + if (resource_size(res) == 0) { + dev_dbg(dev, "DPA(%s): no capacity\n", res->name); return 0; } - - rc = cxl_mem_get_partition_info(cxlds); + rc = request_resource(parent, res); if (rc) { - dev_err(cxlds->dev, "Failed to query partition information\n"); + dev_err(dev, "DPA(%s): failed to track %pr (%d)\n", res->name, + res, rc); return rc; } - dev_dbg(cxlds->dev, - "Get Partition Info\n" - " active_volatile_bytes = %#llx\n" - " active_persistent_bytes = %#llx\n" - " next_volatile_bytes = %#llx\n" - " next_persistent_bytes = %#llx\n", - cxlds->active_volatile_bytes, cxlds->active_persistent_bytes, - cxlds->next_volatile_bytes, cxlds->next_persistent_bytes); + dev_dbg(dev, "DPA(%s): %pr\n", res->name, res); + + return 0; +} - cxlds->ram_range.start = 0; - cxlds->ram_range.end = cxlds->active_volatile_bytes - 1; +int cxl_mem_create_range_info(struct cxl_dev_state *cxlds) +{ + struct device *dev = cxlds->dev; + int rc; - cxlds->pmem_range.start = cxlds->active_volatile_bytes; - cxlds->pmem_range.end = - cxlds->active_volatile_bytes + cxlds->active_persistent_bytes - 1; + cxlds->dpa_res = + (struct resource)DEFINE_RES_MEM(0, cxlds->total_bytes); - return 0; + if (cxlds->partition_align_bytes == 0) { + rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0, + cxlds->volatile_only_bytes, "ram"); + if (rc) + return rc; + return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res, + cxlds->volatile_only_bytes, + cxlds->persistent_only_bytes, "pmem"); + } + + rc = cxl_mem_get_partition_info(cxlds); + if (rc) { + dev_err(dev, "Failed to query partition information\n"); + return rc; + } + + rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0, + cxlds->active_volatile_bytes, "ram"); + if (rc) + return rc; + return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res, + cxlds->active_volatile_bytes, + cxlds->active_persistent_bytes, "pmem"); } EXPORT_SYMBOL_NS_GPL(cxl_mem_create_range_info, CXL); @@ -845,19 +852,11 @@ struct cxl_dev_state *cxl_dev_state_create(struct device *dev) } EXPORT_SYMBOL_NS_GPL(cxl_dev_state_create, CXL); -static struct dentry *cxl_debugfs; - void __init cxl_mbox_init(void) { struct dentry *mbox_debugfs; - cxl_debugfs = debugfs_create_dir("cxl", NULL); - mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs); + mbox_debugfs = cxl_debugfs_create_dir("mbox"); debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs, &cxl_raw_allow_all); } - -void cxl_mbox_exit(void) -{ - debugfs_remove_recursive(cxl_debugfs); -} diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index f7cdcd33504a..20ce488a7754 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -68,7 +68,7 @@ static ssize_t ram_size_show(struct device *dev, struct device_attribute *attr, { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); struct cxl_dev_state *cxlds = cxlmd->cxlds; - unsigned long long len = range_len(&cxlds->ram_range); + unsigned long long len = resource_size(&cxlds->ram_res); return sysfs_emit(buf, "%#llx\n", len); } @@ -81,7 +81,7 @@ static ssize_t pmem_size_show(struct device *dev, struct device_attribute *attr, { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); struct cxl_dev_state *cxlds = cxlmd->cxlds; - unsigned long long len = range_len(&cxlds->pmem_range); + unsigned long long len = resource_size(&cxlds->pmem_res); return sysfs_emit(buf, "%#llx\n", len); } diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index c4c99ff7b55e..9240df53ed87 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -4,6 +4,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/pci-doe.h> #include <cxlpci.h> #include <cxlmem.h> #include <cxl.h> @@ -225,7 +226,6 @@ static int dvsec_range_allowed(struct device *dev, void *arg) { struct range *dev_range = arg; struct cxl_decoder *cxld; - struct range root_range; if (!is_root_decoder(dev)) return 0; @@ -237,12 +237,7 @@ static int dvsec_range_allowed(struct device *dev, void *arg) if (!(cxld->flags & CXL_DECODER_F_RAM)) return 0; - root_range = (struct range) { - .start = cxld->platform_res.start, - .end = cxld->platform_res.end, - }; - - return range_contains(&root_range, dev_range); + return range_contains(&cxld->hpa_range, dev_range); } static void disable_hdm(void *_cxlhdm) @@ -458,3 +453,175 @@ hdm_init: return 0; } EXPORT_SYMBOL_NS_GPL(cxl_hdm_decode_init, CXL); + +#define CXL_DOE_TABLE_ACCESS_REQ_CODE 0x000000ff +#define CXL_DOE_TABLE_ACCESS_REQ_CODE_READ 0 +#define CXL_DOE_TABLE_ACCESS_TABLE_TYPE 0x0000ff00 +#define CXL_DOE_TABLE_ACCESS_TABLE_TYPE_CDATA 0 +#define CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE 0xffff0000 +#define CXL_DOE_TABLE_ACCESS_LAST_ENTRY 0xffff +#define CXL_DOE_PROTOCOL_TABLE_ACCESS 2 + +static struct pci_doe_mb *find_cdat_doe(struct device *uport) +{ + struct cxl_memdev *cxlmd; + struct cxl_dev_state *cxlds; + unsigned long index; + void *entry; + + cxlmd = to_cxl_memdev(uport); + cxlds = cxlmd->cxlds; + + xa_for_each(&cxlds->doe_mbs, index, entry) { + struct pci_doe_mb *cur = entry; + + if (pci_doe_supports_prot(cur, PCI_DVSEC_VENDOR_ID_CXL, + CXL_DOE_PROTOCOL_TABLE_ACCESS)) + return cur; + } + + return NULL; +} + +#define CDAT_DOE_REQ(entry_handle) \ + (FIELD_PREP(CXL_DOE_TABLE_ACCESS_REQ_CODE, \ + CXL_DOE_TABLE_ACCESS_REQ_CODE_READ) | \ + FIELD_PREP(CXL_DOE_TABLE_ACCESS_TABLE_TYPE, \ + CXL_DOE_TABLE_ACCESS_TABLE_TYPE_CDATA) | \ + FIELD_PREP(CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE, (entry_handle))) + +static void cxl_doe_task_complete(struct pci_doe_task *task) +{ + complete(task->private); +} + +struct cdat_doe_task { + u32 request_pl; + u32 response_pl[32]; + struct completion c; + struct pci_doe_task task; +}; + +#define DECLARE_CDAT_DOE_TASK(req, cdt) \ +struct cdat_doe_task cdt = { \ + .c = COMPLETION_INITIALIZER_ONSTACK(cdt.c), \ + .request_pl = req, \ + .task = { \ + .prot.vid = PCI_DVSEC_VENDOR_ID_CXL, \ + .prot.type = CXL_DOE_PROTOCOL_TABLE_ACCESS, \ + .request_pl = &cdt.request_pl, \ + .request_pl_sz = sizeof(cdt.request_pl), \ + .response_pl = cdt.response_pl, \ + .response_pl_sz = sizeof(cdt.response_pl), \ + .complete = cxl_doe_task_complete, \ + .private = &cdt.c, \ + } \ +} + +static int cxl_cdat_get_length(struct device *dev, + struct pci_doe_mb *cdat_doe, + size_t *length) +{ + DECLARE_CDAT_DOE_TASK(CDAT_DOE_REQ(0), t); + int rc; + + rc = pci_doe_submit_task(cdat_doe, &t.task); + if (rc < 0) { + dev_err(dev, "DOE submit failed: %d", rc); + return rc; + } + wait_for_completion(&t.c); + if (t.task.rv < sizeof(u32)) + return -EIO; + + *length = t.response_pl[1]; + dev_dbg(dev, "CDAT length %zu\n", *length); + + return 0; +} + +static int cxl_cdat_read_table(struct device *dev, + struct pci_doe_mb *cdat_doe, + struct cxl_cdat *cdat) +{ + size_t length = cdat->length; + u32 *data = cdat->table; + int entry_handle = 0; + + do { + DECLARE_CDAT_DOE_TASK(CDAT_DOE_REQ(entry_handle), t); + size_t entry_dw; + u32 *entry; + int rc; + + rc = pci_doe_submit_task(cdat_doe, &t.task); + if (rc < 0) { + dev_err(dev, "DOE submit failed: %d", rc); + return rc; + } + wait_for_completion(&t.c); + /* 1 DW header + 1 DW data min */ + if (t.task.rv < (2 * sizeof(u32))) + return -EIO; + + /* Get the CXL table access header entry handle */ + entry_handle = FIELD_GET(CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE, + t.response_pl[0]); + entry = t.response_pl + 1; + entry_dw = t.task.rv / sizeof(u32); + /* Skip Header */ + entry_dw -= 1; + entry_dw = min(length / sizeof(u32), entry_dw); + /* Prevent length < 1 DW from causing a buffer overflow */ + if (entry_dw) { + memcpy(data, entry, entry_dw * sizeof(u32)); + length -= entry_dw * sizeof(u32); + data += entry_dw; + } + } while (entry_handle != CXL_DOE_TABLE_ACCESS_LAST_ENTRY); + + return 0; +} + +/** + * read_cdat_data - Read the CDAT data on this port + * @port: Port to read data from + * + * This call will sleep waiting for responses from the DOE mailbox. + */ +void read_cdat_data(struct cxl_port *port) +{ + struct pci_doe_mb *cdat_doe; + struct device *dev = &port->dev; + struct device *uport = port->uport; + size_t cdat_length; + int rc; + + cdat_doe = find_cdat_doe(uport); + if (!cdat_doe) { + dev_dbg(dev, "No CDAT mailbox\n"); + return; + } + + port->cdat_available = true; + + if (cxl_cdat_get_length(dev, cdat_doe, &cdat_length)) { + dev_dbg(dev, "No CDAT length\n"); + return; + } + + port->cdat.table = devm_kzalloc(dev, cdat_length, GFP_KERNEL); + if (!port->cdat.table) + return; + + port->cdat.length = cdat_length; + rc = cxl_cdat_read_table(dev, cdat_doe, &port->cdat); + if (rc) { + /* Don't leave table data allocated on error */ + devm_kfree(dev, port->cdat.table); + port->cdat.table = NULL; + port->cdat.length = 0; + dev_err(dev, "CDAT data read error\n"); + } +} +EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL); diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index bec7cfb54ebf..1d12a8206444 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -62,9 +62,9 @@ static int match_nvdimm_bridge(struct device *dev, void *data) return is_cxl_nvdimm_bridge(dev); } -struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd) +struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct device *start) { - struct cxl_port *port = find_cxl_root(&cxl_nvd->dev); + struct cxl_port *port = find_cxl_root(start); struct device *dev; if (!port) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index dbce99bdffab..bffde862de0b 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ #include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/memregion.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/module.h> #include <linux/pci.h> @@ -42,6 +44,8 @@ static int cxl_device_id(struct device *dev) return CXL_DEVICE_NVDIMM_BRIDGE; if (dev->type == &cxl_nvdimm_type) return CXL_DEVICE_NVDIMM; + if (dev->type == CXL_PMEM_REGION_TYPE()) + return CXL_DEVICE_PMEM_REGION; if (is_cxl_port(dev)) { if (is_cxl_root(to_cxl_port(dev))) return CXL_DEVICE_ROOT; @@ -49,6 +53,8 @@ static int cxl_device_id(struct device *dev) } if (is_cxl_memdev(dev)) return CXL_DEVICE_MEMORY_EXPANDER; + if (dev->type == CXL_REGION_TYPE()) + return CXL_DEVICE_REGION; return 0; } @@ -73,14 +79,8 @@ static ssize_t start_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cxl_decoder *cxld = to_cxl_decoder(dev); - u64 start; - if (is_root_decoder(dev)) - start = cxld->platform_res.start; - else - start = cxld->decoder_range.start; - - return sysfs_emit(buf, "%#llx\n", start); + return sysfs_emit(buf, "%#llx\n", cxld->hpa_range.start); } static DEVICE_ATTR_ADMIN_RO(start); @@ -88,14 +88,8 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cxl_decoder *cxld = to_cxl_decoder(dev); - u64 size; - - if (is_root_decoder(dev)) - size = resource_size(&cxld->platform_res); - else - size = range_len(&cxld->decoder_range); - return sysfs_emit(buf, "%#llx\n", size); + return sysfs_emit(buf, "%#llx\n", range_len(&cxld->hpa_range)); } static DEVICE_ATTR_RO(size); @@ -131,20 +125,21 @@ static ssize_t target_type_show(struct device *dev, } static DEVICE_ATTR_RO(target_type); -static ssize_t emit_target_list(struct cxl_decoder *cxld, char *buf) +static ssize_t emit_target_list(struct cxl_switch_decoder *cxlsd, char *buf) { + struct cxl_decoder *cxld = &cxlsd->cxld; ssize_t offset = 0; int i, rc = 0; for (i = 0; i < cxld->interleave_ways; i++) { - struct cxl_dport *dport = cxld->target[i]; + struct cxl_dport *dport = cxlsd->target[i]; struct cxl_dport *next = NULL; if (!dport) break; if (i + 1 < cxld->interleave_ways) - next = cxld->target[i + 1]; + next = cxlsd->target[i + 1]; rc = sysfs_emit_at(buf, offset, "%d%s", dport->port_id, next ? "," : ""); if (rc < 0) @@ -158,15 +153,15 @@ static ssize_t emit_target_list(struct cxl_decoder *cxld, char *buf) static ssize_t target_list_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct cxl_decoder *cxld = to_cxl_decoder(dev); + struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); ssize_t offset; unsigned int seq; int rc; do { - seq = read_seqbegin(&cxld->target_lock); - rc = emit_target_list(cxld, buf); - } while (read_seqretry(&cxld->target_lock, seq)); + seq = read_seqbegin(&cxlsd->target_lock); + rc = emit_target_list(cxlsd, buf); + } while (read_seqretry(&cxlsd->target_lock, seq)); if (rc < 0) return rc; @@ -180,10 +175,121 @@ static ssize_t target_list_show(struct device *dev, } static DEVICE_ATTR_RO(target_list); +static ssize_t mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + + switch (cxled->mode) { + case CXL_DECODER_RAM: + return sysfs_emit(buf, "ram\n"); + case CXL_DECODER_PMEM: + return sysfs_emit(buf, "pmem\n"); + case CXL_DECODER_NONE: + return sysfs_emit(buf, "none\n"); + case CXL_DECODER_MIXED: + default: + return sysfs_emit(buf, "mixed\n"); + } +} + +static ssize_t mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + enum cxl_decoder_mode mode; + ssize_t rc; + + if (sysfs_streq(buf, "pmem")) + mode = CXL_DECODER_PMEM; + else if (sysfs_streq(buf, "ram")) + mode = CXL_DECODER_RAM; + else + return -EINVAL; + + rc = cxl_dpa_set_mode(cxled, mode); + if (rc) + return rc; + + return len; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + u64 base = cxl_dpa_resource_start(cxled); + + return sysfs_emit(buf, "%#llx\n", base); +} +static DEVICE_ATTR_RO(dpa_resource); + +static ssize_t dpa_size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + resource_size_t size = cxl_dpa_size(cxled); + + return sysfs_emit(buf, "%pa\n", &size); +} + +static ssize_t dpa_size_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + unsigned long long size; + ssize_t rc; + + rc = kstrtoull(buf, 0, &size); + if (rc) + return rc; + + if (!IS_ALIGNED(size, SZ_256M)) + return -EINVAL; + + rc = cxl_dpa_free(cxled); + if (rc) + return rc; + + if (size == 0) + return len; + + rc = cxl_dpa_alloc(cxled, size); + if (rc) + return rc; + + return len; +} +static DEVICE_ATTR_RW(dpa_size); + +static ssize_t interleave_granularity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cxl_decoder *cxld = to_cxl_decoder(dev); + + return sysfs_emit(buf, "%d\n", cxld->interleave_granularity); +} + +static DEVICE_ATTR_RO(interleave_granularity); + +static ssize_t interleave_ways_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_decoder *cxld = to_cxl_decoder(dev); + + return sysfs_emit(buf, "%d\n", cxld->interleave_ways); +} + +static DEVICE_ATTR_RO(interleave_ways); + static struct attribute *cxl_decoder_base_attrs[] = { &dev_attr_start.attr, &dev_attr_size.attr, &dev_attr_locked.attr, + &dev_attr_interleave_granularity.attr, + &dev_attr_interleave_ways.attr, NULL, }; @@ -197,11 +303,35 @@ static struct attribute *cxl_decoder_root_attrs[] = { &dev_attr_cap_type2.attr, &dev_attr_cap_type3.attr, &dev_attr_target_list.attr, + SET_CXL_REGION_ATTR(create_pmem_region) + SET_CXL_REGION_ATTR(delete_region) NULL, }; +static bool can_create_pmem(struct cxl_root_decoder *cxlrd) +{ + unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM; + + return (cxlrd->cxlsd.cxld.flags & flags) == flags; +} + +static umode_t cxl_root_decoder_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + + if (a == CXL_REGION_ATTR(create_pmem_region) && !can_create_pmem(cxlrd)) + return 0; + + if (a == CXL_REGION_ATTR(delete_region) && !can_create_pmem(cxlrd)) + return 0; + + return a->mode; +} + static struct attribute_group cxl_decoder_root_attribute_group = { .attrs = cxl_decoder_root_attrs, + .is_visible = cxl_root_decoder_visible, }; static const struct attribute_group *cxl_decoder_root_attribute_groups[] = { @@ -214,6 +344,7 @@ static const struct attribute_group *cxl_decoder_root_attribute_groups[] = { static struct attribute *cxl_decoder_switch_attrs[] = { &dev_attr_target_type.attr, &dev_attr_target_list.attr, + SET_CXL_REGION_ATTR(region) NULL, }; @@ -230,6 +361,10 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = { static struct attribute *cxl_decoder_endpoint_attrs[] = { &dev_attr_target_type.attr, + &dev_attr_mode.attr, + &dev_attr_dpa_size.attr, + &dev_attr_dpa_resource.attr, + SET_CXL_REGION_ATTR(region) NULL, }; @@ -244,31 +379,64 @@ static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = { NULL, }; -static void cxl_decoder_release(struct device *dev) +static void __cxl_decoder_release(struct cxl_decoder *cxld) { - struct cxl_decoder *cxld = to_cxl_decoder(dev); - struct cxl_port *port = to_cxl_port(dev->parent); + struct cxl_port *port = to_cxl_port(cxld->dev.parent); ida_free(&port->decoder_ida, cxld->id); - kfree(cxld); put_device(&port->dev); } +static void cxl_endpoint_decoder_release(struct device *dev) +{ + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); + + __cxl_decoder_release(&cxled->cxld); + kfree(cxled); +} + +static void cxl_switch_decoder_release(struct device *dev) +{ + struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); + + __cxl_decoder_release(&cxlsd->cxld); + kfree(cxlsd); +} + +struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_root_decoder(dev), + "not a cxl_root_decoder device\n")) + return NULL; + return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_root_decoder, CXL); + +static void cxl_root_decoder_release(struct device *dev) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + + if (atomic_read(&cxlrd->region_id) >= 0) + memregion_free(atomic_read(&cxlrd->region_id)); + __cxl_decoder_release(&cxlrd->cxlsd.cxld); + kfree(cxlrd); +} + static const struct device_type cxl_decoder_endpoint_type = { .name = "cxl_decoder_endpoint", - .release = cxl_decoder_release, + .release = cxl_endpoint_decoder_release, .groups = cxl_decoder_endpoint_attribute_groups, }; static const struct device_type cxl_decoder_switch_type = { .name = "cxl_decoder_switch", - .release = cxl_decoder_release, + .release = cxl_switch_decoder_release, .groups = cxl_decoder_switch_attribute_groups, }; static const struct device_type cxl_decoder_root_type = { .name = "cxl_decoder_root", - .release = cxl_decoder_release, + .release = cxl_root_decoder_release, .groups = cxl_decoder_root_attribute_groups, }; @@ -283,39 +451,63 @@ bool is_root_decoder(struct device *dev) } EXPORT_SYMBOL_NS_GPL(is_root_decoder, CXL); -bool is_cxl_decoder(struct device *dev) +bool is_switch_decoder(struct device *dev) { - return dev->type && dev->type->release == cxl_decoder_release; + return is_root_decoder(dev) || dev->type == &cxl_decoder_switch_type; } -EXPORT_SYMBOL_NS_GPL(is_cxl_decoder, CXL); struct cxl_decoder *to_cxl_decoder(struct device *dev) { - if (dev_WARN_ONCE(dev, dev->type->release != cxl_decoder_release, + if (dev_WARN_ONCE(dev, + !is_switch_decoder(dev) && !is_endpoint_decoder(dev), "not a cxl_decoder device\n")) return NULL; return container_of(dev, struct cxl_decoder, dev); } EXPORT_SYMBOL_NS_GPL(to_cxl_decoder, CXL); +struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_endpoint_decoder(dev), + "not a cxl_endpoint_decoder device\n")) + return NULL; + return container_of(dev, struct cxl_endpoint_decoder, cxld.dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_endpoint_decoder, CXL); + +struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_switch_decoder(dev), + "not a cxl_switch_decoder device\n")) + return NULL; + return container_of(dev, struct cxl_switch_decoder, cxld.dev); +} + static void cxl_ep_release(struct cxl_ep *ep) { - if (!ep) - return; - list_del(&ep->list); put_device(ep->ep); kfree(ep); } +static void cxl_ep_remove(struct cxl_port *port, struct cxl_ep *ep) +{ + if (!ep) + return; + xa_erase(&port->endpoints, (unsigned long) ep->ep); + cxl_ep_release(ep); +} + static void cxl_port_release(struct device *dev) { struct cxl_port *port = to_cxl_port(dev); - struct cxl_ep *ep, *_e; + unsigned long index; + struct cxl_ep *ep; - device_lock(dev); - list_for_each_entry_safe(ep, _e, &port->endpoints, list) - cxl_ep_release(ep); - device_unlock(dev); + xa_for_each(&port->endpoints, index, ep) + cxl_ep_remove(port, ep); + xa_destroy(&port->endpoints); + xa_destroy(&port->dports); + xa_destroy(&port->regions); ida_free(&cxl_port_ida, port->id); kfree(port); } @@ -370,7 +562,7 @@ static void unregister_port(void *_port) lock_dev = &parent->dev; device_lock_assert(lock_dev); - port->uport = NULL; + port->dead = true; device_unregister(&port->dev); } @@ -395,7 +587,7 @@ static struct lock_class_key cxl_port_key; static struct cxl_port *cxl_port_alloc(struct device *uport, resource_size_t component_reg_phys, - struct cxl_port *parent_port) + struct cxl_dport *parent_dport) { struct cxl_port *port; struct device *dev; @@ -409,6 +601,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport, if (rc < 0) goto err; port->id = rc; + port->uport = uport; /* * The top-level cxl_port "cxl_root" does not have a cxl_port as @@ -417,17 +610,37 @@ static struct cxl_port *cxl_port_alloc(struct device *uport, * description. */ dev = &port->dev; - if (parent_port) { + if (parent_dport) { + struct cxl_port *parent_port = parent_dport->port; + struct cxl_port *iter; + dev->parent = &parent_port->dev; port->depth = parent_port->depth + 1; + port->parent_dport = parent_dport; + + /* + * walk to the host bridge, or the first ancestor that knows + * the host bridge + */ + iter = port; + while (!iter->host_bridge && + !is_cxl_root(to_cxl_port(iter->dev.parent))) + iter = to_cxl_port(iter->dev.parent); + if (iter->host_bridge) + port->host_bridge = iter->host_bridge; + else + port->host_bridge = iter->uport; + dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge)); } else dev->parent = uport; - port->uport = uport; port->component_reg_phys = component_reg_phys; ida_init(&port->decoder_ida); - INIT_LIST_HEAD(&port->dports); - INIT_LIST_HEAD(&port->endpoints); + port->hdm_end = -1; + port->commit_end = -1; + xa_init(&port->dports); + xa_init(&port->endpoints); + xa_init(&port->regions); device_initialize(dev); lockdep_set_class_and_subclass(&dev->mutex, &cxl_port_key, port->depth); @@ -447,24 +660,24 @@ err: * @host: host device for devm operations * @uport: "physical" device implementing this upstream port * @component_reg_phys: (optional) for configurable cxl_port instances - * @parent_port: next hop up in the CXL memory decode hierarchy + * @parent_dport: next hop up in the CXL memory decode hierarchy */ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport, resource_size_t component_reg_phys, - struct cxl_port *parent_port) + struct cxl_dport *parent_dport) { struct cxl_port *port; struct device *dev; int rc; - port = cxl_port_alloc(uport, component_reg_phys, parent_port); + port = cxl_port_alloc(uport, component_reg_phys, parent_dport); if (IS_ERR(port)) return port; dev = &port->dev; if (is_cxl_memdev(uport)) rc = dev_set_name(dev, "endpoint%d", port->id); - else if (parent_port) + else if (parent_dport) rc = dev_set_name(dev, "port%d", port->id); else rc = dev_set_name(dev, "root%d", port->id); @@ -556,17 +769,13 @@ static int match_root_child(struct device *dev, const void *match) return 0; port = to_cxl_port(dev); - device_lock(dev); - list_for_each_entry(dport, &port->dports, list) { - iter = match; - while (iter) { - if (iter == dport->dport) - goto out; - iter = iter->parent; - } + iter = match; + while (iter) { + dport = cxl_find_dport_by_dev(port, iter); + if (dport) + break; + iter = iter->parent; } -out: - device_unlock(dev); return !!iter; } @@ -590,9 +799,10 @@ EXPORT_SYMBOL_NS_GPL(find_cxl_root, CXL); static struct cxl_dport *find_dport(struct cxl_port *port, int id) { struct cxl_dport *dport; + unsigned long index; device_lock_assert(&port->dev); - list_for_each_entry (dport, &port->dports, list) + xa_for_each(&port->dports, index, dport) if (dport->port_id == id) return dport; return NULL; @@ -604,15 +814,15 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new) device_lock_assert(&port->dev); dup = find_dport(port, new->port_id); - if (dup) + if (dup) { dev_err(&port->dev, "unable to add dport%d-%s non-unique port id (%s)\n", new->port_id, dev_name(new->dport), dev_name(dup->dport)); - else - list_add_tail(&new->list, &port->dports); - - return dup ? -EEXIST : 0; + return -EBUSY; + } + return xa_insert(&port->dports, (unsigned long)new->dport, new, + GFP_KERNEL); } /* @@ -639,10 +849,8 @@ static void cxl_dport_remove(void *data) struct cxl_dport *dport = data; struct cxl_port *port = dport->port; + xa_erase(&port->dports, (unsigned long) dport->dport); put_device(dport->dport); - cond_cxl_root_lock(port); - list_del(&dport->list); - cond_cxl_root_unlock(port); } static void cxl_dport_unlink(void *data) @@ -694,7 +902,6 @@ struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port, if (!dport) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&dport->list); dport->dport = dport_dev; dport->port_id = port_id; dport->component_reg_phys = component_reg_phys; @@ -723,44 +930,33 @@ struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port, } EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport, CXL); -static struct cxl_ep *find_ep(struct cxl_port *port, struct device *ep_dev) -{ - struct cxl_ep *ep; - - device_lock_assert(&port->dev); - list_for_each_entry(ep, &port->endpoints, list) - if (ep->ep == ep_dev) - return ep; - return NULL; -} - -static int add_ep(struct cxl_port *port, struct cxl_ep *new) +static int add_ep(struct cxl_ep *new) { - struct cxl_ep *dup; + struct cxl_port *port = new->dport->port; + int rc; device_lock(&port->dev); if (port->dead) { device_unlock(&port->dev); return -ENXIO; } - dup = find_ep(port, new->ep); - if (!dup) - list_add_tail(&new->list, &port->endpoints); + rc = xa_insert(&port->endpoints, (unsigned long)new->ep, new, + GFP_KERNEL); device_unlock(&port->dev); - return dup ? -EEXIST : 0; + return rc; } /** * cxl_add_ep - register an endpoint's interest in a port - * @port: a port in the endpoint's topology ancestry + * @dport: the dport that routes to @ep_dev * @ep_dev: device representing the endpoint * * Intermediate CXL ports are scanned based on the arrival of endpoints. * When those endpoints depart the port can be destroyed once all * endpoints that care about that port have been removed. */ -static int cxl_add_ep(struct cxl_port *port, struct device *ep_dev) +static int cxl_add_ep(struct cxl_dport *dport, struct device *ep_dev) { struct cxl_ep *ep; int rc; @@ -769,10 +965,10 @@ static int cxl_add_ep(struct cxl_port *port, struct device *ep_dev) if (!ep) return -ENOMEM; - INIT_LIST_HEAD(&ep->list); ep->ep = get_device(ep_dev); + ep->dport = dport; - rc = add_ep(port, ep); + rc = add_ep(ep); if (rc) cxl_ep_release(ep); return rc; @@ -781,11 +977,13 @@ static int cxl_add_ep(struct cxl_port *port, struct device *ep_dev) struct cxl_find_port_ctx { const struct device *dport_dev; const struct cxl_port *parent_port; + struct cxl_dport **dport; }; static int match_port_by_dport(struct device *dev, const void *data) { const struct cxl_find_port_ctx *ctx = data; + struct cxl_dport *dport; struct cxl_port *port; if (!is_cxl_port(dev)) @@ -794,7 +992,10 @@ static int match_port_by_dport(struct device *dev, const void *data) return 0; port = to_cxl_port(dev); - return cxl_find_dport_by_dev(port, ctx->dport_dev) != NULL; + dport = cxl_find_dport_by_dev(port, ctx->dport_dev); + if (ctx->dport) + *ctx->dport = dport; + return dport != NULL; } static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx) @@ -810,24 +1011,32 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx) return NULL; } -static struct cxl_port *find_cxl_port(struct device *dport_dev) +static struct cxl_port *find_cxl_port(struct device *dport_dev, + struct cxl_dport **dport) { struct cxl_find_port_ctx ctx = { .dport_dev = dport_dev, + .dport = dport, }; + struct cxl_port *port; - return __find_cxl_port(&ctx); + port = __find_cxl_port(&ctx); + return port; } static struct cxl_port *find_cxl_port_at(struct cxl_port *parent_port, - struct device *dport_dev) + struct device *dport_dev, + struct cxl_dport **dport) { struct cxl_find_port_ctx ctx = { .dport_dev = dport_dev, .parent_port = parent_port, + .dport = dport, }; + struct cxl_port *port; - return __find_cxl_port(&ctx); + port = __find_cxl_port(&ctx); + return port; } /* @@ -851,13 +1060,13 @@ static void delete_endpoint(void *data) struct cxl_port *parent_port; struct device *parent; - parent_port = cxl_mem_find_port(cxlmd); + parent_port = cxl_mem_find_port(cxlmd, NULL); if (!parent_port) goto out; parent = &parent_port->dev; device_lock(parent); - if (parent->driver && endpoint->uport) { + if (parent->driver && !endpoint->dead) { devm_release_action(parent, cxl_unlink_uport, endpoint); devm_release_action(parent, unregister_port, endpoint); } @@ -883,21 +1092,70 @@ EXPORT_SYMBOL_NS_GPL(cxl_endpoint_autoremove, CXL); * for a port to be unregistered is when all memdevs beneath that port have gone * through ->remove(). This "bottom-up" removal selectively removes individual * child ports manually. This depends on devm_cxl_add_port() to not change is - * devm action registration order. + * devm action registration order, and for dports to have already been + * destroyed by reap_dports(). */ -static void delete_switch_port(struct cxl_port *port, struct list_head *dports) +static void delete_switch_port(struct cxl_port *port) +{ + devm_release_action(port->dev.parent, cxl_unlink_uport, port); + devm_release_action(port->dev.parent, unregister_port, port); +} + +static void reap_dports(struct cxl_port *port) { - struct cxl_dport *dport, *_d; + struct cxl_dport *dport; + unsigned long index; + + device_lock_assert(&port->dev); - list_for_each_entry_safe(dport, _d, dports, list) { + xa_for_each(&port->dports, index, dport) { devm_release_action(&port->dev, cxl_dport_unlink, dport); devm_release_action(&port->dev, cxl_dport_remove, dport); devm_kfree(&port->dev, dport); } - devm_release_action(port->dev.parent, cxl_unlink_uport, port); - devm_release_action(port->dev.parent, unregister_port, port); } +int devm_cxl_add_endpoint(struct cxl_memdev *cxlmd, + struct cxl_dport *parent_dport) +{ + struct cxl_port *parent_port = parent_dport->port; + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_port *endpoint, *iter, *down; + int rc; + + /* + * Now that the path to the root is established record all the + * intervening ports in the chain. + */ + for (iter = parent_port, down = NULL; !is_cxl_root(iter); + down = iter, iter = to_cxl_port(iter->dev.parent)) { + struct cxl_ep *ep; + + ep = cxl_ep_load(iter, cxlmd); + ep->next = down; + } + + endpoint = devm_cxl_add_port(&parent_port->dev, &cxlmd->dev, + cxlds->component_reg_phys, parent_dport); + if (IS_ERR(endpoint)) + return PTR_ERR(endpoint); + + dev_dbg(&cxlmd->dev, "add: %s\n", dev_name(&endpoint->dev)); + + rc = cxl_endpoint_autoremove(cxlmd, endpoint); + if (rc) + return rc; + + if (!endpoint->dev.driver) { + dev_err(&cxlmd->dev, "%s failed probe\n", + dev_name(&endpoint->dev)); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(devm_cxl_add_endpoint, CXL); + static void cxl_detach_ep(void *data) { struct cxl_memdev *cxlmd = data; @@ -906,13 +1164,13 @@ static void cxl_detach_ep(void *data) for (iter = &cxlmd->dev; iter; iter = grandparent(iter)) { struct device *dport_dev = grandparent(iter); struct cxl_port *port, *parent_port; - LIST_HEAD(reap_dports); struct cxl_ep *ep; + bool died = false; if (!dport_dev) break; - port = find_cxl_port(dport_dev); + port = find_cxl_port(dport_dev, NULL); if (!port) continue; @@ -936,26 +1194,27 @@ static void cxl_detach_ep(void *data) } device_lock(&port->dev); - ep = find_ep(port, &cxlmd->dev); + ep = cxl_ep_load(port, cxlmd); dev_dbg(&cxlmd->dev, "disconnect %s from %s\n", ep ? dev_name(ep->ep) : "", dev_name(&port->dev)); - cxl_ep_release(ep); - if (ep && !port->dead && list_empty(&port->endpoints) && + cxl_ep_remove(port, ep); + if (ep && !port->dead && xa_empty(&port->endpoints) && !is_cxl_root(parent_port)) { /* * This was the last ep attached to a dynamically * enumerated port. Block new cxl_add_ep() and garbage * collect the port. */ + died = true; port->dead = true; - list_splice_init(&port->dports, &reap_dports); + reap_dports(port); } device_unlock(&port->dev); - if (!list_empty(&reap_dports)) { + if (died) { dev_dbg(&cxlmd->dev, "delete %s\n", dev_name(&port->dev)); - delete_switch_port(port, &reap_dports); + delete_switch_port(port); } put_device(&port->dev); device_unlock(&parent_port->dev); @@ -986,6 +1245,7 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd, { struct device *dparent = grandparent(dport_dev); struct cxl_port *port, *parent_port = NULL; + struct cxl_dport *dport, *parent_dport; resource_size_t component_reg_phys; int rc; @@ -1000,7 +1260,7 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd, return -ENXIO; } - parent_port = find_cxl_port(dparent); + parent_port = find_cxl_port(dparent, &parent_dport); if (!parent_port) { /* iterate to create this parent_port */ return -EAGAIN; @@ -1015,13 +1275,14 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd, goto out; } - port = find_cxl_port_at(parent_port, dport_dev); + port = find_cxl_port_at(parent_port, dport_dev, &dport); if (!port) { component_reg_phys = find_component_registers(uport_dev); port = devm_cxl_add_port(&parent_port->dev, uport_dev, - component_reg_phys, parent_port); + component_reg_phys, parent_dport); + /* retry find to pick up the new dport information */ if (!IS_ERR(port)) - get_device(&port->dev); + port = find_cxl_port_at(parent_port, dport_dev, &dport); } out: device_unlock(&parent_port->dev); @@ -1031,8 +1292,8 @@ out: else { dev_dbg(&cxlmd->dev, "add to new port %s:%s\n", dev_name(&port->dev), dev_name(port->uport)); - rc = cxl_add_ep(port, &cxlmd->dev); - if (rc == -EEXIST) { + rc = cxl_add_ep(dport, &cxlmd->dev); + if (rc == -EBUSY) { /* * "can't" happen, but this error code means * something to the caller, so translate it. @@ -1065,6 +1326,7 @@ retry: for (iter = dev; iter; iter = grandparent(iter)) { struct device *dport_dev = grandparent(iter); struct device *uport_dev; + struct cxl_dport *dport; struct cxl_port *port; if (!dport_dev) @@ -1080,12 +1342,12 @@ retry: dev_dbg(dev, "scan: iter: %s dport_dev: %s parent: %s\n", dev_name(iter), dev_name(dport_dev), dev_name(uport_dev)); - port = find_cxl_port(dport_dev); + port = find_cxl_port(dport_dev, &dport); if (port) { dev_dbg(&cxlmd->dev, "found already registered port %s:%s\n", dev_name(&port->dev), dev_name(port->uport)); - rc = cxl_add_ep(port, &cxlmd->dev); + rc = cxl_add_ep(dport, &cxlmd->dev); /* * If the endpoint already exists in the port's list, @@ -1094,7 +1356,7 @@ retry: * the parent_port lock as the current port may be being * reaped. */ - if (rc && rc != -EEXIST) { + if (rc && rc != -EBUSY) { put_device(&port->dev); return rc; } @@ -1124,30 +1386,14 @@ retry: } EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, CXL); -struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd) +struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd, + struct cxl_dport **dport) { - return find_cxl_port(grandparent(&cxlmd->dev)); + return find_cxl_port(grandparent(&cxlmd->dev), dport); } EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, CXL); -struct cxl_dport *cxl_find_dport_by_dev(struct cxl_port *port, - const struct device *dev) -{ - struct cxl_dport *dport; - - device_lock(&port->dev); - list_for_each_entry(dport, &port->dports, list) - if (dport->dport == dev) { - device_unlock(&port->dev); - return dport; - } - - device_unlock(&port->dev); - return NULL; -} -EXPORT_SYMBOL_NS_GPL(cxl_find_dport_by_dev, CXL); - -static int decoder_populate_targets(struct cxl_decoder *cxld, +static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, struct cxl_port *port, int *target_map) { int i, rc = 0; @@ -1157,88 +1403,92 @@ static int decoder_populate_targets(struct cxl_decoder *cxld, device_lock_assert(&port->dev); - if (list_empty(&port->dports)) + if (xa_empty(&port->dports)) return -EINVAL; - write_seqlock(&cxld->target_lock); - for (i = 0; i < cxld->nr_targets; i++) { + write_seqlock(&cxlsd->target_lock); + for (i = 0; i < cxlsd->nr_targets; i++) { struct cxl_dport *dport = find_dport(port, target_map[i]); if (!dport) { rc = -ENXIO; break; } - cxld->target[i] = dport; + cxlsd->target[i] = dport; } - write_sequnlock(&cxld->target_lock); + write_sequnlock(&cxlsd->target_lock); return rc; } +static struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos) +{ + struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; + struct cxl_decoder *cxld = &cxlsd->cxld; + int iw; + + iw = cxld->interleave_ways; + if (dev_WARN_ONCE(&cxld->dev, iw != cxlsd->nr_targets, + "misconfigured root decoder\n")) + return NULL; + + return cxlrd->cxlsd.target[pos % iw]; +} + static struct lock_class_key cxl_decoder_key; /** - * cxl_decoder_alloc - Allocate a new CXL decoder + * cxl_decoder_init - Common decoder setup / initialization * @port: owning port of this decoder - * @nr_targets: downstream targets accessible by this decoder. All upstream - * ports and root ports must have at least 1 target. Endpoint - * devices will have 0 targets. Callers wishing to register an - * endpoint device should specify 0. - * - * A port should contain one or more decoders. Each of those decoders enable - * some address space for CXL.mem utilization. A decoder is expected to be - * configured by the caller before registering. + * @cxld: common decoder properties to initialize * - * Return: A new cxl decoder to be registered by cxl_decoder_add(). The decoder - * is initialized to be a "passthrough" decoder. + * A port may contain one or more decoders. Each of those decoders + * enable some address space for CXL.mem utilization. A decoder is + * expected to be configured by the caller before registering via + * cxl_decoder_add() */ -static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, - unsigned int nr_targets) +static int cxl_decoder_init(struct cxl_port *port, struct cxl_decoder *cxld) { - struct cxl_decoder *cxld; struct device *dev; - int rc = 0; - - if (nr_targets > CXL_DECODER_MAX_INTERLEAVE) - return ERR_PTR(-EINVAL); - - cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL); - if (!cxld) - return ERR_PTR(-ENOMEM); + int rc; rc = ida_alloc(&port->decoder_ida, GFP_KERNEL); if (rc < 0) - goto err; + return rc; /* need parent to stick around to release the id */ get_device(&port->dev); cxld->id = rc; - cxld->nr_targets = nr_targets; - seqlock_init(&cxld->target_lock); dev = &cxld->dev; device_initialize(dev); lockdep_set_class(&dev->mutex, &cxl_decoder_key); device_set_pm_not_required(dev); dev->parent = &port->dev; dev->bus = &cxl_bus_type; - if (is_cxl_root(port)) - cxld->dev.type = &cxl_decoder_root_type; - else if (is_cxl_endpoint(port)) - cxld->dev.type = &cxl_decoder_endpoint_type; - else - cxld->dev.type = &cxl_decoder_switch_type; /* Pre initialize an "empty" decoder */ cxld->interleave_ways = 1; cxld->interleave_granularity = PAGE_SIZE; cxld->target_type = CXL_DECODER_EXPANDER; - cxld->platform_res = (struct resource)DEFINE_RES_MEM(0, 0); + cxld->hpa_range = (struct range) { + .start = 0, + .end = -1, + }; - return cxld; -err: - kfree(cxld); - return ERR_PTR(rc); + return 0; +} + +static int cxl_switch_decoder_init(struct cxl_port *port, + struct cxl_switch_decoder *cxlsd, + int nr_targets) +{ + if (nr_targets > CXL_DECODER_MAX_INTERLEAVE) + return -EINVAL; + + cxlsd->nr_targets = nr_targets; + seqlock_init(&cxlsd->target_lock); + return cxl_decoder_init(port, &cxlsd->cxld); } /** @@ -1251,13 +1501,46 @@ err: * firmware description of CXL resources into a CXL standard decode * topology. */ -struct cxl_decoder *cxl_root_decoder_alloc(struct cxl_port *port, - unsigned int nr_targets) +struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, + unsigned int nr_targets) { + struct cxl_root_decoder *cxlrd; + struct cxl_switch_decoder *cxlsd; + struct cxl_decoder *cxld; + int rc; + if (!is_cxl_root(port)) return ERR_PTR(-EINVAL); - return cxl_decoder_alloc(port, nr_targets); + cxlrd = kzalloc(struct_size(cxlrd, cxlsd.target, nr_targets), + GFP_KERNEL); + if (!cxlrd) + return ERR_PTR(-ENOMEM); + + cxlsd = &cxlrd->cxlsd; + rc = cxl_switch_decoder_init(port, cxlsd, nr_targets); + if (rc) { + kfree(cxlrd); + return ERR_PTR(rc); + } + + cxlrd->calc_hb = cxl_hb_modulo; + + cxld = &cxlsd->cxld; + cxld->dev.type = &cxl_decoder_root_type; + /* + * cxl_root_decoder_release() special cases negative ids to + * detect memregion_alloc() failures. + */ + atomic_set(&cxlrd->region_id, -1); + rc = memregion_alloc(GFP_KERNEL); + if (rc < 0) { + put_device(&cxld->dev); + return ERR_PTR(rc); + } + + atomic_set(&cxlrd->region_id, rc); + return cxlrd; } EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL); @@ -1272,13 +1555,29 @@ EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL); * that sit between Switch Upstream Ports / Switch Downstream Ports and * Host Bridges / Root Ports. */ -struct cxl_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, - unsigned int nr_targets) +struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, + unsigned int nr_targets) { + struct cxl_switch_decoder *cxlsd; + struct cxl_decoder *cxld; + int rc; + if (is_cxl_root(port) || is_cxl_endpoint(port)) return ERR_PTR(-EINVAL); - return cxl_decoder_alloc(port, nr_targets); + cxlsd = kzalloc(struct_size(cxlsd, target, nr_targets), GFP_KERNEL); + if (!cxlsd) + return ERR_PTR(-ENOMEM); + + rc = cxl_switch_decoder_init(port, cxlsd, nr_targets); + if (rc) { + kfree(cxlsd); + return ERR_PTR(rc); + } + + cxld = &cxlsd->cxld; + cxld->dev.type = &cxl_decoder_switch_type; + return cxlsd; } EXPORT_SYMBOL_NS_GPL(cxl_switch_decoder_alloc, CXL); @@ -1288,18 +1587,35 @@ EXPORT_SYMBOL_NS_GPL(cxl_switch_decoder_alloc, CXL); * * Return: A new cxl decoder to be registered by cxl_decoder_add() */ -struct cxl_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port) +struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port) { + struct cxl_endpoint_decoder *cxled; + struct cxl_decoder *cxld; + int rc; + if (!is_cxl_endpoint(port)) return ERR_PTR(-EINVAL); - return cxl_decoder_alloc(port, 0); + cxled = kzalloc(sizeof(*cxled), GFP_KERNEL); + if (!cxled) + return ERR_PTR(-ENOMEM); + + cxled->pos = -1; + cxld = &cxled->cxld; + rc = cxl_decoder_init(port, cxld); + if (rc) { + kfree(cxled); + return ERR_PTR(rc); + } + + cxld->dev.type = &cxl_decoder_endpoint_type; + return cxled; } EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_alloc, CXL); /** * cxl_decoder_add_locked - Add a decoder with targets - * @cxld: The cxl decoder allocated by cxl_decoder_alloc() + * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc() * @target_map: A list of downstream ports that this decoder can direct memory * traffic to. These numbers should correspond with the port number * in the PCIe Link Capabilities structure. @@ -1335,7 +1651,9 @@ int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map) port = to_cxl_port(cxld->dev.parent); if (!is_endpoint_decoder(dev)) { - rc = decoder_populate_targets(cxld, port, target_map); + struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); + + rc = decoder_populate_targets(cxlsd, port, target_map); if (rc && (cxld->flags & CXL_DECODER_F_ENABLE)) { dev_err(&port->dev, "Failed to populate active decoder targets\n"); @@ -1347,20 +1665,13 @@ int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map) if (rc) return rc; - /* - * Platform decoder resources should show up with a reasonable name. All - * other resources are just sub ranges within the main decoder resource. - */ - if (is_root_decoder(dev)) - cxld->platform_res.name = dev_name(dev); - return device_add(dev); } EXPORT_SYMBOL_NS_GPL(cxl_decoder_add_locked, CXL); /** * cxl_decoder_add - Add a decoder with targets - * @cxld: The cxl decoder allocated by cxl_decoder_alloc() + * @cxld: The cxl decoder allocated by cxl_<type>_decoder_alloc() * @target_map: A list of downstream ports that this decoder can direct memory * traffic to. These numbers should correspond with the port number * in the PCIe Link Capabilities structure. @@ -1394,6 +1705,13 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, CXL); static void cxld_unregister(void *dev) { + struct cxl_endpoint_decoder *cxled; + + if (is_endpoint_decoder(dev)) { + cxled = to_cxl_endpoint_decoder(dev); + cxl_decoder_kill_region(cxled); + } + device_unregister(dev); } @@ -1521,10 +1839,20 @@ struct bus_type cxl_bus_type = { }; EXPORT_SYMBOL_NS_GPL(cxl_bus_type, CXL); +static struct dentry *cxl_debugfs; + +struct dentry *cxl_debugfs_create_dir(const char *dir) +{ + return debugfs_create_dir(dir, cxl_debugfs); +} +EXPORT_SYMBOL_NS_GPL(cxl_debugfs_create_dir, CXL); + static __init int cxl_core_init(void) { int rc; + cxl_debugfs = debugfs_create_dir("cxl", NULL); + cxl_mbox_init(); rc = cxl_memdev_init(); @@ -1541,22 +1869,28 @@ static __init int cxl_core_init(void) if (rc) goto err_bus; + rc = cxl_region_init(); + if (rc) + goto err_region; + return 0; +err_region: + bus_unregister(&cxl_bus_type); err_bus: destroy_workqueue(cxl_bus_wq); err_wq: cxl_memdev_exit(); - cxl_mbox_exit(); return rc; } static void cxl_core_exit(void) { + cxl_region_exit(); bus_unregister(&cxl_bus_type); destroy_workqueue(cxl_bus_wq); cxl_memdev_exit(); - cxl_mbox_exit(); + debugfs_remove_recursive(cxl_debugfs); } module_init(cxl_core_init); diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c new file mode 100644 index 000000000000..401148016978 --- /dev/null +++ b/drivers/cxl/core/region.c @@ -0,0 +1,1896 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include <linux/memregion.h> +#include <linux/genalloc.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uuid.h> +#include <linux/idr.h> +#include <cxlmem.h> +#include <cxl.h> +#include "core.h" + +/** + * DOC: cxl core region + * + * CXL Regions represent mapped memory capacity in system physical address + * space. Whereas the CXL Root Decoders identify the bounds of potential CXL + * Memory ranges, Regions represent the active mapped capacity by the HDM + * Decoder Capability structures throughout the Host Bridges, Switches, and + * Endpoints in the topology. + * + * Region configuration has ordering constraints. UUID may be set at any time + * but is only visible for persistent regions. + * 1. Interleave granularity + * 2. Interleave size + * 3. Decoder targets + */ + +/* + * All changes to the interleave configuration occur with this lock held + * for write. + */ +static DECLARE_RWSEM(cxl_region_rwsem); + +static struct cxl_region *to_cxl_region(struct device *dev); + +static ssize_t uuid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + rc = sysfs_emit(buf, "%pUb\n", &p->uuid); + up_read(&cxl_region_rwsem); + + return rc; +} + +static int is_dup(struct device *match, void *data) +{ + struct cxl_region_params *p; + struct cxl_region *cxlr; + uuid_t *uuid = data; + + if (!is_cxl_region(match)) + return 0; + + lockdep_assert_held(&cxl_region_rwsem); + cxlr = to_cxl_region(match); + p = &cxlr->params; + + if (uuid_equal(&p->uuid, uuid)) { + dev_dbg(match, "already has uuid: %pUb\n", uuid); + return -EBUSY; + } + + return 0; +} + +static ssize_t uuid_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + uuid_t temp; + ssize_t rc; + + if (len != UUID_STRING_LEN + 1) + return -EINVAL; + + rc = uuid_parse(buf, &temp); + if (rc) + return rc; + + if (uuid_is_null(&temp)) + return -EINVAL; + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + + if (uuid_equal(&p->uuid, &temp)) + goto out; + + rc = -EBUSY; + if (p->state >= CXL_CONFIG_ACTIVE) + goto out; + + rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup); + if (rc < 0) + goto out; + + uuid_copy(&p->uuid, &temp); +out: + up_write(&cxl_region_rwsem); + + if (rc) + return rc; + return len; +} +static DEVICE_ATTR_RW(uuid); + +static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port, + struct cxl_region *cxlr) +{ + return xa_load(&port->regions, (unsigned long)cxlr); +} + +static int cxl_region_decode_reset(struct cxl_region *cxlr, int count) +{ + struct cxl_region_params *p = &cxlr->params; + int i; + + for (i = count - 1; i >= 0; i--) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_port *iter = cxled_to_port(cxled); + struct cxl_ep *ep; + int rc; + + while (!is_cxl_root(to_cxl_port(iter->dev.parent))) + iter = to_cxl_port(iter->dev.parent); + + for (ep = cxl_ep_load(iter, cxlmd); iter; + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { + struct cxl_region_ref *cxl_rr; + struct cxl_decoder *cxld; + + cxl_rr = cxl_rr_load(iter, cxlr); + cxld = cxl_rr->decoder; + rc = cxld->reset(cxld); + if (rc) + return rc; + } + + rc = cxled->cxld.reset(&cxled->cxld); + if (rc) + return rc; + } + + return 0; +} + +static int cxl_region_decode_commit(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + int i, rc = 0; + + for (i = 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_region_ref *cxl_rr; + struct cxl_decoder *cxld; + struct cxl_port *iter; + struct cxl_ep *ep; + + /* commit bottom up */ + for (iter = cxled_to_port(cxled); !is_cxl_root(iter); + iter = to_cxl_port(iter->dev.parent)) { + cxl_rr = cxl_rr_load(iter, cxlr); + cxld = cxl_rr->decoder; + rc = cxld->commit(cxld); + if (rc) + break; + } + + if (rc) { + /* programming @iter failed, teardown */ + for (ep = cxl_ep_load(iter, cxlmd); ep && iter; + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { + cxl_rr = cxl_rr_load(iter, cxlr); + cxld = cxl_rr->decoder; + cxld->reset(cxld); + } + + cxled->cxld.reset(&cxled->cxld); + goto err; + } + } + + return 0; + +err: + /* undo the targets that were successfully committed */ + cxl_region_decode_reset(cxlr, i); + return rc; +} + +static ssize_t commit_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + bool commit; + ssize_t rc; + + rc = kstrtobool(buf, &commit); + if (rc) + return rc; + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + + /* Already in the requested state? */ + if (commit && p->state >= CXL_CONFIG_COMMIT) + goto out; + if (!commit && p->state < CXL_CONFIG_COMMIT) + goto out; + + /* Not ready to commit? */ + if (commit && p->state < CXL_CONFIG_ACTIVE) { + rc = -ENXIO; + goto out; + } + + if (commit) + rc = cxl_region_decode_commit(cxlr); + else { + p->state = CXL_CONFIG_RESET_PENDING; + up_write(&cxl_region_rwsem); + device_release_driver(&cxlr->dev); + down_write(&cxl_region_rwsem); + + /* + * The lock was dropped, so need to revalidate that the reset is + * still pending. + */ + if (p->state == CXL_CONFIG_RESET_PENDING) + rc = cxl_region_decode_reset(cxlr, p->interleave_ways); + } + + if (rc) + goto out; + + if (commit) + p->state = CXL_CONFIG_COMMIT; + else if (p->state == CXL_CONFIG_RESET_PENDING) + p->state = CXL_CONFIG_ACTIVE; + +out: + up_write(&cxl_region_rwsem); + + if (rc) + return rc; + return len; +} + +static ssize_t commit_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT); + up_read(&cxl_region_rwsem); + + return rc; +} +static DEVICE_ATTR_RW(commit); + +static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a, + int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_region *cxlr = to_cxl_region(dev); + + if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_DECODER_PMEM) + return 0; + return a->mode; +} + +static ssize_t interleave_ways_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + rc = sysfs_emit(buf, "%d\n", p->interleave_ways); + up_read(&cxl_region_rwsem); + + return rc; +} + +static const struct attribute_group *get_cxl_region_target_group(void); + +static ssize_t interleave_ways_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + unsigned int val, save; + int rc; + u8 iw; + + rc = kstrtouint(buf, 0, &val); + if (rc) + return rc; + + rc = ways_to_cxl(val, &iw); + if (rc) + return rc; + + /* + * Even for x3, x9, and x12 interleaves the region interleave must be a + * power of 2 multiple of the host bridge interleave. + */ + if (!is_power_of_2(val / cxld->interleave_ways) || + (val % cxld->interleave_ways)) { + dev_dbg(&cxlr->dev, "invalid interleave: %d\n", val); + return -EINVAL; + } + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { + rc = -EBUSY; + goto out; + } + + save = p->interleave_ways; + p->interleave_ways = val; + rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group()); + if (rc) + p->interleave_ways = save; +out: + up_write(&cxl_region_rwsem); + if (rc) + return rc; + return len; +} +static DEVICE_ATTR_RW(interleave_ways); + +static ssize_t interleave_granularity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + rc = sysfs_emit(buf, "%d\n", p->interleave_granularity); + up_read(&cxl_region_rwsem); + + return rc; +} + +static ssize_t interleave_granularity_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev->parent); + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + int rc, val; + u16 ig; + + rc = kstrtoint(buf, 0, &val); + if (rc) + return rc; + + rc = granularity_to_cxl(val, &ig); + if (rc) + return rc; + + /* + * When the host-bridge is interleaved, disallow region granularity != + * root granularity. Regions with a granularity less than the root + * interleave result in needing multiple endpoints to support a single + * slot in the interleave (possible to suport in the future). Regions + * with a granularity greater than the root interleave result in invalid + * DPA translations (invalid to support). + */ + if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) + return -EINVAL; + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) { + rc = -EBUSY; + goto out; + } + + p->interleave_granularity = val; +out: + up_write(&cxl_region_rwsem); + if (rc) + return rc; + return len; +} +static DEVICE_ATTR_RW(interleave_granularity); + +static ssize_t resource_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + u64 resource = -1ULL; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + if (p->res) + resource = p->res->start; + rc = sysfs_emit(buf, "%#llx\n", resource); + up_read(&cxl_region_rwsem); + + return rc; +} +static DEVICE_ATTR_RO(resource); + +static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + struct cxl_region_params *p = &cxlr->params; + struct resource *res; + u32 remainder = 0; + + lockdep_assert_held_write(&cxl_region_rwsem); + + /* Nothing to do... */ + if (p->res && resource_size(p->res) == size) + return 0; + + /* To change size the old size must be freed first */ + if (p->res) + return -EBUSY; + + if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) + return -EBUSY; + + /* ways, granularity and uuid (if PMEM) need to be set before HPA */ + if (!p->interleave_ways || !p->interleave_granularity || + (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid))) + return -ENXIO; + + div_u64_rem(size, SZ_256M * p->interleave_ways, &remainder); + if (remainder) + return -EINVAL; + + res = alloc_free_mem_region(cxlrd->res, size, SZ_256M, + dev_name(&cxlr->dev)); + if (IS_ERR(res)) { + dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n", + PTR_ERR(res)); + return PTR_ERR(res); + } + + p->res = res; + p->state = CXL_CONFIG_INTERLEAVE_ACTIVE; + + return 0; +} + +static void cxl_region_iomem_release(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + + if (device_is_registered(&cxlr->dev)) + lockdep_assert_held_write(&cxl_region_rwsem); + if (p->res) { + remove_resource(p->res); + kfree(p->res); + p->res = NULL; + } +} + +static int free_hpa(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + + lockdep_assert_held_write(&cxl_region_rwsem); + + if (!p->res) + return 0; + + if (p->state >= CXL_CONFIG_ACTIVE) + return -EBUSY; + + cxl_region_iomem_release(cxlr); + p->state = CXL_CONFIG_IDLE; + return 0; +} + +static ssize_t size_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + u64 val; + int rc; + + rc = kstrtou64(buf, 0, &val); + if (rc) + return rc; + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + + if (val) + rc = alloc_hpa(cxlr, val); + else + rc = free_hpa(cxlr); + up_write(&cxl_region_rwsem); + + if (rc) + return rc; + + return len; +} + +static ssize_t size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + u64 size = 0; + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + if (p->res) + size = resource_size(p->res); + rc = sysfs_emit(buf, "%#llx\n", size); + up_read(&cxl_region_rwsem); + + return rc; +} +static DEVICE_ATTR_RW(size); + +static struct attribute *cxl_region_attrs[] = { + &dev_attr_uuid.attr, + &dev_attr_commit.attr, + &dev_attr_interleave_ways.attr, + &dev_attr_interleave_granularity.attr, + &dev_attr_resource.attr, + &dev_attr_size.attr, + NULL, +}; + +static const struct attribute_group cxl_region_group = { + .attrs = cxl_region_attrs, + .is_visible = cxl_region_visible, +}; + +static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_endpoint_decoder *cxled; + int rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + + if (pos >= p->interleave_ways) { + dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, + p->interleave_ways); + rc = -ENXIO; + goto out; + } + + cxled = p->targets[pos]; + if (!cxled) + rc = sysfs_emit(buf, "\n"); + else + rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev)); +out: + up_read(&cxl_region_rwsem); + + return rc; +} + +static int match_free_decoder(struct device *dev, void *data) +{ + struct cxl_decoder *cxld; + int *id = data; + + if (!is_switch_decoder(dev)) + return 0; + + cxld = to_cxl_decoder(dev); + + /* enforce ordered allocation */ + if (cxld->id != *id) + return 0; + + if (!cxld->region) + return 1; + + (*id)++; + + return 0; +} + +static struct cxl_decoder *cxl_region_find_decoder(struct cxl_port *port, + struct cxl_region *cxlr) +{ + struct device *dev; + int id = 0; + + dev = device_find_child(&port->dev, &id, match_free_decoder); + if (!dev) + return NULL; + /* + * This decoder is pinned registered as long as the endpoint decoder is + * registered, and endpoint decoder unregistration holds the + * cxl_region_rwsem over unregister events, so no need to hold on to + * this extra reference. + */ + put_device(dev); + return to_cxl_decoder(dev); +} + +static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port, + struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_region_ref *cxl_rr, *iter; + unsigned long index; + int rc; + + xa_for_each(&port->regions, index, iter) { + struct cxl_region_params *ip = &iter->region->params; + + if (ip->res->start > p->res->start) { + dev_dbg(&cxlr->dev, + "%s: HPA order violation %s:%pr vs %pr\n", + dev_name(&port->dev), + dev_name(&iter->region->dev), ip->res, p->res); + return ERR_PTR(-EBUSY); + } + } + + cxl_rr = kzalloc(sizeof(*cxl_rr), GFP_KERNEL); + if (!cxl_rr) + return ERR_PTR(-ENOMEM); + cxl_rr->port = port; + cxl_rr->region = cxlr; + cxl_rr->nr_targets = 1; + xa_init(&cxl_rr->endpoints); + + rc = xa_insert(&port->regions, (unsigned long)cxlr, cxl_rr, GFP_KERNEL); + if (rc) { + dev_dbg(&cxlr->dev, + "%s: failed to track region reference: %d\n", + dev_name(&port->dev), rc); + kfree(cxl_rr); + return ERR_PTR(rc); + } + + return cxl_rr; +} + +static void free_region_ref(struct cxl_region_ref *cxl_rr) +{ + struct cxl_port *port = cxl_rr->port; + struct cxl_region *cxlr = cxl_rr->region; + struct cxl_decoder *cxld = cxl_rr->decoder; + + dev_WARN_ONCE(&cxlr->dev, cxld->region != cxlr, "region mismatch\n"); + if (cxld->region == cxlr) { + cxld->region = NULL; + put_device(&cxlr->dev); + } + + xa_erase(&port->regions, (unsigned long)cxlr); + xa_destroy(&cxl_rr->endpoints); + kfree(cxl_rr); +} + +static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr, + struct cxl_endpoint_decoder *cxled) +{ + int rc; + struct cxl_port *port = cxl_rr->port; + struct cxl_region *cxlr = cxl_rr->region; + struct cxl_decoder *cxld = cxl_rr->decoder; + struct cxl_ep *ep = cxl_ep_load(port, cxled_to_memdev(cxled)); + + if (ep) { + rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep, + GFP_KERNEL); + if (rc) + return rc; + } + cxl_rr->nr_eps++; + + if (!cxld->region) { + cxld->region = cxlr; + get_device(&cxlr->dev); + } + + return 0; +} + +/** + * cxl_port_attach_region() - track a region's interest in a port by endpoint + * @port: port to add a new region reference 'struct cxl_region_ref' + * @cxlr: region to attach to @port + * @cxled: endpoint decoder used to create or further pin a region reference + * @pos: interleave position of @cxled in @cxlr + * + * The attach event is an opportunity to validate CXL decode setup + * constraints and record metadata needed for programming HDM decoders, + * in particular decoder target lists. + * + * The steps are: + * + * - validate that there are no other regions with a higher HPA already + * associated with @port + * - establish a region reference if one is not already present + * + * - additionally allocate a decoder instance that will host @cxlr on + * @port + * + * - pin the region reference by the endpoint + * - account for how many entries in @port's target list are needed to + * cover all of the added endpoints. + */ +static int cxl_port_attach_region(struct cxl_port *port, + struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_ep *ep = cxl_ep_load(port, cxlmd); + struct cxl_region_ref *cxl_rr; + bool nr_targets_inc = false; + struct cxl_decoder *cxld; + unsigned long index; + int rc = -EBUSY; + + lockdep_assert_held_write(&cxl_region_rwsem); + + cxl_rr = cxl_rr_load(port, cxlr); + if (cxl_rr) { + struct cxl_ep *ep_iter; + int found = 0; + + /* + * Walk the existing endpoints that have been attached to + * @cxlr at @port and see if they share the same 'next' port + * in the downstream direction. I.e. endpoints that share common + * upstream switch. + */ + xa_for_each(&cxl_rr->endpoints, index, ep_iter) { + if (ep_iter == ep) + continue; + if (ep_iter->next == ep->next) { + found++; + break; + } + } + + /* + * New target port, or @port is an endpoint port that always + * accounts its own local decode as a target. + */ + if (!found || !ep->next) { + cxl_rr->nr_targets++; + nr_targets_inc = true; + } + + /* + * The decoder for @cxlr was allocated when the region was first + * attached to @port. + */ + cxld = cxl_rr->decoder; + } else { + cxl_rr = alloc_region_ref(port, cxlr); + if (IS_ERR(cxl_rr)) { + dev_dbg(&cxlr->dev, + "%s: failed to allocate region reference\n", + dev_name(&port->dev)); + return PTR_ERR(cxl_rr); + } + nr_targets_inc = true; + + if (port == cxled_to_port(cxled)) + cxld = &cxled->cxld; + else + cxld = cxl_region_find_decoder(port, cxlr); + if (!cxld) { + dev_dbg(&cxlr->dev, "%s: no decoder available\n", + dev_name(&port->dev)); + goto out_erase; + } + + if (cxld->region) { + dev_dbg(&cxlr->dev, "%s: %s already attached to %s\n", + dev_name(&port->dev), dev_name(&cxld->dev), + dev_name(&cxld->region->dev)); + rc = -EBUSY; + goto out_erase; + } + + cxl_rr->decoder = cxld; + } + + rc = cxl_rr_ep_add(cxl_rr, cxled); + if (rc) { + dev_dbg(&cxlr->dev, + "%s: failed to track endpoint %s:%s reference\n", + dev_name(&port->dev), dev_name(&cxlmd->dev), + dev_name(&cxld->dev)); + goto out_erase; + } + + dev_dbg(&cxlr->dev, + "%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n", + dev_name(port->uport), dev_name(&port->dev), + dev_name(&cxld->dev), dev_name(&cxlmd->dev), + dev_name(&cxled->cxld.dev), pos, + ep ? ep->next ? dev_name(ep->next->uport) : + dev_name(&cxlmd->dev) : + "none", + cxl_rr->nr_eps, cxl_rr->nr_targets); + + return 0; +out_erase: + if (nr_targets_inc) + cxl_rr->nr_targets--; + if (cxl_rr->nr_eps == 0) + free_region_ref(cxl_rr); + return rc; +} + +static void cxl_port_detach_region(struct cxl_port *port, + struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled) +{ + struct cxl_region_ref *cxl_rr; + struct cxl_ep *ep = NULL; + + lockdep_assert_held_write(&cxl_region_rwsem); + + cxl_rr = cxl_rr_load(port, cxlr); + if (!cxl_rr) + return; + + /* + * Endpoint ports do not carry cxl_ep references, and they + * never target more than one endpoint by definition + */ + if (cxl_rr->decoder == &cxled->cxld) + cxl_rr->nr_eps--; + else + ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled); + if (ep) { + struct cxl_ep *ep_iter; + unsigned long index; + int found = 0; + + cxl_rr->nr_eps--; + xa_for_each(&cxl_rr->endpoints, index, ep_iter) { + if (ep_iter->next == ep->next) { + found++; + break; + } + } + if (!found) + cxl_rr->nr_targets--; + } + + if (cxl_rr->nr_eps == 0) + free_region_ref(cxl_rr); +} + +static int check_last_peer(struct cxl_endpoint_decoder *cxled, + struct cxl_ep *ep, struct cxl_region_ref *cxl_rr, + int distance) +{ + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_region *cxlr = cxl_rr->region; + struct cxl_region_params *p = &cxlr->params; + struct cxl_endpoint_decoder *cxled_peer; + struct cxl_port *port = cxl_rr->port; + struct cxl_memdev *cxlmd_peer; + struct cxl_ep *ep_peer; + int pos = cxled->pos; + + /* + * If this position wants to share a dport with the last endpoint mapped + * then that endpoint, at index 'position - distance', must also be + * mapped by this dport. + */ + if (pos < distance) { + dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n", + dev_name(port->uport), dev_name(&port->dev), + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); + return -ENXIO; + } + cxled_peer = p->targets[pos - distance]; + cxlmd_peer = cxled_to_memdev(cxled_peer); + ep_peer = cxl_ep_load(port, cxlmd_peer); + if (ep->dport != ep_peer->dport) { + dev_dbg(&cxlr->dev, + "%s:%s: %s:%s pos %d mismatched peer %s:%s\n", + dev_name(port->uport), dev_name(&port->dev), + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos, + dev_name(&cxlmd_peer->dev), + dev_name(&cxled_peer->cxld.dev)); + return -ENXIO; + } + + return 0; +} + +static int cxl_port_setup_targets(struct cxl_port *port, + struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos; + struct cxl_port *parent_port = to_cxl_port(port->dev.parent); + struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_ep *ep = cxl_ep_load(port, cxlmd); + struct cxl_region_params *p = &cxlr->params; + struct cxl_decoder *cxld = cxl_rr->decoder; + struct cxl_switch_decoder *cxlsd; + u16 eig, peig; + u8 eiw, peiw; + + /* + * While root level decoders support x3, x6, x12, switch level + * decoders only support powers of 2 up to x16. + */ + if (!is_power_of_2(cxl_rr->nr_targets)) { + dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n", + dev_name(port->uport), dev_name(&port->dev), + cxl_rr->nr_targets); + return -EINVAL; + } + + cxlsd = to_cxl_switch_decoder(&cxld->dev); + if (cxl_rr->nr_targets_set) { + int i, distance; + + distance = p->nr_targets / cxl_rr->nr_targets; + for (i = 0; i < cxl_rr->nr_targets_set; i++) + if (ep->dport == cxlsd->target[i]) { + rc = check_last_peer(cxled, ep, cxl_rr, + distance); + if (rc) + return rc; + goto out_target_set; + } + goto add_target; + } + + if (is_cxl_root(parent_port)) { + parent_ig = cxlrd->cxlsd.cxld.interleave_granularity; + parent_iw = cxlrd->cxlsd.cxld.interleave_ways; + /* + * For purposes of address bit routing, use power-of-2 math for + * switch ports. + */ + if (!is_power_of_2(parent_iw)) + parent_iw /= 3; + } else { + struct cxl_region_ref *parent_rr; + struct cxl_decoder *parent_cxld; + + parent_rr = cxl_rr_load(parent_port, cxlr); + parent_cxld = parent_rr->decoder; + parent_ig = parent_cxld->interleave_granularity; + parent_iw = parent_cxld->interleave_ways; + } + + rc = granularity_to_cxl(parent_ig, &peig); + if (rc) { + dev_dbg(&cxlr->dev, "%s:%s: invalid parent granularity: %d\n", + dev_name(parent_port->uport), + dev_name(&parent_port->dev), parent_ig); + return rc; + } + + rc = ways_to_cxl(parent_iw, &peiw); + if (rc) { + dev_dbg(&cxlr->dev, "%s:%s: invalid parent interleave: %d\n", + dev_name(parent_port->uport), + dev_name(&parent_port->dev), parent_iw); + return rc; + } + + iw = cxl_rr->nr_targets; + rc = ways_to_cxl(iw, &eiw); + if (rc) { + dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n", + dev_name(port->uport), dev_name(&port->dev), iw); + return rc; + } + + /* + * If @parent_port is masking address bits, pick the next unused address + * bit to route @port's targets. + */ + if (parent_iw > 1 && cxl_rr->nr_targets > 1) { + u32 address_bit = max(peig + peiw, eiw + peig); + + eig = address_bit - eiw + 1; + } else { + eiw = peiw; + eig = peig; + } + + rc = cxl_to_granularity(eig, &ig); + if (rc) { + dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n", + dev_name(port->uport), dev_name(&port->dev), + 256 << eig); + return rc; + } + + cxld->interleave_ways = iw; + cxld->interleave_granularity = ig; + cxld->hpa_range = (struct range) { + .start = p->res->start, + .end = p->res->end, + }; + dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport), + dev_name(&port->dev), iw, ig); +add_target: + if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) { + dev_dbg(&cxlr->dev, + "%s:%s: targets full trying to add %s:%s at %d\n", + dev_name(port->uport), dev_name(&port->dev), + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); + return -ENXIO; + } + cxlsd->target[cxl_rr->nr_targets_set] = ep->dport; + inc = 1; +out_target_set: + cxl_rr->nr_targets_set += inc; + dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n", + dev_name(port->uport), dev_name(&port->dev), + cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport), + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); + + return 0; +} + +static void cxl_port_reset_targets(struct cxl_port *port, + struct cxl_region *cxlr) +{ + struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); + struct cxl_decoder *cxld; + + /* + * After the last endpoint has been detached the entire cxl_rr may now + * be gone. + */ + if (!cxl_rr) + return; + cxl_rr->nr_targets_set = 0; + + cxld = cxl_rr->decoder; + cxld->hpa_range = (struct range) { + .start = 0, + .end = -1, + }; +} + +static void cxl_region_teardown_targets(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_endpoint_decoder *cxled; + struct cxl_memdev *cxlmd; + struct cxl_port *iter; + struct cxl_ep *ep; + int i; + + for (i = 0; i < p->nr_targets; i++) { + cxled = p->targets[i]; + cxlmd = cxled_to_memdev(cxled); + + iter = cxled_to_port(cxled); + while (!is_cxl_root(to_cxl_port(iter->dev.parent))) + iter = to_cxl_port(iter->dev.parent); + + for (ep = cxl_ep_load(iter, cxlmd); iter; + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) + cxl_port_reset_targets(iter, cxlr); + } +} + +static int cxl_region_setup_targets(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_endpoint_decoder *cxled; + struct cxl_memdev *cxlmd; + struct cxl_port *iter; + struct cxl_ep *ep; + int i, rc; + + for (i = 0; i < p->nr_targets; i++) { + cxled = p->targets[i]; + cxlmd = cxled_to_memdev(cxled); + + iter = cxled_to_port(cxled); + while (!is_cxl_root(to_cxl_port(iter->dev.parent))) + iter = to_cxl_port(iter->dev.parent); + + /* + * Descend the topology tree programming targets while + * looking for conflicts. + */ + for (ep = cxl_ep_load(iter, cxlmd); iter; + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { + rc = cxl_port_setup_targets(iter, cxlr, cxled); + if (rc) { + cxl_region_teardown_targets(cxlr); + return rc; + } + } + } + + return 0; +} + +static int cxl_region_attach(struct cxl_region *cxlr, + struct cxl_endpoint_decoder *cxled, int pos) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_port *ep_port, *root_port, *iter; + struct cxl_region_params *p = &cxlr->params; + struct cxl_dport *dport; + int i, rc = -ENXIO; + + if (cxled->mode == CXL_DECODER_DEAD) { + dev_dbg(&cxlr->dev, "%s dead\n", dev_name(&cxled->cxld.dev)); + return -ENODEV; + } + + /* all full of members, or interleave config not established? */ + if (p->state > CXL_CONFIG_INTERLEAVE_ACTIVE) { + dev_dbg(&cxlr->dev, "region already active\n"); + return -EBUSY; + } else if (p->state < CXL_CONFIG_INTERLEAVE_ACTIVE) { + dev_dbg(&cxlr->dev, "interleave config missing\n"); + return -ENXIO; + } + + if (pos < 0 || pos >= p->interleave_ways) { + dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, + p->interleave_ways); + return -ENXIO; + } + + if (p->targets[pos] == cxled) + return 0; + + if (p->targets[pos]) { + struct cxl_endpoint_decoder *cxled_target = p->targets[pos]; + struct cxl_memdev *cxlmd_target = cxled_to_memdev(cxled_target); + + dev_dbg(&cxlr->dev, "position %d already assigned to %s:%s\n", + pos, dev_name(&cxlmd_target->dev), + dev_name(&cxled_target->cxld.dev)); + return -EBUSY; + } + + for (i = 0; i < p->interleave_ways; i++) { + struct cxl_endpoint_decoder *cxled_target; + struct cxl_memdev *cxlmd_target; + + cxled_target = p->targets[pos]; + if (!cxled_target) + continue; + + cxlmd_target = cxled_to_memdev(cxled_target); + if (cxlmd_target == cxlmd) { + dev_dbg(&cxlr->dev, + "%s already specified at position %d via: %s\n", + dev_name(&cxlmd->dev), pos, + dev_name(&cxled_target->cxld.dev)); + return -EBUSY; + } + } + + ep_port = cxled_to_port(cxled); + root_port = cxlrd_to_port(cxlrd); + dport = cxl_find_dport_by_dev(root_port, ep_port->host_bridge); + if (!dport) { + dev_dbg(&cxlr->dev, "%s:%s invalid target for %s\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), + dev_name(cxlr->dev.parent)); + return -ENXIO; + } + + if (cxlrd->calc_hb(cxlrd, pos) != dport) { + dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), + dev_name(&cxlrd->cxlsd.cxld.dev)); + return -ENXIO; + } + + if (cxled->cxld.target_type != cxlr->type) { + dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), + cxled->cxld.target_type, cxlr->type); + return -ENXIO; + } + + if (!cxled->dpa_res) { + dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev)); + return -ENXIO; + } + + if (resource_size(cxled->dpa_res) * p->interleave_ways != + resource_size(p->res)) { + dev_dbg(&cxlr->dev, + "%s:%s: decoder-size-%#llx * ways-%d != region-size-%#llx\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), + (u64)resource_size(cxled->dpa_res), p->interleave_ways, + (u64)resource_size(p->res)); + return -EINVAL; + } + + for (iter = ep_port; !is_cxl_root(iter); + iter = to_cxl_port(iter->dev.parent)) { + rc = cxl_port_attach_region(iter, cxlr, cxled, pos); + if (rc) + goto err; + } + + p->targets[pos] = cxled; + cxled->pos = pos; + p->nr_targets++; + + if (p->nr_targets == p->interleave_ways) { + rc = cxl_region_setup_targets(cxlr); + if (rc) + goto err_decrement; + p->state = CXL_CONFIG_ACTIVE; + } + + cxled->cxld.interleave_ways = p->interleave_ways; + cxled->cxld.interleave_granularity = p->interleave_granularity; + cxled->cxld.hpa_range = (struct range) { + .start = p->res->start, + .end = p->res->end, + }; + + return 0; + +err_decrement: + p->nr_targets--; +err: + for (iter = ep_port; !is_cxl_root(iter); + iter = to_cxl_port(iter->dev.parent)) + cxl_port_detach_region(iter, cxlr, cxled); + return rc; +} + +static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_port *iter, *ep_port = cxled_to_port(cxled); + struct cxl_region *cxlr = cxled->cxld.region; + struct cxl_region_params *p; + int rc = 0; + + lockdep_assert_held_write(&cxl_region_rwsem); + + if (!cxlr) + return 0; + + p = &cxlr->params; + get_device(&cxlr->dev); + + if (p->state > CXL_CONFIG_ACTIVE) { + /* + * TODO: tear down all impacted regions if a device is + * removed out of order + */ + rc = cxl_region_decode_reset(cxlr, p->interleave_ways); + if (rc) + goto out; + p->state = CXL_CONFIG_ACTIVE; + } + + for (iter = ep_port; !is_cxl_root(iter); + iter = to_cxl_port(iter->dev.parent)) + cxl_port_detach_region(iter, cxlr, cxled); + + if (cxled->pos < 0 || cxled->pos >= p->interleave_ways || + p->targets[cxled->pos] != cxled) { + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + + dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n", + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), + cxled->pos); + goto out; + } + + if (p->state == CXL_CONFIG_ACTIVE) { + p->state = CXL_CONFIG_INTERLEAVE_ACTIVE; + cxl_region_teardown_targets(cxlr); + } + p->targets[cxled->pos] = NULL; + p->nr_targets--; + cxled->cxld.hpa_range = (struct range) { + .start = 0, + .end = -1, + }; + + /* notify the region driver that one of its targets has departed */ + up_write(&cxl_region_rwsem); + device_release_driver(&cxlr->dev); + down_write(&cxl_region_rwsem); +out: + put_device(&cxlr->dev); + return rc; +} + +void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled) +{ + down_write(&cxl_region_rwsem); + cxled->mode = CXL_DECODER_DEAD; + cxl_region_detach(cxled); + up_write(&cxl_region_rwsem); +} + +static int attach_target(struct cxl_region *cxlr, const char *decoder, int pos) +{ + struct device *dev; + int rc; + + dev = bus_find_device_by_name(&cxl_bus_type, NULL, decoder); + if (!dev) + return -ENODEV; + + if (!is_endpoint_decoder(dev)) { + put_device(dev); + return -EINVAL; + } + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + goto out; + down_read(&cxl_dpa_rwsem); + rc = cxl_region_attach(cxlr, to_cxl_endpoint_decoder(dev), pos); + up_read(&cxl_dpa_rwsem); + up_write(&cxl_region_rwsem); +out: + put_device(dev); + return rc; +} + +static int detach_target(struct cxl_region *cxlr, int pos) +{ + struct cxl_region_params *p = &cxlr->params; + int rc; + + rc = down_write_killable(&cxl_region_rwsem); + if (rc) + return rc; + + if (pos >= p->interleave_ways) { + dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos, + p->interleave_ways); + rc = -ENXIO; + goto out; + } + + if (!p->targets[pos]) { + rc = 0; + goto out; + } + + rc = cxl_region_detach(p->targets[pos]); +out: + up_write(&cxl_region_rwsem); + return rc; +} + +static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos, + size_t len) +{ + int rc; + + if (sysfs_streq(buf, "\n")) + rc = detach_target(cxlr, pos); + else + rc = attach_target(cxlr, buf, pos); + + if (rc < 0) + return rc; + return len; +} + +#define TARGET_ATTR_RW(n) \ +static ssize_t target##n##_show( \ + struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_targetN(to_cxl_region(dev), buf, (n)); \ +} \ +static ssize_t target##n##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_targetN(to_cxl_region(dev), buf, (n), len); \ +} \ +static DEVICE_ATTR_RW(target##n) + +TARGET_ATTR_RW(0); +TARGET_ATTR_RW(1); +TARGET_ATTR_RW(2); +TARGET_ATTR_RW(3); +TARGET_ATTR_RW(4); +TARGET_ATTR_RW(5); +TARGET_ATTR_RW(6); +TARGET_ATTR_RW(7); +TARGET_ATTR_RW(8); +TARGET_ATTR_RW(9); +TARGET_ATTR_RW(10); +TARGET_ATTR_RW(11); +TARGET_ATTR_RW(12); +TARGET_ATTR_RW(13); +TARGET_ATTR_RW(14); +TARGET_ATTR_RW(15); + +static struct attribute *target_attrs[] = { + &dev_attr_target0.attr, + &dev_attr_target1.attr, + &dev_attr_target2.attr, + &dev_attr_target3.attr, + &dev_attr_target4.attr, + &dev_attr_target5.attr, + &dev_attr_target6.attr, + &dev_attr_target7.attr, + &dev_attr_target8.attr, + &dev_attr_target9.attr, + &dev_attr_target10.attr, + &dev_attr_target11.attr, + &dev_attr_target12.attr, + &dev_attr_target13.attr, + &dev_attr_target14.attr, + &dev_attr_target15.attr, + NULL, +}; + +static umode_t cxl_region_target_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + + if (n < p->interleave_ways) + return a->mode; + return 0; +} + +static const struct attribute_group cxl_region_target_group = { + .attrs = target_attrs, + .is_visible = cxl_region_target_visible, +}; + +static const struct attribute_group *get_cxl_region_target_group(void) +{ + return &cxl_region_target_group; +} + +static const struct attribute_group *region_groups[] = { + &cxl_base_attribute_group, + &cxl_region_group, + &cxl_region_target_group, + NULL, +}; + +static void cxl_region_release(struct device *dev) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + + memregion_free(cxlr->id); + kfree(cxlr); +} + +const struct device_type cxl_region_type = { + .name = "cxl_region", + .release = cxl_region_release, + .groups = region_groups +}; + +bool is_cxl_region(struct device *dev) +{ + return dev->type == &cxl_region_type; +} +EXPORT_SYMBOL_NS_GPL(is_cxl_region, CXL); + +static struct cxl_region *to_cxl_region(struct device *dev) +{ + if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type, + "not a cxl_region device\n")) + return NULL; + + return container_of(dev, struct cxl_region, dev); +} + +static void unregister_region(void *dev) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + + device_del(dev); + cxl_region_iomem_release(cxlr); + put_device(dev); +} + +static struct lock_class_key cxl_region_key; + +static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id) +{ + struct cxl_region *cxlr; + struct device *dev; + + cxlr = kzalloc(sizeof(*cxlr), GFP_KERNEL); + if (!cxlr) { + memregion_free(id); + return ERR_PTR(-ENOMEM); + } + + dev = &cxlr->dev; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_region_key); + dev->parent = &cxlrd->cxlsd.cxld.dev; + device_set_pm_not_required(dev); + dev->bus = &cxl_bus_type; + dev->type = &cxl_region_type; + cxlr->id = id; + + return cxlr; +} + +/** + * devm_cxl_add_region - Adds a region to a decoder + * @cxlrd: root decoder + * @id: memregion id to create, or memregion_free() on failure + * @mode: mode for the endpoint decoders of this region + * @type: select whether this is an expander or accelerator (type-2 or type-3) + * + * This is the second step of region initialization. Regions exist within an + * address space which is mapped by a @cxlrd. + * + * Return: 0 if the region was added to the @cxlrd, else returns negative error + * code. The region will be named "regionZ" where Z is the unique region number. + */ +static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, + int id, + enum cxl_decoder_mode mode, + enum cxl_decoder_type type) +{ + struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent); + struct cxl_region *cxlr; + struct device *dev; + int rc; + + cxlr = cxl_region_alloc(cxlrd, id); + if (IS_ERR(cxlr)) + return cxlr; + cxlr->mode = mode; + cxlr->type = type; + + dev = &cxlr->dev; + rc = dev_set_name(dev, "region%d", id); + if (rc) + goto err; + + rc = device_add(dev); + if (rc) + goto err; + + rc = devm_add_action_or_reset(port->uport, unregister_region, cxlr); + if (rc) + return ERR_PTR(rc); + + dev_dbg(port->uport, "%s: created %s\n", + dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev)); + return cxlr; + +err: + put_device(dev); + return ERR_PTR(rc); +} + +static ssize_t create_pmem_region_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + + return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id)); +} + +static ssize_t create_pmem_region_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + struct cxl_region *cxlr; + int id, rc; + + rc = sscanf(buf, "region%d\n", &id); + if (rc != 1) + return -EINVAL; + + rc = memregion_alloc(GFP_KERNEL); + if (rc < 0) + return rc; + + if (atomic_cmpxchg(&cxlrd->region_id, id, rc) != id) { + memregion_free(rc); + return -EBUSY; + } + + cxlr = devm_cxl_add_region(cxlrd, id, CXL_DECODER_PMEM, + CXL_DECODER_EXPANDER); + if (IS_ERR(cxlr)) + return PTR_ERR(cxlr); + + return len; +} +DEVICE_ATTR_RW(create_pmem_region); + +static ssize_t region_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_decoder *cxld = to_cxl_decoder(dev); + ssize_t rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) + return rc; + + if (cxld->region) + rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev)); + else + rc = sysfs_emit(buf, "\n"); + up_read(&cxl_region_rwsem); + + return rc; +} +DEVICE_ATTR_RO(region); + +static struct cxl_region * +cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name) +{ + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + struct device *region_dev; + + region_dev = device_find_child_by_name(&cxld->dev, name); + if (!region_dev) + return ERR_PTR(-ENODEV); + + return to_cxl_region(region_dev); +} + +static ssize_t delete_region_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + struct cxl_port *port = to_cxl_port(dev->parent); + struct cxl_region *cxlr; + + cxlr = cxl_find_region_by_name(cxlrd, buf); + if (IS_ERR(cxlr)) + return PTR_ERR(cxlr); + + devm_release_action(port->uport, unregister_region, cxlr); + put_device(&cxlr->dev); + + return len; +} +DEVICE_ATTR_WO(delete_region); + +static void cxl_pmem_region_release(struct device *dev) +{ + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); + int i; + + for (i = 0; i < cxlr_pmem->nr_mappings; i++) { + struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; + + put_device(&cxlmd->dev); + } + + kfree(cxlr_pmem); +} + +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { + &cxl_base_attribute_group, + NULL, +}; + +const struct device_type cxl_pmem_region_type = { + .name = "cxl_pmem_region", + .release = cxl_pmem_region_release, + .groups = cxl_pmem_region_attribute_groups, +}; + +bool is_cxl_pmem_region(struct device *dev) +{ + return dev->type == &cxl_pmem_region_type; +} +EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, CXL); + +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), + "not a cxl_pmem_region device\n")) + return NULL; + return container_of(dev, struct cxl_pmem_region, dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, CXL); + +static struct lock_class_key cxl_pmem_region_key; + +static struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_pmem_region *cxlr_pmem; + struct device *dev; + int i; + + down_read(&cxl_region_rwsem); + if (p->state != CXL_CONFIG_COMMIT) { + cxlr_pmem = ERR_PTR(-ENXIO); + goto out; + } + + cxlr_pmem = kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), + GFP_KERNEL); + if (!cxlr_pmem) { + cxlr_pmem = ERR_PTR(-ENOMEM); + goto out; + } + + cxlr_pmem->hpa_range.start = p->res->start; + cxlr_pmem->hpa_range.end = p->res->end; + + /* Snapshot the region configuration underneath the cxl_region_rwsem */ + cxlr_pmem->nr_mappings = p->nr_targets; + for (i = 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; + + m->cxlmd = cxlmd; + get_device(&cxlmd->dev); + m->start = cxled->dpa_res->start; + m->size = resource_size(cxled->dpa_res); + m->position = i; + } + + dev = &cxlr_pmem->dev; + cxlr_pmem->cxlr = cxlr; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); + device_set_pm_not_required(dev); + dev->parent = &cxlr->dev; + dev->bus = &cxl_bus_type; + dev->type = &cxl_pmem_region_type; +out: + up_read(&cxl_region_rwsem); + + return cxlr_pmem; +} + +static void cxlr_pmem_unregister(void *dev) +{ + device_unregister(dev); +} + +/** + * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge + * @cxlr: parent CXL region for this pmem region bridge device + * + * Return: 0 on success negative error code on failure. + */ +static int devm_cxl_add_pmem_region(struct cxl_region *cxlr) +{ + struct cxl_pmem_region *cxlr_pmem; + struct device *dev; + int rc; + + cxlr_pmem = cxl_pmem_region_alloc(cxlr); + if (IS_ERR(cxlr_pmem)) + return PTR_ERR(cxlr_pmem); + + dev = &cxlr_pmem->dev; + rc = dev_set_name(dev, "pmem_region%d", cxlr->id); + if (rc) + goto err; + + rc = device_add(dev); + if (rc) + goto err; + + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), + dev_name(dev)); + + return devm_add_action_or_reset(&cxlr->dev, cxlr_pmem_unregister, dev); + +err: + put_device(dev); + return rc; +} + +static int cxl_region_probe(struct device *dev) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + int rc; + + rc = down_read_interruptible(&cxl_region_rwsem); + if (rc) { + dev_dbg(&cxlr->dev, "probe interrupted\n"); + return rc; + } + + if (p->state < CXL_CONFIG_COMMIT) { + dev_dbg(&cxlr->dev, "config state: %d\n", p->state); + rc = -ENXIO; + } + + /* + * From this point on any path that changes the region's state away from + * CXL_CONFIG_COMMIT is also responsible for releasing the driver. + */ + up_read(&cxl_region_rwsem); + + switch (cxlr->mode) { + case CXL_DECODER_PMEM: + return devm_cxl_add_pmem_region(cxlr); + default: + dev_dbg(&cxlr->dev, "unsupported region mode: %d\n", + cxlr->mode); + return -ENXIO; + } +} + +static struct cxl_driver cxl_region_driver = { + .name = "cxl_region", + .probe = cxl_region_probe, + .id = CXL_DEVICE_REGION, +}; + +int cxl_region_init(void) +{ + return cxl_driver_register(&cxl_region_driver); +} + +void cxl_region_exit(void) +{ + cxl_driver_unregister(&cxl_region_driver); +} + +MODULE_IMPORT_NS(CXL); +MODULE_ALIAS_CXL(CXL_DEVICE_REGION); diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 6799b27c7db2..f680450f0b16 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -7,6 +7,7 @@ #include <linux/libnvdimm.h> #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/log2.h> #include <linux/io.h> /** @@ -53,9 +54,12 @@ #define CXL_HDM_DECODER0_CTRL_LOCK BIT(8) #define CXL_HDM_DECODER0_CTRL_COMMIT BIT(9) #define CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10) +#define CXL_HDM_DECODER0_CTRL_COMMIT_ERROR BIT(11) #define CXL_HDM_DECODER0_CTRL_TYPE BIT(12) #define CXL_HDM_DECODER0_TL_LOW(i) (0x20 * (i) + 0x24) #define CXL_HDM_DECODER0_TL_HIGH(i) (0x20 * (i) + 0x28) +#define CXL_HDM_DECODER0_SKIP_LOW(i) CXL_HDM_DECODER0_TL_LOW(i) +#define CXL_HDM_DECODER0_SKIP_HIGH(i) CXL_HDM_DECODER0_TL_HIGH(i) static inline int cxl_hdm_decoder_count(u32 cap_hdr) { @@ -64,6 +68,57 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr) return val ? val * 2 : 1; } +/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ +static inline int cxl_to_granularity(u16 ig, unsigned int *val) +{ + if (ig > 6) + return -EINVAL; + *val = 256 << ig; + return 0; +} + +/* Encode defined in CXL ECN "3, 6, 12 and 16-way memory Interleaving" */ +static inline int cxl_to_ways(u8 eniw, unsigned int *val) +{ + switch (eniw) { + case 0 ... 4: + *val = 1 << eniw; + break; + case 8 ... 10: + *val = 3 << (eniw - 8); + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline int granularity_to_cxl(int g, u16 *ig) +{ + if (g > SZ_16K || g < 256 || !is_power_of_2(g)) + return -EINVAL; + *ig = ilog2(g) - 8; + return 0; +} + +static inline int ways_to_cxl(unsigned int ways, u8 *iw) +{ + if (ways > 16) + return -EINVAL; + if (is_power_of_2(ways)) { + *iw = ilog2(ways); + return 0; + } + if (ways % 3) + return -EINVAL; + ways /= 3; + if (!is_power_of_2(ways)) + return -EINVAL; + *iw = ilog2(ways) + 8; + return 0; +} + /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 #define CXLDEV_CAP_ARRAY_CAP_ID 0 @@ -193,31 +248,77 @@ enum cxl_decoder_type { */ #define CXL_DECODER_MAX_INTERLEAVE 16 +#define CXL_DECODER_MIN_GRANULARITY 256 + /** - * struct cxl_decoder - CXL address range decode configuration + * struct cxl_decoder - Common CXL HDM Decoder Attributes * @dev: this decoder's device * @id: kernel device name id - * @platform_res: address space resources considered by root decoder - * @decoder_range: address space resources considered by midlevel decoder + * @hpa_range: Host physical address range mapped by this decoder * @interleave_ways: number of cxl_dports in this decode * @interleave_granularity: data stride per dport * @target_type: accelerator vs expander (type2 vs type3) selector + * @region: currently assigned region for this decoder * @flags: memory type capabilities and locking - * @target_lock: coordinate coherent reads of the target list - * @nr_targets: number of elements in @target - * @target: active ordered target list in current decoder configuration - */ + * @commit: device/decoder-type specific callback to commit settings to hw + * @reset: device/decoder-type specific callback to reset hw settings +*/ struct cxl_decoder { struct device dev; int id; - union { - struct resource platform_res; - struct range decoder_range; - }; + struct range hpa_range; int interleave_ways; int interleave_granularity; enum cxl_decoder_type target_type; + struct cxl_region *region; unsigned long flags; + int (*commit)(struct cxl_decoder *cxld); + int (*reset)(struct cxl_decoder *cxld); +}; + +/* + * CXL_DECODER_DEAD prevents endpoints from being reattached to regions + * while cxld_unregister() is running + */ +enum cxl_decoder_mode { + CXL_DECODER_NONE, + CXL_DECODER_RAM, + CXL_DECODER_PMEM, + CXL_DECODER_MIXED, + CXL_DECODER_DEAD, +}; + +/** + * struct cxl_endpoint_decoder - Endpoint / SPA to DPA decoder + * @cxld: base cxl_decoder_object + * @dpa_res: actively claimed DPA span of this decoder + * @skip: offset into @dpa_res where @cxld.hpa_range maps + * @mode: which memory type / access-mode-partition this decoder targets + * @pos: interleave position in @cxld.region + */ +struct cxl_endpoint_decoder { + struct cxl_decoder cxld; + struct resource *dpa_res; + resource_size_t skip; + enum cxl_decoder_mode mode; + int pos; +}; + +/** + * struct cxl_switch_decoder - Switch specific CXL HDM Decoder + * @cxld: base cxl_decoder object + * @target_lock: coordinate coherent reads of the target list + * @nr_targets: number of elements in @target + * @target: active ordered target list in current decoder configuration + * + * The 'switch' decoder type represents the decoder instances of cxl_port's that + * route from the root of a CXL memory decode topology to the endpoints. They + * come in two flavors, root-level decoders, statically defined by platform + * firmware, and mid-level decoders, where interleave-granularity, + * interleave-width, and the target list are mutable. + */ +struct cxl_switch_decoder { + struct cxl_decoder cxld; seqlock_t target_lock; int nr_targets; struct cxl_dport *target[]; @@ -225,6 +326,76 @@ struct cxl_decoder { /** + * struct cxl_root_decoder - Static platform CXL address decoder + * @res: host / parent resource for region allocations + * @region_id: region id for next region provisioning event + * @calc_hb: which host bridge covers the n'th position by granularity + * @cxlsd: base cxl switch decoder + */ +struct cxl_root_decoder { + struct resource *res; + atomic_t region_id; + struct cxl_dport *(*calc_hb)(struct cxl_root_decoder *cxlrd, int pos); + struct cxl_switch_decoder cxlsd; +}; + +/* + * enum cxl_config_state - State machine for region configuration + * @CXL_CONFIG_IDLE: Any sysfs attribute can be written freely + * @CXL_CONFIG_INTERLEAVE_ACTIVE: region size has been set, no more + * changes to interleave_ways or interleave_granularity + * @CXL_CONFIG_ACTIVE: All targets have been added the region is now + * active + * @CXL_CONFIG_RESET_PENDING: see commit_store() + * @CXL_CONFIG_COMMIT: Soft-config has been committed to hardware + */ +enum cxl_config_state { + CXL_CONFIG_IDLE, + CXL_CONFIG_INTERLEAVE_ACTIVE, + CXL_CONFIG_ACTIVE, + CXL_CONFIG_RESET_PENDING, + CXL_CONFIG_COMMIT, +}; + +/** + * struct cxl_region_params - region settings + * @state: allow the driver to lockdown further parameter changes + * @uuid: unique id for persistent regions + * @interleave_ways: number of endpoints in the region + * @interleave_granularity: capacity each endpoint contributes to a stripe + * @res: allocated iomem capacity for this region + * @targets: active ordered targets in current decoder configuration + * @nr_targets: number of targets + * + * State transitions are protected by the cxl_region_rwsem + */ +struct cxl_region_params { + enum cxl_config_state state; + uuid_t uuid; + int interleave_ways; + int interleave_granularity; + struct resource *res; + struct cxl_endpoint_decoder *targets[CXL_DECODER_MAX_INTERLEAVE]; + int nr_targets; +}; + +/** + * struct cxl_region - CXL region + * @dev: This region's device + * @id: This region's id. Id is globally unique across all regions + * @mode: Endpoint decoder allocation / access mode + * @type: Endpoint decoder target type + * @params: active + config params for the region + */ +struct cxl_region { + struct device dev; + int id; + enum cxl_decoder_mode mode; + enum cxl_decoder_type type; + struct cxl_region_params params; +}; + +/** * enum cxl_nvdimm_brige_state - state machine for managing bus rescans * @CXL_NVB_NEW: Set at bridge create and after cxl_pmem_wq is destroyed * @CXL_NVB_DEAD: Set at brige unregistration to preclude async probing @@ -251,7 +422,26 @@ struct cxl_nvdimm_bridge { struct cxl_nvdimm { struct device dev; struct cxl_memdev *cxlmd; - struct nvdimm *nvdimm; + struct cxl_nvdimm_bridge *bridge; + struct cxl_pmem_region *region; +}; + +struct cxl_pmem_region_mapping { + struct cxl_memdev *cxlmd; + struct cxl_nvdimm *cxl_nvd; + u64 start; + u64 size; + int position; +}; + +struct cxl_pmem_region { + struct device dev; + struct cxl_region *cxlr; + struct nd_region *nd_region; + struct cxl_nvdimm_bridge *bridge; + struct range hpa_range; + int nr_mappings; + struct cxl_pmem_region_mapping mapping[]; }; /** @@ -260,50 +450,94 @@ struct cxl_nvdimm { * decode hierarchy. * @dev: this port's device * @uport: PCI or platform device implementing the upstream port capability + * @host_bridge: Shortcut to the platform attach point for this port * @id: id for port device-name * @dports: cxl_dport instances referenced by decoders * @endpoints: cxl_ep instances, endpoints that are a descendant of this port + * @regions: cxl_region_ref instances, regions mapped by this port + * @parent_dport: dport that points to this port in the parent * @decoder_ida: allocator for decoder ids + * @hdm_end: track last allocated HDM decoder instance for allocation ordering + * @commit_end: cursor to track highest committed decoder for commit ordering * @component_reg_phys: component register capability base address (optional) * @dead: last ep has been removed, force port re-creation * @depth: How deep this port is relative to the root. depth 0 is the root. + * @cdat: Cached CDAT data + * @cdat_available: Should a CDAT attribute be available in sysfs */ struct cxl_port { struct device dev; struct device *uport; + struct device *host_bridge; int id; - struct list_head dports; - struct list_head endpoints; + struct xarray dports; + struct xarray endpoints; + struct xarray regions; + struct cxl_dport *parent_dport; struct ida decoder_ida; + int hdm_end; + int commit_end; resource_size_t component_reg_phys; bool dead; unsigned int depth; + struct cxl_cdat { + void *table; + size_t length; + } cdat; + bool cdat_available; }; +static inline struct cxl_dport * +cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev) +{ + return xa_load(&port->dports, (unsigned long)dport_dev); +} + /** * struct cxl_dport - CXL downstream port * @dport: PCI bridge or firmware device representing the downstream link * @port_id: unique hardware identifier for dport in decoder target list * @component_reg_phys: downstream port component registers * @port: reference to cxl_port that contains this downstream port - * @list: node for a cxl_port's list of cxl_dport instances */ struct cxl_dport { struct device *dport; int port_id; resource_size_t component_reg_phys; struct cxl_port *port; - struct list_head list; }; /** * struct cxl_ep - track an endpoint's interest in a port * @ep: device that hosts a generic CXL endpoint (expander or accelerator) - * @list: node on port->endpoints list + * @dport: which dport routes to this endpoint on @port + * @next: cxl switch port across the link attached to @dport NULL if + * attached to an endpoint */ struct cxl_ep { struct device *ep; - struct list_head list; + struct cxl_dport *dport; + struct cxl_port *next; +}; + +/** + * struct cxl_region_ref - track a region's interest in a port + * @port: point in topology to install this reference + * @decoder: decoder assigned for @region in @port + * @region: region for this reference + * @endpoints: cxl_ep references for region members beneath @port + * @nr_targets_set: track how many targets have been programmed during setup + * @nr_eps: number of endpoints beneath @port + * @nr_targets: number of distinct targets needed to reach @nr_eps + */ +struct cxl_region_ref { + struct cxl_port *port; + struct cxl_decoder *decoder; + struct cxl_region *region; + struct xarray endpoints; + int nr_targets_set; + int nr_eps; + int nr_targets; }; /* @@ -325,29 +559,31 @@ int devm_cxl_register_pci_bus(struct device *host, struct device *uport, struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port); struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport, resource_size_t component_reg_phys, - struct cxl_port *parent_port); + struct cxl_dport *parent_dport); +int devm_cxl_add_endpoint(struct cxl_memdev *cxlmd, + struct cxl_dport *parent_dport); struct cxl_port *find_cxl_root(struct device *dev); int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd); int cxl_bus_rescan(void); -struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd); +struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd, + struct cxl_dport **dport); bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd); struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id, resource_size_t component_reg_phys); -struct cxl_dport *cxl_find_dport_by_dev(struct cxl_port *port, - const struct device *dev); struct cxl_decoder *to_cxl_decoder(struct device *dev); +struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev); +struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev); bool is_root_decoder(struct device *dev); bool is_endpoint_decoder(struct device *dev); -bool is_cxl_decoder(struct device *dev); -struct cxl_decoder *cxl_root_decoder_alloc(struct cxl_port *port, - unsigned int nr_targets); -struct cxl_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, - unsigned int nr_targets); +struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, + unsigned int nr_targets); +struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, + unsigned int nr_targets); int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map); -struct cxl_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port); +struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port); int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map); int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld); int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint); @@ -357,6 +593,8 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port); int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm); int devm_cxl_add_passthrough_decoder(struct cxl_port *port); +bool is_cxl_region(struct device *dev); + extern struct bus_type cxl_bus_type; struct cxl_driver { @@ -385,6 +623,8 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv); #define CXL_DEVICE_PORT 3 #define CXL_DEVICE_ROOT 4 #define CXL_DEVICE_MEMORY_EXPANDER 5 +#define CXL_DEVICE_REGION 6 +#define CXL_DEVICE_PMEM_REGION 7 #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*") #define CXL_MODALIAS_FMT "cxl:t%d" @@ -396,7 +636,21 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev); bool is_cxl_nvdimm(struct device *dev); bool is_cxl_nvdimm_bridge(struct device *dev); int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd); -struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd); +struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct device *dev); + +#ifdef CONFIG_CXL_REGION +bool is_cxl_pmem_region(struct device *dev); +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev); +#else +static inline bool is_cxl_pmem_region(struct device *dev) +{ + return false; +} +static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) +{ + return NULL; +} +#endif /* * Unit test builds overrides this to __weak, find the 'strong' version diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 7df0b053373a..88e3a8e54b6a 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -50,6 +50,24 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev) return container_of(dev, struct cxl_memdev, dev); } +static inline struct cxl_port *cxled_to_port(struct cxl_endpoint_decoder *cxled) +{ + return to_cxl_port(cxled->cxld.dev.parent); +} + +static inline struct cxl_port *cxlrd_to_port(struct cxl_root_decoder *cxlrd) +{ + return to_cxl_port(cxlrd->cxlsd.cxld.dev.parent); +} + +static inline struct cxl_memdev * +cxled_to_memdev(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_port *port = to_cxl_port(cxled->cxld.dev.parent); + + return to_cxl_memdev(port->uport); +} + bool is_cxl_memdev(struct device *dev); static inline bool is_cxl_endpoint(struct cxl_port *port) { @@ -178,8 +196,9 @@ struct cxl_endpoint_dvsec_info { * @firmware_version: Firmware version for the memory device. * @enabled_cmds: Hardware commands found enabled in CEL. * @exclusive_cmds: Commands that are kernel-internal only - * @pmem_range: Active Persistent memory capacity configuration - * @ram_range: Active Volatile memory capacity configuration + * @dpa_res: Overall DPA resource tree for the device + * @pmem_res: Active Persistent memory capacity configuration + * @ram_res: Active Volatile memory capacity configuration * @total_bytes: sum of all possible capacities * @volatile_only_bytes: hard volatile capacity * @persistent_only_bytes: hard persistent capacity @@ -191,6 +210,7 @@ struct cxl_endpoint_dvsec_info { * @component_reg_phys: register base of component registers * @info: Cached DVSEC information about the device. * @serial: PCIe Device Serial Number + * @doe_mbs: PCI DOE mailbox array * @mbox_send: @dev specific transport for transmitting mailbox commands * * See section 8.2.9.5.2 Capacity Configuration and Label Storage for @@ -209,8 +229,9 @@ struct cxl_dev_state { DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX); DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); - struct range pmem_range; - struct range ram_range; + struct resource dpa_res; + struct resource pmem_res; + struct resource ram_res; u64 total_bytes; u64 volatile_only_bytes; u64 persistent_only_bytes; @@ -224,6 +245,8 @@ struct cxl_dev_state { resource_size_t component_reg_phys; u64 serial; + struct xarray doe_mbs; + int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd); }; @@ -299,6 +322,13 @@ struct cxl_mbox_identify { u8 qos_telemetry_caps; } __packed; +struct cxl_mbox_get_partition_info { + __le64 active_volatile_cap; + __le64 active_persistent_cap; + __le64 next_volatile_cap; + __le64 next_persistent_cap; +} __packed; + struct cxl_mbox_get_lsa { __le32 offset; __le32 length; @@ -370,4 +400,8 @@ struct cxl_hdm { unsigned int interleave_mask; struct cxl_port *port; }; + +struct seq_file; +struct dentry *cxl_debugfs_create_dir(const char *dir); +void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds); #endif /* __CXL_MEM_H__ */ diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index fce1c11729c2..eec597dbe763 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -74,4 +74,5 @@ static inline resource_size_t cxl_regmap_to_base(struct pci_dev *pdev, int devm_cxl_port_enumerate_dports(struct cxl_port *port); struct cxl_dev_state; int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm); +void read_cdat_data(struct cxl_port *port); #endif /* __CXL_PCI_H__ */ diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index a979d0b484d5..64ccf053d32c 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/module.h> #include <linux/pci.h> @@ -24,42 +25,32 @@ * in higher level operations. */ -static int create_endpoint(struct cxl_memdev *cxlmd, - struct cxl_port *parent_port) +static void enable_suspend(void *data) { - struct cxl_dev_state *cxlds = cxlmd->cxlds; - struct cxl_port *endpoint; - int rc; + cxl_mem_active_dec(); +} - endpoint = devm_cxl_add_port(&parent_port->dev, &cxlmd->dev, - cxlds->component_reg_phys, parent_port); - if (IS_ERR(endpoint)) - return PTR_ERR(endpoint); +static void remove_debugfs(void *dentry) +{ + debugfs_remove_recursive(dentry); +} - dev_dbg(&cxlmd->dev, "add: %s\n", dev_name(&endpoint->dev)); +static int cxl_mem_dpa_show(struct seq_file *file, void *data) +{ + struct device *dev = file->private; + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); - rc = cxl_endpoint_autoremove(cxlmd, endpoint); - if (rc) - return rc; - - if (!endpoint->dev.driver) { - dev_err(&cxlmd->dev, "%s failed probe\n", - dev_name(&endpoint->dev)); - return -ENXIO; - } + cxl_dpa_debug(file, cxlmd->cxlds); return 0; } -static void enable_suspend(void *data) -{ - cxl_mem_active_dec(); -} - static int cxl_mem_probe(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); struct cxl_port *parent_port; + struct cxl_dport *dport; + struct dentry *dentry; int rc; /* @@ -73,11 +64,17 @@ static int cxl_mem_probe(struct device *dev) if (work_pending(&cxlmd->detach_work)) return -EBUSY; + dentry = cxl_debugfs_create_dir(dev_name(dev)); + debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show); + rc = devm_add_action_or_reset(dev, remove_debugfs, dentry); + if (rc) + return rc; + rc = devm_cxl_enumerate_ports(cxlmd); if (rc) return rc; - parent_port = cxl_mem_find_port(cxlmd); + parent_port = cxl_mem_find_port(cxlmd, &dport); if (!parent_port) { dev_err(dev, "CXL port topology not found\n"); return -ENXIO; @@ -91,7 +88,7 @@ static int cxl_mem_probe(struct device *dev) goto unlock; } - rc = create_endpoint(cxlmd, parent_port); + rc = devm_cxl_add_endpoint(cxlmd, dport); unlock: device_unlock(&parent_port->dev); put_device(&parent_port->dev); diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 5a0ae46d4989..faeb5d9d7a7a 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -8,6 +8,7 @@ #include <linux/mutex.h> #include <linux/list.h> #include <linux/pci.h> +#include <linux/pci-doe.h> #include <linux/io.h> #include "cxlmem.h" #include "cxlpci.h" @@ -386,6 +387,47 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, return rc; } +static void cxl_pci_destroy_doe(void *mbs) +{ + xa_destroy(mbs); +} + +static void devm_cxl_pci_create_doe(struct cxl_dev_state *cxlds) +{ + struct device *dev = cxlds->dev; + struct pci_dev *pdev = to_pci_dev(dev); + u16 off = 0; + + xa_init(&cxlds->doe_mbs); + if (devm_add_action(&pdev->dev, cxl_pci_destroy_doe, &cxlds->doe_mbs)) { + dev_err(dev, "Failed to create XArray for DOE's\n"); + return; + } + + /* + * Mailbox creation is best effort. Higher layers must determine if + * the lack of a mailbox for their protocol is a device failure or not. + */ + pci_doe_for_each_off(pdev, off) { + struct pci_doe_mb *doe_mb; + + doe_mb = pcim_doe_create_mb(pdev, off); + if (IS_ERR(doe_mb)) { + dev_err(dev, "Failed to create MB object for MB @ %x\n", + off); + continue; + } + + if (xa_insert(&cxlds->doe_mbs, off, doe_mb, GFP_KERNEL)) { + dev_err(dev, "xa_insert failed to insert MB @ %x\n", + off); + continue; + } + + dev_dbg(dev, "Created DOE mailbox @%x\n", off); + } +} + static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct cxl_register_map map; @@ -434,6 +476,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) cxlds->component_reg_phys = cxl_regmap_to_base(pdev, &map); + devm_cxl_pci_create_doe(cxlds); + rc = cxl_pci_setup_mailbox(cxlds); if (rc) return rc; @@ -454,7 +498,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); - if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM)) + if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) rc = devm_cxl_add_nvdimm(&pdev->dev, cxlmd); return rc; diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c index 0aaa70b4e0f7..7dc0a2fa1a6b 100644 --- a/drivers/cxl/pmem.c +++ b/drivers/cxl/pmem.c @@ -7,6 +7,7 @@ #include <linux/ndctl.h> #include <linux/async.h> #include <linux/slab.h> +#include <linux/nd.h> #include "cxlmem.h" #include "cxl.h" @@ -26,7 +27,23 @@ static void clear_exclusive(void *cxlds) static void unregister_nvdimm(void *nvdimm) { + struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm); + struct cxl_nvdimm_bridge *cxl_nvb = cxl_nvd->bridge; + struct cxl_pmem_region *cxlr_pmem; + + device_lock(&cxl_nvb->dev); + cxlr_pmem = cxl_nvd->region; + dev_set_drvdata(&cxl_nvd->dev, NULL); + cxl_nvd->region = NULL; + device_unlock(&cxl_nvb->dev); + + if (cxlr_pmem) { + device_release_driver(&cxlr_pmem->dev); + put_device(&cxlr_pmem->dev); + } + nvdimm_delete(nvdimm); + cxl_nvd->bridge = NULL; } static int cxl_nvdimm_probe(struct device *dev) @@ -39,7 +56,7 @@ static int cxl_nvdimm_probe(struct device *dev) struct nvdimm *nvdimm; int rc; - cxl_nvb = cxl_find_nvdimm_bridge(cxl_nvd); + cxl_nvb = cxl_find_nvdimm_bridge(dev); if (!cxl_nvb) return -ENXIO; @@ -66,6 +83,7 @@ static int cxl_nvdimm_probe(struct device *dev) } dev_set_drvdata(dev, nvdimm); + cxl_nvd->bridge = cxl_nvb; rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm); out: device_unlock(&cxl_nvb->dev); @@ -204,15 +222,38 @@ static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb) return cxl_nvb->nvdimm_bus != NULL; } -static int cxl_nvdimm_release_driver(struct device *dev, void *data) +static int cxl_nvdimm_release_driver(struct device *dev, void *cxl_nvb) { + struct cxl_nvdimm *cxl_nvd; + if (!is_cxl_nvdimm(dev)) return 0; + + cxl_nvd = to_cxl_nvdimm(dev); + if (cxl_nvd->bridge != cxl_nvb) + return 0; + device_release_driver(dev); return 0; } -static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus) +static int cxl_pmem_region_release_driver(struct device *dev, void *cxl_nvb) +{ + struct cxl_pmem_region *cxlr_pmem; + + if (!is_cxl_pmem_region(dev)) + return 0; + + cxlr_pmem = to_cxl_pmem_region(dev); + if (cxlr_pmem->bridge != cxl_nvb) + return 0; + + device_release_driver(dev); + return 0; +} + +static void offline_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb, + struct nvdimm_bus *nvdimm_bus) { if (!nvdimm_bus) return; @@ -222,7 +263,10 @@ static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus) * nvdimm_bus_unregister() rips the nvdimm objects out from * underneath them. */ - bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_release_driver); + bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb, + cxl_pmem_region_release_driver); + bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb, + cxl_nvdimm_release_driver); nvdimm_bus_unregister(nvdimm_bus); } @@ -260,7 +304,7 @@ static void cxl_nvb_update_state(struct work_struct *work) dev_dbg(&cxl_nvb->dev, "rescan: %d\n", rc); } - offline_nvdimm_bus(victim_bus); + offline_nvdimm_bus(cxl_nvb, victim_bus); put_device(&cxl_nvb->dev); } @@ -315,6 +359,203 @@ static struct cxl_driver cxl_nvdimm_bridge_driver = { .id = CXL_DEVICE_NVDIMM_BRIDGE, }; +static int match_cxl_nvdimm(struct device *dev, void *data) +{ + return is_cxl_nvdimm(dev); +} + +static void unregister_nvdimm_region(void *nd_region) +{ + struct cxl_nvdimm_bridge *cxl_nvb; + struct cxl_pmem_region *cxlr_pmem; + int i; + + cxlr_pmem = nd_region_provider_data(nd_region); + cxl_nvb = cxlr_pmem->bridge; + device_lock(&cxl_nvb->dev); + for (i = 0; i < cxlr_pmem->nr_mappings; i++) { + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; + struct cxl_nvdimm *cxl_nvd = m->cxl_nvd; + + if (cxl_nvd->region) { + put_device(&cxlr_pmem->dev); + cxl_nvd->region = NULL; + } + } + device_unlock(&cxl_nvb->dev); + + nvdimm_region_delete(nd_region); +} + +static void cxlr_pmem_remove_resource(void *res) +{ + remove_resource(res); +} + +struct cxl_pmem_region_info { + u64 offset; + u64 serial; +}; + +static int cxl_pmem_region_probe(struct device *dev) +{ + struct nd_mapping_desc mappings[CXL_DECODER_MAX_INTERLEAVE]; + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); + struct cxl_region *cxlr = cxlr_pmem->cxlr; + struct cxl_pmem_region_info *info = NULL; + struct cxl_nvdimm_bridge *cxl_nvb; + struct nd_interleave_set *nd_set; + struct nd_region_desc ndr_desc; + struct cxl_nvdimm *cxl_nvd; + struct nvdimm *nvdimm; + struct resource *res; + int rc, i = 0; + + cxl_nvb = cxl_find_nvdimm_bridge(&cxlr_pmem->mapping[0].cxlmd->dev); + if (!cxl_nvb) { + dev_dbg(dev, "bridge not found\n"); + return -ENXIO; + } + cxlr_pmem->bridge = cxl_nvb; + + device_lock(&cxl_nvb->dev); + if (!cxl_nvb->nvdimm_bus) { + dev_dbg(dev, "nvdimm bus not found\n"); + rc = -ENXIO; + goto err; + } + + memset(&mappings, 0, sizeof(mappings)); + memset(&ndr_desc, 0, sizeof(ndr_desc)); + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) { + rc = -ENOMEM; + goto err; + } + + res->name = "Persistent Memory"; + res->start = cxlr_pmem->hpa_range.start; + res->end = cxlr_pmem->hpa_range.end; + res->flags = IORESOURCE_MEM; + res->desc = IORES_DESC_PERSISTENT_MEMORY; + + rc = insert_resource(&iomem_resource, res); + if (rc) + goto err; + + rc = devm_add_action_or_reset(dev, cxlr_pmem_remove_resource, res); + if (rc) + goto err; + + ndr_desc.res = res; + ndr_desc.provider_data = cxlr_pmem; + + ndr_desc.numa_node = memory_add_physaddr_to_nid(res->start); + ndr_desc.target_node = phys_to_target_node(res->start); + if (ndr_desc.target_node == NUMA_NO_NODE) { + ndr_desc.target_node = ndr_desc.numa_node; + dev_dbg(&cxlr->dev, "changing target node from %d to %d", + NUMA_NO_NODE, ndr_desc.target_node); + } + + nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL); + if (!nd_set) { + rc = -ENOMEM; + goto err; + } + + ndr_desc.memregion = cxlr->id; + set_bit(ND_REGION_CXL, &ndr_desc.flags); + set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc.flags); + + info = kmalloc_array(cxlr_pmem->nr_mappings, sizeof(*info), GFP_KERNEL); + if (!info) { + rc = -ENOMEM; + goto err; + } + + for (i = 0; i < cxlr_pmem->nr_mappings; i++) { + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; + struct cxl_memdev *cxlmd = m->cxlmd; + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct device *d; + + d = device_find_child(&cxlmd->dev, NULL, match_cxl_nvdimm); + if (!d) { + dev_dbg(dev, "[%d]: %s: no cxl_nvdimm found\n", i, + dev_name(&cxlmd->dev)); + rc = -ENODEV; + goto err; + } + + /* safe to drop ref now with bridge lock held */ + put_device(d); + + cxl_nvd = to_cxl_nvdimm(d); + nvdimm = dev_get_drvdata(&cxl_nvd->dev); + if (!nvdimm) { + dev_dbg(dev, "[%d]: %s: no nvdimm found\n", i, + dev_name(&cxlmd->dev)); + rc = -ENODEV; + goto err; + } + cxl_nvd->region = cxlr_pmem; + get_device(&cxlr_pmem->dev); + m->cxl_nvd = cxl_nvd; + mappings[i] = (struct nd_mapping_desc) { + .nvdimm = nvdimm, + .start = m->start, + .size = m->size, + .position = i, + }; + info[i].offset = m->start; + info[i].serial = cxlds->serial; + } + ndr_desc.num_mappings = cxlr_pmem->nr_mappings; + ndr_desc.mapping = mappings; + + /* + * TODO enable CXL labels which skip the need for 'interleave-set cookie' + */ + nd_set->cookie1 = + nd_fletcher64(info, sizeof(*info) * cxlr_pmem->nr_mappings, 0); + nd_set->cookie2 = nd_set->cookie1; + ndr_desc.nd_set = nd_set; + + cxlr_pmem->nd_region = + nvdimm_pmem_region_create(cxl_nvb->nvdimm_bus, &ndr_desc); + if (!cxlr_pmem->nd_region) { + rc = -ENOMEM; + goto err; + } + + rc = devm_add_action_or_reset(dev, unregister_nvdimm_region, + cxlr_pmem->nd_region); +out: + kfree(info); + device_unlock(&cxl_nvb->dev); + put_device(&cxl_nvb->dev); + + return rc; + +err: + dev_dbg(dev, "failed to create nvdimm region\n"); + for (i--; i >= 0; i--) { + nvdimm = mappings[i].nvdimm; + cxl_nvd = nvdimm_provider_data(nvdimm); + put_device(&cxl_nvd->region->dev); + cxl_nvd->region = NULL; + } + goto out; +} + +static struct cxl_driver cxl_pmem_region_driver = { + .name = "cxl_pmem_region", + .probe = cxl_pmem_region_probe, + .id = CXL_DEVICE_PMEM_REGION, +}; + /* * Return all bridges to the CXL_NVB_NEW state to invalidate any * ->state_work referring to the now destroyed cxl_pmem_wq. @@ -359,8 +600,14 @@ static __init int cxl_pmem_init(void) if (rc) goto err_nvdimm; + rc = cxl_driver_register(&cxl_pmem_region_driver); + if (rc) + goto err_region; + return 0; +err_region: + cxl_driver_unregister(&cxl_nvdimm_driver); err_nvdimm: cxl_driver_unregister(&cxl_nvdimm_bridge_driver); err_bridge: @@ -370,6 +617,7 @@ err_bridge: static __exit void cxl_pmem_exit(void) { + cxl_driver_unregister(&cxl_pmem_region_driver); cxl_driver_unregister(&cxl_nvdimm_driver); cxl_driver_unregister(&cxl_nvdimm_bridge_driver); destroy_cxl_pmem_wq(); @@ -381,3 +629,4 @@ module_exit(cxl_pmem_exit); MODULE_IMPORT_NS(CXL); MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM_BRIDGE); MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM); +MODULE_ALIAS_CXL(CXL_DEVICE_PMEM_REGION); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index 3cf308f114c4..5453771bf330 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -53,6 +53,9 @@ static int cxl_port_probe(struct device *dev) struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport); struct cxl_dev_state *cxlds = cxlmd->cxlds; + /* Cache the data early to ensure is_visible() works */ + read_cdat_data(port); + get_device(&cxlmd->dev); rc = devm_add_action_or_reset(dev, schedule_detach, cxlmd); if (rc) @@ -78,10 +81,60 @@ static int cxl_port_probe(struct device *dev) return 0; } +static ssize_t CDAT_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_port *port = to_cxl_port(dev); + + if (!port->cdat_available) + return -ENXIO; + + if (!port->cdat.table) + return 0; + + return memory_read_from_buffer(buf, count, &offset, + port->cdat.table, + port->cdat.length); +} + +static BIN_ATTR_ADMIN_RO(CDAT, 0); + +static umode_t cxl_port_bin_attr_is_visible(struct kobject *kobj, + struct bin_attribute *attr, int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_port *port = to_cxl_port(dev); + + if ((attr == &bin_attr_CDAT) && port->cdat_available) + return attr->attr.mode; + + return 0; +} + +static struct bin_attribute *cxl_cdat_bin_attributes[] = { + &bin_attr_CDAT, + NULL, +}; + +static struct attribute_group cxl_cdat_attribute_group = { + .bin_attrs = cxl_cdat_bin_attributes, + .is_bin_visible = cxl_port_bin_attr_is_visible, +}; + +static const struct attribute_group *cxl_port_attribute_groups[] = { + &cxl_cdat_attribute_group, + NULL, +}; + static struct cxl_driver cxl_port_driver = { .name = "cxl_port", .probe = cxl_port_probe, .id = CXL_DEVICE_PORT, + .drv = { + .dev_groups = cxl_port_attribute_groups, + }, }; module_cxl_driver(cxl_port_driver); diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index f7dcc44f9414..027e8f336acc 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -33,7 +33,7 @@ struct exynos_bus { unsigned long curr_freq; - struct opp_table *opp_table; + int opp_token; struct clk *clk; unsigned int ratio; }; @@ -161,8 +161,7 @@ static void exynos_bus_exit(struct device *dev) dev_pm_opp_of_remove_table(dev); clk_disable_unprepare(bus->clk); - dev_pm_opp_put_regulators(bus->opp_table); - bus->opp_table = NULL; + dev_pm_opp_put_regulators(bus->opp_token); } static void exynos_bus_passive_exit(struct device *dev) @@ -179,18 +178,16 @@ static int exynos_bus_parent_parse_of(struct device_node *np, struct exynos_bus *bus) { struct device *dev = bus->dev; - struct opp_table *opp_table; - const char *vdd = "vdd"; + const char *supplies[] = { "vdd", NULL }; int i, ret, count, size; - opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1); - if (IS_ERR(opp_table)) { - ret = PTR_ERR(opp_table); + ret = dev_pm_opp_set_regulators(dev, supplies); + if (ret < 0) { dev_err(dev, "failed to set regulators %d\n", ret); return ret; } - bus->opp_table = opp_table; + bus->opp_token = ret; /* * Get the devfreq-event devices to get the current utilization of @@ -236,8 +233,7 @@ static int exynos_bus_parent_parse_of(struct device_node *np, return 0; err_regulator: - dev_pm_opp_put_regulators(bus->opp_table); - bus->opp_table = NULL; + dev_pm_opp_put_regulators(bus->opp_token); return ret; } @@ -459,8 +455,7 @@ err: dev_pm_opp_of_remove_table(dev); clk_disable_unprepare(bus->clk); err_reg: - dev_pm_opp_put_regulators(bus->opp_table); - bus->opp_table = NULL; + dev_pm_opp_put_regulators(bus->opp_token); return ret; } diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index 585a95fe2bd6..503376b894b6 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -821,6 +821,15 @@ static int devm_tegra_devfreq_init_hw(struct device *dev, return err; } +static int tegra_devfreq_config_clks_nop(struct device *dev, + struct opp_table *opp_table, + struct dev_pm_opp *opp, void *data, + bool scaling_down) +{ + /* We want to skip clk configuration via dev_pm_opp_set_opp() */ + return 0; +} + static int tegra_devfreq_probe(struct platform_device *pdev) { u32 hw_version = BIT(tegra_sku_info.soc_speedo_id); @@ -830,6 +839,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev) unsigned int i; long rate; int err; + const char *clk_names[] = { "actmon", NULL }; + struct dev_pm_opp_config config = { + .supported_hw = &hw_version, + .supported_hw_count = 1, + .clk_names = clk_names, + .config_clks = tegra_devfreq_config_clks_nop, + }; tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); if (!tegra) @@ -874,13 +890,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return err; } - err = devm_pm_opp_set_supported_hw(&pdev->dev, &hw_version, 1); + err = devm_pm_opp_set_config(&pdev->dev, &config); if (err) { - dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err); + dev_err(&pdev->dev, "Failed to set OPP config: %d\n", err); return err; } - err = devm_pm_opp_of_add_table_noclk(&pdev->dev, 0); + err = devm_pm_opp_of_add_table_indexed(&pdev->dev, 0); if (err) { dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err); return err; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index eb0c2d041f13..86d670c71286 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1226,7 +1226,7 @@ retry: ret = dma_resv_lock_slow_interruptible(obj->resv, acquire_ctx); if (ret) { - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } @@ -1251,7 +1251,7 @@ retry: goto retry; } - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 8ad0e02991ca..904fc893c905 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -302,6 +302,7 @@ static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, ret = dma_buf_vmap(obj->import_attach->dmabuf, map); if (!ret) { if (WARN_ON(map->is_iomem)) { + dma_buf_vunmap(obj->import_attach->dmabuf, map); ret = -EIO; goto err_put_pages; } diff --git a/drivers/gpu/drm/lima/lima_devfreq.c b/drivers/gpu/drm/lima/lima_devfreq.c index 8989e215dfc9..011be7ff51e1 100644 --- a/drivers/gpu/drm/lima/lima_devfreq.c +++ b/drivers/gpu/drm/lima/lima_devfreq.c @@ -111,6 +111,12 @@ int lima_devfreq_init(struct lima_device *ldev) struct dev_pm_opp *opp; unsigned long cur_freq; int ret; + const char *regulator_names[] = { "mali", NULL }; + const char *clk_names[] = { "core", NULL }; + struct dev_pm_opp_config config = { + .regulator_names = regulator_names, + .clk_names = clk_names, + }; if (!device_property_present(dev, "operating-points-v2")) /* Optional, continue without devfreq */ @@ -118,11 +124,7 @@ int lima_devfreq_init(struct lima_device *ldev) spin_lock_init(&ldevfreq->lock); - ret = devm_pm_opp_set_clkname(dev, "core"); - if (ret) - return ret; - - ret = devm_pm_opp_set_regulators(dev, (const char *[]){ "mali" }, 1); + ret = devm_pm_opp_set_config(dev, &config); if (ret) { /* Continue if the optional regulator is missing */ if (ret != -ENODEV) diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 194af7f607a6..5110cd9b2425 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -101,8 +101,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) return 0; } - ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names, - pfdev->comp->num_supplies); + ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names); if (ret) { /* Continue if the optional regulator is missing */ if (ret != -ENODEV) { diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 2d870cf73b07..2fa5afe21288 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -626,24 +626,29 @@ static int panfrost_remove(struct platform_device *pdev) return 0; } -static const char * const default_supplies[] = { "mali" }; +/* + * The OPP core wants the supply names to be NULL terminated, but we need the + * correct num_supplies value for regulator core. Hence, we NULL terminate here + * and then initialize num_supplies with ARRAY_SIZE - 1. + */ +static const char * const default_supplies[] = { "mali", NULL }; static const struct panfrost_compatible default_data = { - .num_supplies = ARRAY_SIZE(default_supplies), + .num_supplies = ARRAY_SIZE(default_supplies) - 1, .supply_names = default_supplies, .num_pm_domains = 1, /* optional */ .pm_domain_names = NULL, }; static const struct panfrost_compatible amlogic_data = { - .num_supplies = ARRAY_SIZE(default_supplies), + .num_supplies = ARRAY_SIZE(default_supplies) - 1, .supply_names = default_supplies, .vendor_quirk = panfrost_gpu_amlogic_quirk, }; -static const char * const mediatek_mt8183_supplies[] = { "mali", "sram" }; +static const char * const mediatek_mt8183_supplies[] = { "mali", "sram", NULL }; static const char * const mediatek_mt8183_pm_domains[] = { "core0", "core1", "core2" }; static const struct panfrost_compatible mediatek_mt8183_data = { - .num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies), + .num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies) - 1, .supply_names = mediatek_mt8183_supplies, .num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains), .pm_domain_names = mediatek_mt8183_pm_domains, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 03d07da8c2dc..221de01a327a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2321,7 +2321,7 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id, const char *name = NULL; if (config2 < 0) - return ERR_PTR(-ENODEV); + return NULL; if (address == 0x4c && !(config1 & 0x2a) && !(config2 & 0xf8)) { if (chip_id == 0x01 && convrate <= 0x09) { diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 446964cbae4c..da9ec6983e13 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -1480,7 +1480,7 @@ static int nct6775_update_pwm_limits(struct device *dev) return 0; } -static struct nct6775_data *nct6775_update_device(struct device *dev) +struct nct6775_data *nct6775_update_device(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); int i, j, err = 0; @@ -1615,6 +1615,7 @@ out: mutex_unlock(&data->update_lock); return err ? ERR_PTR(err) : data; } +EXPORT_SYMBOL_GPL(nct6775_update_device); /* * Sysfs callback functions diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index ab30437221ce..41c97cfacfb8 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -359,7 +359,7 @@ static int __maybe_unused nct6775_suspend(struct device *dev) { int err; u16 tmp; - struct nct6775_data *data = dev_get_drvdata(dev); + struct nct6775_data *data = nct6775_update_device(dev); if (IS_ERR(data)) return PTR_ERR(data); diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h index 93f708148e65..be41848c3cd2 100644 --- a/drivers/hwmon/nct6775.h +++ b/drivers/hwmon/nct6775.h @@ -196,6 +196,8 @@ static inline int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 va return regmap_write(data->regmap, reg, value); } +struct nct6775_data *nct6775_update_device(struct device *dev); + bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg); int nct6775_probe(struct device *dev, struct nct6775_data *data, const struct regmap_config *regmapcfg); diff --git a/drivers/input/input-core-private.h b/drivers/input/input-core-private.h new file mode 100644 index 000000000000..116834cf8868 --- /dev/null +++ b/drivers/input/input-core-private.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _INPUT_CORE_PRIVATE_H +#define _INPUT_CORE_PRIVATE_H + +/* + * Functions and definitions that are private to input core, + * should not be used by input drivers or handlers. + */ + +struct input_dev; + +void input_mt_release_slots(struct input_dev *dev); +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value); + +#endif /* _INPUT_CORE_PRIVATE_H */ diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 44fe6f2f063c..14b53dac1253 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -8,6 +8,7 @@ #include <linux/input/mt.h> #include <linux/export.h> #include <linux/slab.h> +#include "input-core-private.h" #define TRKID_SGN ((TRKID_MAX + 1) >> 1) @@ -259,10 +260,13 @@ static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt) { int i; + lockdep_assert_held(&dev->event_lock); + for (i = 0; i < mt->num_slots; i++) { - if (!input_mt_is_used(mt, &mt->slots[i])) { - input_mt_slot(dev, i); - input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + if (input_mt_is_active(&mt->slots[i]) && + !input_mt_is_used(mt, &mt->slots[i])) { + input_handle_event(dev, EV_ABS, ABS_MT_SLOT, i); + input_handle_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); } } } @@ -278,13 +282,44 @@ void input_mt_drop_unused(struct input_dev *dev) struct input_mt *mt = dev->mt; if (mt) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + __input_mt_drop_unused(dev, mt); mt->frame++; + + spin_unlock_irqrestore(&dev->event_lock, flags); } } EXPORT_SYMBOL(input_mt_drop_unused); /** + * input_mt_release_slots() - Deactivate all slots + * @dev: input device with allocated MT slots + * + * Lift all active slots. + */ +void input_mt_release_slots(struct input_dev *dev) +{ + struct input_mt *mt = dev->mt; + + lockdep_assert_held(&dev->event_lock); + + if (mt) { + /* This will effectively mark all slots unused. */ + mt->frame++; + + __input_mt_drop_unused(dev, mt); + + if (test_bit(ABS_PRESSURE, dev->absbit)) + input_handle_event(dev, EV_ABS, ABS_PRESSURE, 0); + + mt->frame++; + } +} + +/** * input_mt_sync_frame() - synchronize mt frame * @dev: input device with allocated MT slots * @@ -300,8 +335,13 @@ void input_mt_sync_frame(struct input_dev *dev) if (!mt) return; - if (mt->flags & INPUT_MT_DROP_UNUSED) + if (mt->flags & INPUT_MT_DROP_UNUSED) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); __input_mt_drop_unused(dev, mt); + spin_unlock_irqrestore(&dev->event_lock, flags); + } if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT)) use_count = true; diff --git a/drivers/input/input.c b/drivers/input/input.c index 1365c9dfb5f2..ebb2b7f0f8ff 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -24,6 +24,7 @@ #include <linux/mutex.h> #include <linux/rcupdate.h> #include "input-compat.h" +#include "input-core-private.h" #include "input-poller.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); @@ -142,6 +143,8 @@ static void input_pass_values(struct input_dev *dev, struct input_handle *handle; struct input_value *v; + lockdep_assert_held(&dev->event_lock); + if (!count) return; @@ -174,44 +177,6 @@ static void input_pass_values(struct input_dev *dev, } } -static void input_pass_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - struct input_value vals[] = { { type, code, value } }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); -} - -/* - * Generate software autorepeat event. Note that we take - * dev->event_lock here to avoid racing with input_event - * which may cause keys get "stuck". - */ -static void input_repeat_key(struct timer_list *t) -{ - struct input_dev *dev = from_timer(dev, t, timer); - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - if (test_bit(dev->repeat_key, dev->key) && - is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { - struct input_value vals[] = { - { EV_KEY, dev->repeat_key, 2 }, - input_value_sync - }; - - input_set_timestamp(dev, ktime_get()); - input_pass_values(dev, vals, ARRAY_SIZE(vals)); - - if (dev->rep[REP_PERIOD]) - mod_timer(&dev->timer, jiffies + - msecs_to_jiffies(dev->rep[REP_PERIOD])); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - #define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 @@ -275,6 +240,10 @@ static int input_get_disposition(struct input_dev *dev, int disposition = INPUT_IGNORE_EVENT; int value = *pval; + /* filter-out events from inhibited devices */ + if (dev->inhibited) + return INPUT_IGNORE_EVENT; + switch (type) { case EV_SYN: @@ -375,19 +344,9 @@ static int input_get_disposition(struct input_dev *dev, return disposition; } -static void input_handle_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) +static void input_event_dispose(struct input_dev *dev, int disposition, + unsigned int type, unsigned int code, int value) { - int disposition; - - /* filter-out events from inhibited devices */ - if (dev->inhibited) - return; - - disposition = input_get_disposition(dev, type, code, &value); - if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) - add_input_randomness(type, code, value); - if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); @@ -426,7 +385,22 @@ static void input_handle_event(struct input_dev *dev, input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } +} +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + int disposition; + + lockdep_assert_held(&dev->event_lock); + + disposition = input_get_disposition(dev, type, code, &value); + if (disposition != INPUT_IGNORE_EVENT) { + if (type != EV_SYN) + add_input_randomness(type, code, value); + + input_event_dispose(dev, disposition, type, code, value); + } } /** @@ -613,7 +587,7 @@ static void __input_release_device(struct input_handle *handle) lockdep_is_held(&dev->mutex)); if (grabber == handle) { rcu_assign_pointer(dev->grab, NULL); - /* Make sure input_pass_event() notices that grab is gone */ + /* Make sure input_pass_values() notices that grab is gone */ synchronize_rcu(); list_for_each_entry(handle, &dev->h_list, d_node) @@ -736,7 +710,7 @@ void input_close_device(struct input_handle *handle) if (!--handle->open) { /* - * synchronize_rcu() makes sure that input_pass_event() + * synchronize_rcu() makes sure that input_pass_values() * completed and that no more input events are delivered * through this handle */ @@ -751,22 +725,21 @@ EXPORT_SYMBOL(input_close_device); * Simulate keyup events for all keys that are marked as pressed. * The function must be called with dev->event_lock held. */ -static void input_dev_release_keys(struct input_dev *dev) +static bool input_dev_release_keys(struct input_dev *dev) { bool need_sync = false; int code; + lockdep_assert_held(&dev->event_lock); + if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { for_each_set_bit(code, dev->key, KEY_CNT) { - input_pass_event(dev, EV_KEY, code, 0); + input_handle_event(dev, EV_KEY, code, 0); need_sync = true; } - - if (need_sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); - - memset(dev->key, 0, sizeof(dev->key)); } + + return need_sync; } /* @@ -793,7 +766,8 @@ static void input_disconnect_device(struct input_dev *dev) * generate events even after we done here but they will not * reach any handlers. */ - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); list_for_each_entry(handle, &dev->h_list, d_node) handle->open = 0; @@ -1004,12 +978,16 @@ int input_set_keycode(struct input_dev *dev, } else if (test_bit(EV_KEY, dev->evbit) && !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __test_and_clear_bit(old_keycode, dev->key)) { - struct input_value vals[] = { - { EV_KEY, old_keycode, 0 }, - input_value_sync - }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); + /* + * We have to use input_event_dispose() here directly instead + * of input_handle_event() because the key we want to release + * here is considered no longer supported by the device and + * input_handle_event() will ignore it. + */ + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS, + EV_KEY, old_keycode, 0); + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS | INPUT_FLUSH, + EV_SYN, SYN_REPORT, 1); } out: @@ -1784,7 +1762,8 @@ void input_reset_device(struct input_dev *dev) spin_lock_irqsave(&dev->event_lock, flags); input_dev_toggle(dev, true); - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irqrestore(&dev->event_lock, flags); mutex_unlock(&dev->mutex); @@ -1806,7 +1785,9 @@ static int input_inhibit_device(struct input_dev *dev) } spin_lock_irq(&dev->event_lock); + input_mt_release_slots(dev); input_dev_release_keys(dev); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); input_dev_toggle(dev, false); spin_unlock_irq(&dev->event_lock); @@ -1857,7 +1838,8 @@ static int input_dev_suspend(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); /* Turn off LEDs and sounds, if any are active. */ input_dev_toggle(input_dev, false); @@ -1891,7 +1873,8 @@ static int input_dev_freeze(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irq(&input_dev->event_lock); @@ -2259,6 +2242,34 @@ static void devm_input_device_unregister(struct device *dev, void *res) __input_unregister_device(input); } +/* + * Generate software autorepeat event. Note that we take + * dev->event_lock here to avoid racing with input_event + * which may cause keys get "stuck". + */ +static void input_repeat_key(struct timer_list *t) +{ + struct input_dev *dev = from_timer(dev, t, timer); + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (!dev->inhibited && + test_bit(dev->repeat_key, dev->key) && + is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { + + input_set_timestamp(dev, ktime_get()); + input_handle_event(dev, EV_KEY, dev->repeat_key, 2); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); + + if (dev->rep[REP_PERIOD]) + mod_timer(&dev->timer, jiffies + + msecs_to_jiffies(dev->rep[REP_PERIOD])); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + /** * input_enable_softrepeat - enable software autorepeat * @dev: input device diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index 78ebca7d400a..e0cfdc84763f 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -222,13 +222,6 @@ static int adc_joystick_probe(struct platform_device *pdev) if (error) return error; - input_set_drvdata(input, joy); - error = input_register_device(input); - if (error) { - dev_err(dev, "Unable to register input device\n"); - return error; - } - joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy); if (IS_ERR(joy->buffer)) { dev_err(dev, "Unable to allocate callback buffer\n"); @@ -241,6 +234,14 @@ static int adc_joystick_probe(struct platform_device *pdev) return error; } + input_set_drvdata(input, joy); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device\n"); + return error; + } + return 0; } diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c index 5ad1fe4ff496..a84df39d3b2f 100644 --- a/drivers/input/joystick/sensehat-joystick.c +++ b/drivers/input/joystick/sensehat-joystick.c @@ -98,10 +98,8 @@ static int sensehat_joystick_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Could not retrieve interrupt request"); + if (irq < 0) return irq; - } error = devm_request_threaded_irq(&pdev->dev, irq, NULL, sensehat_joystick_report, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 4ea79db8f134..a20ee693b22b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -795,7 +795,7 @@ config KEYBOARD_MT6779 config KEYBOARD_MTK_PMIC tristate "MediaTek PMIC keys support" - depends on MFD_MT6397 + depends on MFD_MT6397 || COMPILE_TEST help Say Y here if you want to use the pmic keys (powerkey/homekey). diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 1592da4de336..1a1a05d7cd42 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -8,17 +8,19 @@ * Copyright (C) 2008-2010 Analog Devices Inc. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/workqueue.h> -#include <linux/errno.h> -#include <linux/pm.h> +#include <linux/ktime.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/input.h> -#include <linux/i2c.h> -#include <linux/gpio/driver.h> +#include <linux/pm.h> #include <linux/slab.h> +#include <linux/timekeeping.h> #include <linux/platform_data/adp5588.h> @@ -36,18 +38,18 @@ * asserted. */ #define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) +#define WA_DELAYED_READOUT_TIME 25 struct adp5588_kpad { struct i2c_client *client; struct input_dev *input; - struct delayed_work work; + ktime_t irq_time; unsigned long delay; unsigned short keycode[ADP5588_KEYMAPSIZE]; const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; #ifdef CONFIG_GPIOLIB unsigned char gpiomap[ADP5588_MAXGPIO]; - bool export_gpio; struct gpio_chip gc; struct mutex gpio_lock; /* Protect cached dir, dat_out */ u8 dat_out[3]; @@ -179,6 +181,21 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad, return n_unused; } +static void adp5588_gpio_do_teardown(void *_kpad) +{ + struct adp5588_kpad *kpad = _kpad; + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(&kpad->client->dev, "teardown failed %d\n", error); +} + static int adp5588_gpio_add(struct adp5588_kpad *kpad) { struct device *dev = &kpad->client->dev; @@ -195,8 +212,6 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) return 0; } - kpad->export_gpio = true; - kpad->gc.direction_input = adp5588_gpio_direction_input; kpad->gc.direction_output = adp5588_gpio_direction_output; kpad->gc.get = adp5588_gpio_get_value; @@ -210,9 +225,9 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) mutex_init(&kpad->gpio_lock); - error = gpiochip_add_data(&kpad->gc, kpad); + error = devm_gpiochip_add_data(dev, &kpad->gc, kpad); if (error) { - dev_err(dev, "gpiochip_add failed, err: %d\n", error); + dev_err(dev, "gpiochip_add failed: %d\n", error); return error; } @@ -227,41 +242,24 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) kpad->gc.base, kpad->gc.ngpio, gpio_data->context); if (error) - dev_warn(dev, "setup failed, %d\n", error); + dev_warn(dev, "setup failed: %d\n", error); } - return 0; -} - -static void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ - struct device *dev = &kpad->client->dev; - const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); - const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; - int error; - - if (!kpad->export_gpio) - return; - if (gpio_data->teardown) { - error = gpio_data->teardown(kpad->client, - kpad->gc.base, kpad->gc.ngpio, - gpio_data->context); + error = devm_add_action(dev, adp5588_gpio_do_teardown, kpad); if (error) - dev_warn(dev, "teardown failed %d\n", error); + dev_warn(dev, "failed to schedule teardown: %d\n", + error); } - gpiochip_remove(&kpad->gc); + return 0; } + #else static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) { return 0; } - -static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ -} #endif static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) @@ -289,13 +287,36 @@ static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) } } -static void adp5588_work(struct work_struct *work) +static irqreturn_t adp5588_hard_irq(int irq, void *handle) { - struct adp5588_kpad *kpad = container_of(work, - struct adp5588_kpad, work.work); + struct adp5588_kpad *kpad = handle; + + kpad->irq_time = ktime_get(); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t adp5588_thread_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; struct i2c_client *client = kpad->client; + ktime_t target_time, now; + unsigned long delay; int status, ev_cnt; + /* + * Readout needs to wait for at least 25ms after the notification + * for REVID < 4. + */ + if (kpad->delay) { + target_time = ktime_add_ms(kpad->irq_time, kpad->delay); + now = ktime_get(); + if (ktime_before(now, target_time)) { + delay = ktime_to_us(ktime_sub(target_time, now)); + usleep_range(delay, delay + 1000); + } + } + status = adp5588_read(client, INT_STAT); if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ @@ -308,20 +329,8 @@ static void adp5588_work(struct work_struct *work) input_sync(kpad->input); } } - adp5588_write(client, INT_STAT, status); /* Status is W1C */ -} - -static irqreturn_t adp5588_irq(int irq, void *handle) -{ - struct adp5588_kpad *kpad = handle; - /* - * use keventd context to read the event fifo registers - * Schedule readout at least 25ms after notification for - * REVID < 4 - */ - - schedule_delayed_work(&kpad->work, kpad->delay); + adp5588_write(client, INT_STAT, status); /* Status is W1C */ return IRQ_HANDLED; } @@ -496,30 +505,27 @@ static int adp5588_probe(struct i2c_client *client, return -EINVAL; } - kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); - input = input_allocate_device(); - if (!kpad || !input) { - error = -ENOMEM; - goto err_free_mem; - } + kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; kpad->client = client; kpad->input = input; - INIT_DELAYED_WORK(&kpad->work, adp5588_work); ret = adp5588_read(client, DEV_ID); - if (ret < 0) { - error = ret; - goto err_free_mem; - } + if (ret < 0) + return ret; revid = (u8) ret & ADP5588_DEVICE_ID_MASK; if (WA_DELAYED_READOUT_REVID(revid)) - kpad->delay = msecs_to_jiffies(30); + kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME); input->name = client->name; input->phys = "adp5588-keys/input0"; - input->dev.parent = &client->dev; input_set_drvdata(input, kpad); @@ -556,95 +562,63 @@ static int adp5588_probe(struct i2c_client *client, error = input_register_device(input); if (error) { - dev_err(&client->dev, "unable to register input device\n"); - goto err_free_mem; + dev_err(&client->dev, "unable to register input device: %d\n", + error); + return error; } - error = request_irq(client->irq, adp5588_irq, - IRQF_TRIGGER_FALLING, - client->dev.driver->name, kpad); + error = devm_request_threaded_irq(&client->dev, client->irq, + adp5588_hard_irq, adp5588_thread_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); if (error) { - dev_err(&client->dev, "irq %d busy?\n", client->irq); - goto err_unreg_dev; + dev_err(&client->dev, "failed to request irq %d: %d\n", + client->irq, error); + return error; } error = adp5588_setup(client); if (error) - goto err_free_irq; + return error; if (kpad->gpimapsize) adp5588_report_switch_state(kpad); error = adp5588_gpio_add(kpad); if (error) - goto err_free_irq; - - device_init_wakeup(&client->dev, 1); - i2c_set_clientdata(client, kpad); + return error; dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); return 0; - - err_free_irq: - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - err_unreg_dev: - input_unregister_device(input); - input = NULL; - err_free_mem: - input_free_device(input); - kfree(kpad); - - return error; } static int adp5588_remove(struct i2c_client *client) { - struct adp5588_kpad *kpad = i2c_get_clientdata(client); - adp5588_write(client, CFG, 0); - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - input_unregister_device(kpad->input); - adp5588_gpio_remove(kpad); - kfree(kpad); + /* all resources will be freed by devm */ return 0; } -#ifdef CONFIG_PM -static int adp5588_suspend(struct device *dev) +static int __maybe_unused adp5588_suspend(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; + struct i2c_client *client = to_i2c_client(dev); disable_irq(client->irq); - cancel_delayed_work_sync(&kpad->work); - - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); return 0; } -static int adp5588_resume(struct device *dev) +static int __maybe_unused adp5588_resume(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + struct i2c_client *client = to_i2c_client(dev); enable_irq(client->irq); return 0; } -static const struct dev_pm_ops adp5588_dev_pm_ops = { - .suspend = adp5588_suspend, - .resume = adp5588_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume); static const struct i2c_device_id adp5588_id[] = { { "adp5588-keys", 0 }, @@ -656,9 +630,7 @@ MODULE_DEVICE_TABLE(i2c, adp5588_id); static struct i2c_driver adp5588_driver = { .driver = { .name = KBUILD_MODNAME, -#ifdef CONFIG_PM .pm = &adp5588_dev_pm_ops, -#endif }, .probe = adp5588_probe, .remove = adp5588_remove, diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index cc73a149da28..c14136b733a9 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -12,6 +12,7 @@ // expensive. #include <linux/module.h> +#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/input.h> @@ -518,6 +519,50 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev, return 0; } +static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) +{ + u32 *physmap = ckdev->vdata.function_row_physmap; + unsigned int row, col, scancode; + int n_physmap; + int error; + int i; + + n_physmap = device_property_count_u32(ckdev->dev, + "function-row-physmap"); + if (n_physmap <= 0) + return; + + if (n_physmap >= VIVALDI_MAX_FUNCTION_ROW_KEYS) { + dev_warn(ckdev->dev, + "only up to %d top row keys is supported (%d specified)\n", + VIVALDI_MAX_FUNCTION_ROW_KEYS, n_physmap); + n_physmap = VIVALDI_MAX_FUNCTION_ROW_KEYS; + } + + error = device_property_read_u32_array(ckdev->dev, + "function-row-physmap", + physmap, n_physmap); + if (error) { + dev_warn(ckdev->dev, + "failed to parse function-row-physmap property: %d\n", + error); + return; + } + + /* + * Convert (in place) from row/column encoding to matrix "scancode" + * used by the driver. + */ + for (i = 0; i < n_physmap; i++) { + row = KEY_ROW(physmap[i]); + col = KEY_COL(physmap[i]); + scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap[i] = scancode; + } + + ckdev->vdata.num_function_row_keys = n_physmap; +} + /** * cros_ec_keyb_register_matrix - Register matrix keys * @@ -534,11 +579,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; - struct property *prop; - const __be32 *p; - u32 *physmap; - u32 key_pos; - unsigned int row, col, scancode, n_physmap; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -573,7 +613,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.product = 0; idev->dev.parent = dev; - ckdev->ghost_filter = of_property_read_bool(dev->of_node, + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, @@ -589,22 +629,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) input_set_drvdata(idev, ckdev); ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); - - physmap = ckdev->vdata.function_row_physmap; - n_physmap = 0; - of_property_for_each_u32(dev->of_node, "function-row-physmap", - prop, p, key_pos) { - if (n_physmap == VIVALDI_MAX_FUNCTION_ROW_KEYS) { - dev_warn(dev, "Only support up to %d top row keys\n", - VIVALDI_MAX_FUNCTION_ROW_KEYS); - break; - } - row = KEY_ROW(key_pos); - col = KEY_COL(key_pos); - scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - physmap[n_physmap++] = scancode; - } - ckdev->vdata.num_function_row_keys = n_physmap; + cros_ec_keyb_parse_vivaldi_physmap(ckdev); err = input_register_device(ckdev->idev); if (err) { @@ -653,14 +678,19 @@ static const struct attribute_group cros_ec_keyb_attr_group = { static int cros_ec_keyb_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *ec; struct device *dev = &pdev->dev; struct cros_ec_keyb *ckdev; bool buttons_switches_only = device_get_match_data(dev); int err; - if (!dev->of_node) - return -ENODEV; + /* + * If the parent ec device has not been probed yet, defer the probe of + * this keyboard/button driver until later. + */ + ec = dev_get_drvdata(pdev->dev.parent); + if (!ec) + return -EPROBE_DEFER; ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); if (!ckdev) @@ -713,6 +743,14 @@ static int cros_ec_keyb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_keyb_acpi_match[] = { + { "GOOG0007", true }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_keyb_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id cros_ec_keyb_of_match[] = { { .compatible = "google,cros-ec-keyb" }, @@ -730,6 +768,7 @@ static struct platform_driver cros_ec_keyb_driver = { .driver = { .name = "cros-ec-keyb", .of_match_table = of_match_ptr(cros_ec_keyb_of_match), + .acpi_match_table = ACPI_PTR(cros_ec_keyb_acpi_match), .pm = &cros_ec_keyb_pm_ops, }, }; diff --git a/drivers/input/keyboard/mt6779-keypad.c b/drivers/input/keyboard/mt6779-keypad.c index 2e7c9187c10f..bf447bf598fb 100644 --- a/drivers/input/keyboard/mt6779-keypad.c +++ b/drivers/input/keyboard/mt6779-keypad.c @@ -17,6 +17,11 @@ #define MTK_KPD_DEBOUNCE 0x0018 #define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0) #define MTK_KPD_DEBOUNCE_MAX_MS 256 +#define MTK_KPD_SEL 0x0020 +#define MTK_KPD_SEL_COL GENMASK(15, 10) +#define MTK_KPD_SEL_ROW GENMASK(9, 4) +#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10) +#define MTK_KPD_SEL_ROWMASK(r) GENMASK((r) + 3, 4) #define MTK_KPD_NUM_MEMS 5 #define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */ @@ -42,7 +47,7 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) const unsigned short *keycode = keypad->input_dev->keycode; DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS); DECLARE_BITMAP(change, MTK_KPD_NUM_BITS); - unsigned int bit_nr; + unsigned int bit_nr, key; unsigned int row, col; unsigned int scancode; unsigned int row_shift = get_count_order(keypad->n_cols); @@ -61,8 +66,10 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) if (bit_nr % 32 >= 16) continue; - row = bit_nr / 32; - col = bit_nr % 32; + key = bit_nr / 32 * 16 + bit_nr % 32; + row = key / 9; + col = key % 9; + scancode = MATRIX_SCAN_CODE(row, col, row_shift); /* 1: not pressed, 0: pressed */ pressed = !test_bit(bit_nr, new_state); @@ -159,6 +166,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev) regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE, (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW, + MTK_KPD_SEL_ROWMASK(keypad->n_rows)); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL, + MTK_KPD_SEL_COLMASK(keypad->n_cols)); + keypad->clk = devm_clk_get(&pdev->dev, "kpd"); if (IS_ERR(keypad->clk)) return PTR_ERR(keypad->clk); diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index c31ab4368388..6404081253ea 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -18,17 +18,9 @@ #include <linux/platform_device.h> #include <linux/regmap.h> -#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6 -#define MTK_PMIC_HOMEKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_HOMEKEY_RST_EN_SHIFT 5 -#define MTK_PMIC_RST_DU_MASK 0x3 -#define MTK_PMIC_RST_DU_SHIFT 8 - -#define MTK_PMIC_PWRKEY_RST \ - (MTK_PMIC_PWRKEY_RST_EN_MASK << MTK_PMIC_PWRKEY_RST_EN_SHIFT) -#define MTK_PMIC_HOMEKEY_RST \ - (MTK_PMIC_HOMEKEY_RST_EN_MASK << MTK_PMIC_HOMEKEY_RST_EN_SHIFT) +#define MTK_PMIC_RST_DU_MASK GENMASK(9, 8) +#define MTK_PMIC_PWRKEY_RST BIT(6) +#define MTK_PMIC_HOMEKEY_RST BIT(5) #define MTK_PMIC_PWRKEY_INDEX 0 #define MTK_PMIC_HOMEKEY_INDEX 1 @@ -39,50 +31,58 @@ struct mtk_pmic_keys_regs { u32 deb_mask; u32 intsel_reg; u32 intsel_mask; + u32 rst_en_mask; }; #define MTK_PMIC_KEYS_REGS(_deb_reg, _deb_mask, \ - _intsel_reg, _intsel_mask) \ + _intsel_reg, _intsel_mask, _rst_mask) \ { \ .deb_reg = _deb_reg, \ .deb_mask = _deb_mask, \ .intsel_reg = _intsel_reg, \ .intsel_mask = _intsel_mask, \ + .rst_en_mask = _rst_mask, \ } struct mtk_pmic_regs { const struct mtk_pmic_keys_regs keys_regs[MTK_PMIC_MAX_KEY_COUNT]; u32 pmic_rst_reg; + u32 rst_lprst_mask; /* Long-press reset timeout bitmask */ }; static const struct mtk_pmic_regs mt6397_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_CHRSTATUS, - 0x8, MT6397_INT_RSV, 0x10), + 0x8, MT6397_INT_RSV, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_OCSTATUS2, - 0x10, MT6397_INT_RSV, 0x8), + 0x10, MT6397_INT_RSV, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6397_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6323_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x2, MT6323_INT_MISC_CON, 0x10), + 0x2, MT6323_INT_MISC_CON, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x4, MT6323_INT_MISC_CON, 0x8), + 0x4, MT6323_INT_MISC_CON, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6323_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6358_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x2, MT6358_PSC_TOP_INT_CON0, 0x5), + 0x2, MT6358_PSC_TOP_INT_CON0, 0x5, + MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x8, MT6358_PSC_TOP_INT_CON0, 0xa), + 0x8, MT6358_PSC_TOP_INT_CON0, 0xa, + MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6358_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; struct mtk_pmic_keys_info { @@ -108,53 +108,49 @@ enum mtk_pmic_keys_lp_mode { }; static void mtk_pmic_keys_lp_reset_setup(struct mtk_pmic_keys *keys, - u32 pmic_rst_reg) + const struct mtk_pmic_regs *regs) { - int ret; + const struct mtk_pmic_keys_regs *kregs_home, *kregs_pwr; u32 long_press_mode, long_press_debounce; + u32 value, mask; + int error; + + kregs_home = keys->keys[MTK_PMIC_HOMEKEY_INDEX].regs; + kregs_pwr = keys->keys[MTK_PMIC_PWRKEY_INDEX].regs; - ret = of_property_read_u32(keys->dev->of_node, - "power-off-time-sec", &long_press_debounce); - if (ret) + error = of_property_read_u32(keys->dev->of_node, "power-off-time-sec", + &long_press_debounce); + if (error) long_press_debounce = 0; - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_RST_DU_MASK << MTK_PMIC_RST_DU_SHIFT, - long_press_debounce << MTK_PMIC_RST_DU_SHIFT); + mask = regs->rst_lprst_mask; + value = long_press_debounce << (ffs(regs->rst_lprst_mask) - 1); - ret = of_property_read_u32(keys->dev->of_node, - "mediatek,long-press-mode", &long_press_mode); - if (ret) + error = of_property_read_u32(keys->dev->of_node, + "mediatek,long-press-mode", + &long_press_mode); + if (error) long_press_mode = LP_DISABLE; switch (long_press_mode) { - case LP_ONEKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); - break; case LP_TWOKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - MTK_PMIC_HOMEKEY_RST); - break; + value |= kregs_home->rst_en_mask; + fallthrough; + + case LP_ONEKEY: + value |= kregs_pwr->rst_en_mask; + fallthrough; + case LP_DISABLE: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - 0); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); + mask |= kregs_home->rst_en_mask; + mask |= kregs_pwr->rst_en_mask; break; + default: break; } + + regmap_update_bits(keys->regmap, regs->pmic_rst_reg, mask, value); } static irqreturn_t mtk_pmic_keys_irq_handler_thread(int irq, void *data) @@ -358,7 +354,7 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) return error; } - mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs->pmic_rst_reg); + mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs); platform_set_drvdata(pdev, keys); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 8a7ce41b8c56..ee9d04a3f0d5 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -179,11 +179,9 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) int error; u64 keys; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return IRQ_NONE; - } low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); @@ -207,11 +205,9 @@ static int omap4_keypad_open(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return error; - } disable_irq(keypad_data->irq); @@ -254,9 +250,10 @@ static void omap4_keypad_close(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) + dev_err(dev, "%s: pm_runtime_resume_and_get() failed: %d\n", + __func__, error); disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); @@ -392,10 +389,9 @@ static int omap4_keypad_probe(struct platform_device *pdev) * Enable clocks for the keypad module so that we can read * revision register. */ - error = pm_runtime_get_sync(dev); - if (error < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) { + dev_err(dev, "pm_runtime_resume_and_get() failed\n"); return error; } diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index 6b4138771a3f..b2e8097a2e6d 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -40,7 +40,6 @@ #define IQS7222_SLDR_SETUP_2_RES_MASK GENMASK(15, 8) #define IQS7222_SLDR_SETUP_2_RES_SHIFT 8 #define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK GENMASK(7, 0) -#define IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK GENMASK(9, 0) #define IQS7222_GPIO_SETUP_0_GPIO_EN BIT(0) @@ -54,6 +53,9 @@ #define IQS7222_SYS_SETUP_ACK_RESET BIT(0) #define IQS7222_EVENT_MASK_ATI BIT(12) +#define IQS7222_EVENT_MASK_SLDR BIT(10) +#define IQS7222_EVENT_MASK_TOUCH BIT(1) +#define IQS7222_EVENT_MASK_PROX BIT(0) #define IQS7222_COMMS_HOLD BIT(0) #define IQS7222_COMMS_ERROR 0xEEEE @@ -92,11 +94,11 @@ enum iqs7222_reg_key_id { enum iqs7222_reg_grp_id { IQS7222_REG_GRP_STAT, + IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_CYCLE, IQS7222_REG_GRP_GLBL, IQS7222_REG_GRP_BTN, IQS7222_REG_GRP_CHAN, - IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_SLDR, IQS7222_REG_GRP_GPIO, IQS7222_REG_GRP_SYS, @@ -135,12 +137,12 @@ struct iqs7222_event_desc { static const struct iqs7222_event_desc iqs7222_kp_events[] = { { .name = "event-prox", - .enable = BIT(0), + .enable = IQS7222_EVENT_MASK_PROX, .reg_key = IQS7222_REG_KEY_PROX, }, { .name = "event-touch", - .enable = BIT(1), + .enable = IQS7222_EVENT_MASK_TOUCH, .reg_key = IQS7222_REG_KEY_TOUCH, }, }; @@ -556,13 +558,6 @@ static const struct iqs7222_prop_desc iqs7222_props[] = { .label = "current reference trim", }, { - .name = "azoteq,rf-filt-enable", - .reg_grp = IQS7222_REG_GRP_GLBL, - .reg_offset = 0, - .reg_shift = 15, - .reg_width = 1, - }, - { .name = "azoteq,max-counts", .reg_grp = IQS7222_REG_GRP_GLBL, .reg_offset = 0, @@ -1272,9 +1267,22 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) struct i2c_client *client = iqs7222->client; ktime_t ati_timeout; u16 sys_status = 0; - u16 sys_setup = iqs7222->sys_setup[0] & ~IQS7222_SYS_SETUP_ACK_RESET; + u16 sys_setup; int error, i; + /* + * The reserved fields of the system setup register may have changed + * as a result of other registers having been written. As such, read + * the register's latest value to avoid unexpected behavior when the + * register is written in the loop that follows. + */ + error = iqs7222_read_word(iqs7222, IQS7222_SYS_SETUP, &sys_setup); + if (error) + return error; + + sys_setup &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; + sys_setup &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; + for (i = 0; i < IQS7222_NUM_RETRIES; i++) { /* * Trigger ATI from streaming and normal-power modes so that @@ -1299,12 +1307,15 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) if (error) return error; - if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) - continue; + if (sys_status & IQS7222_SYS_STATUS_RESET) + return 0; if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR) break; + if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) + continue; + /* * Use stream-in-touch mode if either slider reports * absolute position. @@ -1321,7 +1332,7 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) dev_err(&client->dev, "ATI attempt %d of %d failed with status 0x%02X, %s\n", i + 1, IQS7222_NUM_RETRIES, (u8)sys_status, - i < IQS7222_NUM_RETRIES ? "retrying..." : "stopping"); + i + 1 < IQS7222_NUM_RETRIES ? "retrying" : "stopping"); } return -ETIMEDOUT; @@ -1334,6 +1345,34 @@ static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir) int error, i, j, k; /* + * Acknowledge reset before writing any registers in case the device + * suffers a spurious reset during initialization. Because this step + * may change the reserved fields of the second filter beta register, + * its cache must be updated. + * + * Writing the second filter beta register, in turn, may clobber the + * system status register. As such, the filter beta register pair is + * written first to protect against this hazard. + */ + if (dir == WRITE) { + u16 reg = dev_desc->reg_grps[IQS7222_REG_GRP_FILT].base + 1; + u16 filt_setup; + + error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP, + iqs7222->sys_setup[0] | + IQS7222_SYS_SETUP_ACK_RESET); + if (error) + return error; + + error = iqs7222_read_word(iqs7222, reg, &filt_setup); + if (error) + return error; + + iqs7222->filt_setup[1] &= GENMASK(7, 0); + iqs7222->filt_setup[1] |= (filt_setup & ~GENMASK(7, 0)); + } + + /* * Take advantage of the stop-bit disable function, if available, to * save the trouble of having to reopen a communication window after * each burst read or write. @@ -1957,8 +1996,8 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row; int ext_chan = rounddown(num_chan, 10); int count, error, reg_offset, i; + u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset]; u16 *sldr_setup = iqs7222->sldr_setup[sldr_index]; - u16 *sys_setup = iqs7222->sys_setup; unsigned int chan_sel[4], val; error = iqs7222_parse_props(iqs7222, &sldr_node, sldr_index, @@ -2003,7 +2042,7 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1; sldr_setup[0] |= count; - sldr_setup[3 + reg_offset] &= ~IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK; + sldr_setup[3 + reg_offset] &= ~GENMASK(ext_chan - 1, 0); for (i = 0; i < ARRAY_SIZE(chan_sel); i++) { sldr_setup[5 + reg_offset + i] = 0; @@ -2081,17 +2120,19 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) sldr_setup[0] |= dev_desc->wheel_enable; } + /* + * The absence of a register offset makes it safe to assume the device + * supports gestures, each of which is first disabled until explicitly + * enabled. + */ + if (!reg_offset) + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) + sldr_setup[9] &= ~iqs7222_sl_events[i].enable; + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) { const char *event_name = iqs7222_sl_events[i].name; struct fwnode_handle *event_node; - /* - * The absence of a register offset means the remaining fields - * in the group represent gesture settings. - */ - if (iqs7222_sl_events[i].enable && !reg_offset) - sldr_setup[9] &= ~iqs7222_sl_events[i].enable; - event_node = fwnode_get_named_child_node(sldr_node, event_name); if (!event_node) continue; @@ -2104,6 +2145,22 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) if (error) return error; + /* + * The press/release event does not expose a direct GPIO link, + * but one can be emulated by tying each of the participating + * channels to the same GPIO. + */ + error = iqs7222_gpio_select(iqs7222, event_node, + i ? iqs7222_sl_events[i].enable + : sldr_setup[3 + reg_offset], + i ? 1568 + sldr_index * 30 + : sldr_setup[4 + reg_offset]); + if (error) + return error; + + if (!reg_offset) + sldr_setup[9] |= iqs7222_sl_events[i].enable; + error = fwnode_property_read_u32(event_node, "linux,code", &val); if (error) { @@ -2115,26 +2172,20 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) iqs7222->sl_code[sldr_index][i] = val; input_set_capability(iqs7222->keypad, EV_KEY, val); - /* - * The press/release event is determined based on whether the - * coordinate field reports 0xFFFF and has no explicit enable - * control. - */ - if (!iqs7222_sl_events[i].enable || reg_offset) - continue; - - sldr_setup[9] |= iqs7222_sl_events[i].enable; - - error = iqs7222_gpio_select(iqs7222, event_node, - iqs7222_sl_events[i].enable, - 1568 + sldr_index * 30); - if (error) - return error; - if (!dev_desc->event_offset) continue; - sys_setup[dev_desc->event_offset] |= BIT(10 + sldr_index); + /* + * The press/release event is determined based on whether the + * coordinate field reports 0xFFFF and solely relies on touch + * or proximity interrupts to be unmasked. + */ + if (i && !reg_offset) + *event_mask |= (IQS7222_EVENT_MASK_SLDR << sldr_index); + else if (sldr_setup[4 + reg_offset] == dev_desc->touch_link) + *event_mask |= IQS7222_EVENT_MASK_TOUCH; + else + *event_mask |= IQS7222_EVENT_MASK_PROX; } /* @@ -2227,11 +2278,6 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222) return error; } - sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; - sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; - - sys_setup[0] |= IQS7222_SYS_SETUP_ACK_RESET; - return iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_SYS, IQS7222_REG_KEY_NONE); } @@ -2299,29 +2345,37 @@ static int iqs7222_report(struct iqs7222_private *iqs7222) input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i], sldr_pos); - for (j = 0; j < ARRAY_SIZE(iqs7222_sl_events); j++) { - u16 mask = iqs7222_sl_events[j].mask; - u16 val = iqs7222_sl_events[j].val; + input_report_key(iqs7222->keypad, iqs7222->sl_code[i][0], + sldr_pos < dev_desc->sldr_res); - if (!iqs7222_sl_events[j].enable) { - input_report_key(iqs7222->keypad, - iqs7222->sl_code[i][j], - sldr_pos < dev_desc->sldr_res); - continue; - } + /* + * A maximum resolution indicates the device does not support + * gestures, in which case the remaining fields are ignored. + */ + if (dev_desc->sldr_res == U16_MAX) + continue; - /* - * The remaining offsets represent gesture state, and - * are discarded in the case of IQS7222C because only - * absolute position is reported. - */ - if (num_stat < IQS7222_MAX_COLS_STAT) - continue; + if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_SLDR << i)) + continue; + + /* + * Skip the press/release event, as it does not have separate + * status fields and is handled separately. + */ + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) { + u16 mask = iqs7222_sl_events[j].mask; + u16 val = iqs7222_sl_events[j].val; input_report_key(iqs7222->keypad, iqs7222->sl_code[i][j], (state & mask) == val); } + + input_sync(iqs7222->keypad); + + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) + input_report_key(iqs7222->keypad, + iqs7222->sl_code[i][j], 0); } input_sync(iqs7222->keypad); diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index 812edfced86e..0caaf3e64215 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -57,7 +57,7 @@ struct pip_app_resp_head { * The value of data_status can be the first byte of data or * the command status or the unsupported command code depending on the * requested command code. - */ + */ u8 data_status; } __packed; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 23507fce3a2b..18ccbd45004a 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -41,7 +41,7 @@ struct gpio_mouse { /* * Timer function which is run every scan_ms ms when the device is opened. - * The dev input variable is set to the the input_dev pointer. + * The dev input variable is set to the input_dev pointer. */ static void gpio_mouse_scan(struct input_dev *input) { diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 148a7c5fd0e2..4fbec7bbecca 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -67,25 +67,84 @@ static inline void i8042_write_command(int val) #include <linux/dmi.h> -static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { +#define SERIO_QUIRK_NOKBD BIT(0) +#define SERIO_QUIRK_NOAUX BIT(1) +#define SERIO_QUIRK_NOMUX BIT(2) +#define SERIO_QUIRK_FORCEMUX BIT(3) +#define SERIO_QUIRK_UNLOCK BIT(4) +#define SERIO_QUIRK_PROBE_DEFER BIT(5) +#define SERIO_QUIRK_RESET_ALWAYS BIT(6) +#define SERIO_QUIRK_RESET_NEVER BIT(7) +#define SERIO_QUIRK_DIECT BIT(8) +#define SERIO_QUIRK_DUMBKBD BIT(9) +#define SERIO_QUIRK_NOLOOP BIT(10) +#define SERIO_QUIRK_NOTIMEOUT BIT(11) +#define SERIO_QUIRK_KBDRESET BIT(12) +#define SERIO_QUIRK_DRITEK BIT(13) +#define SERIO_QUIRK_NOPNP BIT(14) + +/* Quirk table for different mainboards. Options similar or identical to i8042 + * module parameters. + * ORDERING IS IMPORTANT! The first match will be apllied and the rest ignored. + * This allows entries to overwrite vendor wide quirks on a per device basis. + * Where this is irrelevant, entries are sorted case sensitive by DMI_SYS_VENDOR + * and/or DMI_BOARD_VENDOR to make it easier to avoid dublicate entries. + */ +static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { { - /* - * Arima-Rioworks HDAMB - - * AUX LOOP command does not raise AUX IRQ - */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), - DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), - DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), + DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_BOARD_NAME, "G1S"), - DMI_MATCH(DMI_BOARD_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + /* Asus X450LCP */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UX425UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UM325UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + /* + * On some Asus laptops, just running self tests cause problems. + */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) }, { /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ @@ -94,585 +153,689 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { + /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "G1S"), + DMI_MATCH(DMI_BOARD_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Acer Aspire 5710 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Embedded Box PC 3000 */ + /* Acer Aspire 7738 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* OQO Model 01 */ + /* Acer Aspire 5536 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "OQO"), - DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "00"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ULI EV4873 - AUX LOOP does not work properly */ + /* + * Acer Aspire 5738z + * Touchpad stops working in mux mode when dis- + re-enabled + * with the touchpad enable/disable toggle hotkey + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ULI"), - DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Microsoft Virtual Machine */ + /* Acer Aspire One 150 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), - DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Medion MAM 2070 */ + /* Acer Aspire One 532h */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AO532h"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Medion"), - DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), - DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "blue"), - DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), - DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "M912"), - DMI_MATCH(DMI_PRODUCT_VERSION, "01"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M1022M netbook */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), - DMI_MATCH(DMI_BOARD_NAME, "M1022E"), - DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Wistron based laptops need us to explicitly enable the 'Dritek + * keyboard extension' to make their extra keys start generating scancodes. + * Originally, this was just confined to older laptops, but a few Acer laptops + * have turned up in 2007 that also need this again. + */ { + /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), - DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), - DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, - { } -}; - -/* - * Some Fujitsu notebooks are having trouble with touchpads if - * active multiplexing mode is activated. Luckily they don't have - * external PS/2 ports so we can safely disable it. - * ... apparently some Toshibas don't like MUX mode either and - * die horrible death on reboot. - */ -static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - /* Fujitsu Lifebook P7010/P7010D */ + /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P7010 */ + /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P5020D */ + /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S2000 */ + /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S6230 */ + /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook T725 laptop */ + /* Acer TravelMate 2490 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook U745 */ + /* Acer TravelMate 4280 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu T70H */ + /* Amoi M636/A737 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), + DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), + DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Lifebook E4010 */ + /* Compal HEL80I */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * No data is coming from the touchscreen unless KBC - * is in legacy mode. - */ - /* Panasonic CF-29 */ + /* Advent 4211 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), - DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* - * HP Pavilion DV4017EA - - * errors on MUX ports are reported without raising AUXDATA - * causing "spurious NAK" messages. - */ + /* Dell Embedded Box PC 3000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * HP Pavilion ZT1000 - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell XPS M1530 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), - DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * HP Pavilion DV4270ca - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell Vostro 1510 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { + /* Dell Vostro 1320 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1520 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Sharp Actius MM20 */ + /* Entroware Proteus */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), - DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), + DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Fujitsu notebooks are having trouble with touchpads if + * active multiplexing mode is activated. Luckily they don't have + * external PS/2 ports so we can safely disable it. + * ... apparently some Toshibas don't like MUX mode either and + * die horrible death on reboot. + */ { - /* Sony Vaio FS-115b */ + /* Fujitsu Lifebook P7010/P7010D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Sony Vaio FZ-240E - - * reset and GET ID commands issued via KBD port are - * sometimes being delivered to AUX3. - */ + /* Fujitsu Lifebook P5020D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Most (all?) VAIOs do not have external PS/2 ports nor - * they implement active multiplexing properly, and - * MUX discovery usually messes up keyboard/touchpad. - */ + /* Fujitsu Lifebook S2000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Amoi M636/A737 */ + /* Fujitsu Lifebook S6230 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Lenovo 3000 n100 */ + /* Fujitsu Lifebook T725 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* Lenovo XiaoXin Air 12 */ + /* Fujitsu Lifebook U745 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Fujitsu T70H */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5710 */ + /* Fujitsu A544 laptop */ + /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Acer Aspire 7738 */ + /* Fujitsu AH544 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Gericom Bellagio */ + /* Fujitsu U574 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), - DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* IBM 2656 */ + /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "IBM"), - DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Dell XPS M1530 */ + /* Fujitsu Lifebook P7010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Compal HEL80I */ + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), - DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1510 */ + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5536 */ + /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), - DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro V13 */ + /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Newer HP Pavilion dv4 models */ + /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "M912"), + DMI_MATCH(DMI_PRODUCT_VERSION, "01"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Asus X450LCP */ + /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Avatar AVIU-145A6 */ + /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel"), - DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* TUXEDO BU1406 */ + /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Lenovo LaVie Z */ + /* Gigabyte P35 v2 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* - * Acer Aspire 5738z - * Touchpad stops working in mux mode when dis- + re-enabled - * with the touchpad enable/disable toggle hotkey - */ + /* Gigabyte P34 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P34"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Entroware Proteus */ + /* Gigabyte P57 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P57"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), + DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* Gigabyte M1022M netbook */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "M1022E"), + DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, - { } -}; - -static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = { { /* - * Sony Vaio VGN-CS series require MUX or the touch sensor - * buttons will disturb touchpad operation + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA + * causing "spurious NAK" messages. */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -/* - * On some Asus laptops, just running self tests cause problems. - */ -static const struct dmi_system_id i8042_dmi_noselftest_table[] = { { + /* + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), }, - }, { + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { - /* MSI Wind U-100 */ + /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* LG Electronics X110 */ + /* IBM 2656 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "X110"), - DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire One 150 */ + /* Avatar AVIU-145A6 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + DMI_MATCH(DMI_SYS_VENDOR, "Intel"), + DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Intel MBO Desktop D845PESV */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* + * Intel NUC D54250WYK - does not have i8042 controller but + * declares PS/2 devices in DSDT. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* Lenovo 3000 n100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo XiaoXin Air 12 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo LaVie Z */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo Ideapad U455 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Lenovo ThinkPad L460 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Advent 4211 */ + /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), - DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) + }, + { + /* LG Electronics X110 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_BOARD_NAME, "X110"), + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya Mini E1210 */ @@ -680,6 +843,7 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya E1222 */ @@ -687,331 +851,434 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E122X"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Mivvy M310 */ + /* MSI Wind U-100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), - DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_BOARD_NAME, "U-100"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOPNP) }, { - /* Dell Vostro 1320 */ + /* + * No data is coming from the touchscreen unless KBC + * is in legacy mode. + */ + /* Panasonic CF-29 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1520 */ + /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Dell Vostro 1720 */ + /* Microsoft Virtual Machine */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo Ideapad U455 */ + /* Medion MAM 2070 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad L460 */ + /* TUXEDO BU1406 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ + /* OQO Model 01 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "00"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), + DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Entroware Proteus */ + /* Acer Aspire 5 A515 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, - { } -}; - -#ifdef CONFIG_PNP -static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { { - /* Intel MBO Desktop D845PESV */ + /* ULI EV4873 - AUX LOOP does not work properly */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_SYS_VENDOR, "ULI"), + DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { /* - * Intel NUC D54250WYK - does not have i8042 controller but - * declares PS/2 devices in DSDT. + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), + DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), + DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* MSI Wind U-100 */ + /* Sharp Actius MM20 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), + DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5 A515 */ + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), - DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { { + /* Sony Vaio FS-115b */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* + * Sony Vaio VGN-CS series require MUX or the touch sensor + * buttons will disturb touchpad operation + */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), }, + .driver_data = (void *)(SERIO_QUIRK_FORCEMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -#endif - -static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { { - /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. These two are based on a Clevo design, but have the + * board_name changed. + */ { - /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "AURA1501"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu A544 laptop */ - /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu AH544 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Mivvy M310 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Fujitsu Lifebook T725 laptop */ + /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "XMG"), + DMI_MATCH(DMI_PRODUCT_NAME, "C504"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Fujitsu U574 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), + DMI_MATCH(DMI_SYS_VENDOR, "blue"), + DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), + DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. + * Clevo barebones come with board_vendor and/or system_vendor set to + * either the very generic string "Notebook" and/or a different value + * for each individual reseller. The only somewhat universal way to + * identify them is by board_name. + */ { - /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some Wistron based laptops need us to explicitly enable the 'Dritek - * keyboard extension' to make their extra keys start generating scancodes. - * Originally, this was just confined to older laptops, but a few Acer laptops - * have turned up in 2007 that also need this again. - */ -static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { - /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + DMI_MATCH(DMI_BOARD_NAME, "N140CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), + DMI_MATCH(DMI_BOARD_NAME, "N141CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + /* + * At least one modern Clevo barebone has the touchpad connected both + * via PS/2 and i2c interface. This causes a race condition between the + * psmouse and i2c-hid driver. Since the full capability of the touchpad + * is available via the i2c interface and the device has no external + * PS/2 port, it is safe to just ignore all ps2 mouses here to avoid + * this issue. The known affected device is the + * TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU which comes with one of + * the two different dmi strings below. NS50MU is not a typo! + */ { - /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + DMI_MATCH(DMI_BOARD_NAME, "NS50MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), + DMI_MATCH(DMI_BOARD_NAME, "NS50_70MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + DMI_MATCH(DMI_BOARD_NAME, "NJ50_70CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 2490 */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xH"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 4280 */ + /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some laptops need keyboard reset before probing for the trackpad to get - * it detected, initialised & finally work. - */ -static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { { - /* Gigabyte P35 v2 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_P67H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { - /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + { + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P34 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P34"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RS"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P57 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P57"), + DMI_MATCH(DMI_PRODUCT_NAME, "P67xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "XMG"), - DMI_MATCH(DMI_PRODUCT_NAME, "C504"), + DMI_MATCH(DMI_BOARD_NAME, "PB50_70DFx,DDx"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170SM"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170KM-G"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { } }; -static const struct dmi_system_id i8042_dmi_probe_defer_table[] __initconst = { +#ifdef CONFIG_PNP +static const struct dmi_system_id i8042_dmi_laptop_table[] __initconst = { { - /* ASUS ZenBook UX425UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, }, { - /* ASUS ZenBook UM325UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, }, { } }; +#endif #endif /* CONFIG_X86 */ @@ -1167,11 +1434,6 @@ static int __init i8042_pnp_init(void) bool pnp_data_busted = false; int err; -#ifdef CONFIG_X86 - if (dmi_check_system(i8042_dmi_nopnp_table)) - i8042_nopnp = true; -#endif - if (i8042_nopnp) { pr_info("PNP detection disabled\n"); return 0; @@ -1275,6 +1537,59 @@ static inline int i8042_pnp_init(void) { return 0; } static inline void i8042_pnp_exit(void) { } #endif /* CONFIG_PNP */ + +#ifdef CONFIG_X86 +static void __init i8042_check_quirks(void) +{ + const struct dmi_system_id *device_quirk_info; + uintptr_t quirks; + + device_quirk_info = dmi_first_match(i8042_dmi_quirk_table); + if (!device_quirk_info) + return; + + quirks = (uintptr_t)device_quirk_info->driver_data; + + if (quirks & SERIO_QUIRK_NOKBD) + i8042_nokbd = true; + if (quirks & SERIO_QUIRK_NOAUX) + i8042_noaux = true; + if (quirks & SERIO_QUIRK_NOMUX) + i8042_nomux = true; + if (quirks & SERIO_QUIRK_FORCEMUX) + i8042_nomux = false; + if (quirks & SERIO_QUIRK_UNLOCK) + i8042_unlock = true; + if (quirks & SERIO_QUIRK_PROBE_DEFER) + i8042_probe_defer = true; + /* Honor module parameter when value is not default */ + if (i8042_reset == I8042_RESET_DEFAULT) { + if (quirks & SERIO_QUIRK_RESET_ALWAYS) + i8042_reset = I8042_RESET_ALWAYS; + if (quirks & SERIO_QUIRK_RESET_NEVER) + i8042_reset = I8042_RESET_NEVER; + } + if (quirks & SERIO_QUIRK_DIECT) + i8042_direct = true; + if (quirks & SERIO_QUIRK_DUMBKBD) + i8042_dumbkbd = true; + if (quirks & SERIO_QUIRK_NOLOOP) + i8042_noloop = true; + if (quirks & SERIO_QUIRK_NOTIMEOUT) + i8042_notimeout = true; + if (quirks & SERIO_QUIRK_KBDRESET) + i8042_kbdreset = true; + if (quirks & SERIO_QUIRK_DRITEK) + i8042_dritek = true; +#ifdef CONFIG_PNP + if (quirks & SERIO_QUIRK_NOPNP) + i8042_nopnp = true; +#endif +} +#else +static inline void i8042_check_quirks(void) {} +#endif + static int __init i8042_platform_init(void) { int retval; @@ -1297,45 +1612,42 @@ static int __init i8042_platform_init(void) i8042_kbd_irq = I8042_MAP_IRQ(1); i8042_aux_irq = I8042_MAP_IRQ(12); - retval = i8042_pnp_init(); - if (retval) - return retval; - #if defined(__ia64__) - i8042_reset = I8042_RESET_ALWAYS; + i8042_reset = I8042_RESET_ALWAYS; #endif + i8042_check_quirks(); + + pr_debug("Active quirks (empty means none):%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + i8042_nokbd ? " nokbd" : "", + i8042_noaux ? " noaux" : "", + i8042_nomux ? " nomux" : "", + i8042_unlock ? " unlock" : "", + i8042_probe_defer ? "probe_defer" : "", + i8042_reset == I8042_RESET_DEFAULT ? + "" : i8042_reset == I8042_RESET_ALWAYS ? + " reset_always" : " reset_never", + i8042_direct ? " direct" : "", + i8042_dumbkbd ? " dumbkbd" : "", + i8042_noloop ? " noloop" : "", + i8042_notimeout ? " notimeout" : "", + i8042_kbdreset ? " kbdreset" : "", #ifdef CONFIG_X86 - /* Honor module parameter when value is not default */ - if (i8042_reset == I8042_RESET_DEFAULT) { - if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = I8042_RESET_ALWAYS; - - if (dmi_check_system(i8042_dmi_noselftest_table)) - i8042_reset = I8042_RESET_NEVER; - } - - if (dmi_check_system(i8042_dmi_noloop_table)) - i8042_noloop = true; - - if (dmi_check_system(i8042_dmi_nomux_table)) - i8042_nomux = true; - - if (dmi_check_system(i8042_dmi_forcemux_table)) - i8042_nomux = false; - - if (dmi_check_system(i8042_dmi_notimeout_table)) - i8042_notimeout = true; - - if (dmi_check_system(i8042_dmi_dritek_table)) - i8042_dritek = true; - - if (dmi_check_system(i8042_dmi_kbdreset_table)) - i8042_kbdreset = true; + i8042_dritek ? " dritek" : "", +#else + "", +#endif +#ifdef CONFIG_PNP + i8042_nopnp ? " nopnp" : ""); +#else + ""); +#endif - if (dmi_check_system(i8042_dmi_probe_defer_table)) - i8042_probe_defer = true; + retval = i8042_pnp_init(); + if (retval) + return retval; +#ifdef CONFIG_X86 /* * A20 was already enabled during early kernel init. But some buggy * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index bb2e1cbffba7..82beddb28761 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -24,6 +24,7 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/ratelimit.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -47,6 +48,8 @@ #define M09_REGISTER_NUM_X 0x94 #define M09_REGISTER_NUM_Y 0x95 +#define M12_REGISTER_REPORT_RATE 0x88 + #define EV_REGISTER_THRESHOLD 0x40 #define EV_REGISTER_GAIN 0x41 #define EV_REGISTER_OFFSET_Y 0x45 @@ -127,9 +130,12 @@ struct edt_ft5x06_ts_data { int max_support_points; char name[EDT_NAME_LEN]; + char fw_version[EDT_NAME_LEN]; struct edt_reg_addr reg_addr; enum edt_ver version; + unsigned int crc_errors; + unsigned int header_errors; }; struct edt_i2c_chip_data { @@ -178,6 +184,7 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, crc ^= buf[i]; if (crc != buf[buflen-1]) { + tsdata->crc_errors++; dev_err_ratelimited(&tsdata->client->dev, "crc error: 0x%02x expected, got 0x%02x\n", crc, buf[buflen-1]); @@ -235,6 +242,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (tsdata->version == EDT_M06) { if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != datalen) { + tsdata->header_errors++; dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", rdbuf[0], rdbuf[1], rdbuf[2]); @@ -523,9 +531,55 @@ static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); -/* m06: range 3 to 14, m12: (0x64: 100Hz) */ +/* m06: range 3 to 14, m12: range 1 to 255 */ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, - NO_REGISTER, NO_REGISTER, 0, 255); + M12_REGISTER_REPORT_RATE, NO_REGISTER, 0, 255); + +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->name); +} + +static DEVICE_ATTR_RO(model); + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->fw_version); +} + +static DEVICE_ATTR_RO(fw_version); + +/* m06 only */ +static ssize_t header_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->header_errors); +} + +static DEVICE_ATTR_RO(header_errors); + +/* m06 only */ +static ssize_t crc_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->crc_errors); +} + +static DEVICE_ATTR_RO(crc_errors); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, @@ -534,6 +588,10 @@ static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_offset_y.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, + &dev_attr_model.attr, + &dev_attr_fw_version.attr, + &dev_attr_header_errors.attr, + &dev_attr_crc_errors.attr, NULL }; @@ -820,13 +878,13 @@ static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ static int edt_ft5x06_ts_identify(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; char *model_name = tsdata->name; + char *fw_version = tsdata->fw_version; /* see what we find if we assume it is a M06 * * if we get less than EDT_NAME_LEN, we don't want @@ -1030,7 +1088,8 @@ static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) case EDT_M09: case EDT_M12: reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; - reg_addr->reg_report_rate = NO_REGISTER; + reg_addr->reg_report_rate = tsdata->version == EDT_M12 ? + M12_REGISTER_REPORT_RATE : NO_REGISTER; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; reg_addr->reg_offset_x = NO_REGISTER; @@ -1081,7 +1140,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, struct input_dev *input; unsigned long irq_flags; int error; - char fw_version[EDT_NAME_LEN]; + u32 report_rate; dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); @@ -1194,7 +1253,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); return error; @@ -1210,9 +1269,30 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, edt_ft5x06_ts_get_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); + if (tsdata->reg_addr.reg_report_rate != NO_REGISTER && + !device_property_read_u32(&client->dev, + "report-rate-hz", &report_rate)) { + if (tsdata->version == EDT_M06) + tsdata->report_rate = clamp_val(report_rate, 30, 140); + else + tsdata->report_rate = clamp_val(report_rate, 1, 255); + + if (report_rate != tsdata->report_rate) + dev_warn(&client->dev, + "report-rate %dHz is unsupported, use %dHz\n", + report_rate, tsdata->report_rate); + + if (tsdata->version == EDT_M06) + tsdata->report_rate /= 10; + + edt_ft5x06_register_write(tsdata, + tsdata->reg_addr.reg_report_rate, + tsdata->report_rate); + } + dev_dbg(&client->dev, "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", - tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + tsdata->name, tsdata->fw_version, tsdata->num_x, tsdata->num_y); input->name = tsdata->name; input->id.bustype = BUS_I2C; diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index cbe0dd412912..4b7eee01c6aa 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -220,6 +220,7 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, { u8 buf[EXC3000_LEN_VENDOR_REQUEST] = { 0x67, 0x00, 0x42, 0x00, 0x03 }; int ret; + unsigned long time_left; mutex_lock(&data->query_lock); @@ -233,9 +234,9 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, goto out_unlock; if (response) { - ret = wait_for_completion_timeout(&data->wait_event, - timeout * HZ); - if (ret <= 0) { + time_left = wait_for_completion_timeout(&data->wait_event, + timeout * HZ); + if (time_left == 0) { ret = -ETIMEDOUT; goto out_unlock; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index aa45a9fee6a0..d016505fc081 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -822,22 +822,16 @@ static int goodix_resource(struct acpi_resource *ares, void *data) struct device *dev = &ts->client->dev; struct acpi_resource_gpio *gpio; - switch (ares->type) { - case ACPI_RESOURCE_TYPE_GPIO: - gpio = &ares->data.gpio; - if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) { - if (ts->gpio_int_idx == -1) { - ts->gpio_int_idx = ts->gpio_count; - } else { - dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); - ts->gpio_int_idx = -2; - } + if (acpi_gpio_get_irq_resource(ares, &gpio)) { + if (ts->gpio_int_idx == -1) { + ts->gpio_int_idx = ts->gpio_count; + } else { + dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); + ts->gpio_int_idx = -2; } ts->gpio_count++; - break; - default: - break; - } + } else if (acpi_gpio_get_io_resource(ares, &gpio)) + ts->gpio_count++; return 0; } diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 8bd03278ad9a..52f9e9eaab14 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -15,75 +15,75 @@ /* Register Map */ -#define BT541_SWRESET_CMD 0x0000 -#define BT541_WAKEUP_CMD 0x0001 +#define ZINITIX_SWRESET_CMD 0x0000 +#define ZINITIX_WAKEUP_CMD 0x0001 -#define BT541_IDLE_CMD 0x0004 -#define BT541_SLEEP_CMD 0x0005 +#define ZINITIX_IDLE_CMD 0x0004 +#define ZINITIX_SLEEP_CMD 0x0005 -#define BT541_CLEAR_INT_STATUS_CMD 0x0003 -#define BT541_CALIBRATE_CMD 0x0006 -#define BT541_SAVE_STATUS_CMD 0x0007 -#define BT541_SAVE_CALIBRATION_CMD 0x0008 -#define BT541_RECALL_FACTORY_CMD 0x000f +#define ZINITIX_CLEAR_INT_STATUS_CMD 0x0003 +#define ZINITIX_CALIBRATE_CMD 0x0006 +#define ZINITIX_SAVE_STATUS_CMD 0x0007 +#define ZINITIX_SAVE_CALIBRATION_CMD 0x0008 +#define ZINITIX_RECALL_FACTORY_CMD 0x000f -#define BT541_THRESHOLD 0x0020 +#define ZINITIX_THRESHOLD 0x0020 -#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F +#define ZINITIX_LARGE_PALM_REJECT_AREA_TH 0x003F -#define BT541_DEBUG_REG 0x0115 /* 0~7 */ +#define ZINITIX_DEBUG_REG 0x0115 /* 0~7 */ -#define BT541_TOUCH_MODE 0x0010 -#define BT541_CHIP_REVISION 0x0011 -#define BT541_FIRMWARE_VERSION 0x0012 +#define ZINITIX_TOUCH_MODE 0x0010 +#define ZINITIX_CHIP_REVISION 0x0011 +#define ZINITIX_FIRMWARE_VERSION 0x0012 #define ZINITIX_USB_DETECT 0x116 -#define BT541_MINOR_FW_VERSION 0x0121 +#define ZINITIX_MINOR_FW_VERSION 0x0121 -#define BT541_VENDOR_ID 0x001C -#define BT541_HW_ID 0x0014 +#define ZINITIX_VENDOR_ID 0x001C +#define ZINITIX_HW_ID 0x0014 -#define BT541_DATA_VERSION_REG 0x0013 -#define BT541_SUPPORTED_FINGER_NUM 0x0015 -#define BT541_EEPROM_INFO 0x0018 -#define BT541_INITIAL_TOUCH_MODE 0x0019 +#define ZINITIX_DATA_VERSION_REG 0x0013 +#define ZINITIX_SUPPORTED_FINGER_NUM 0x0015 +#define ZINITIX_EEPROM_INFO 0x0018 +#define ZINITIX_INITIAL_TOUCH_MODE 0x0019 -#define BT541_TOTAL_NUMBER_OF_X 0x0060 -#define BT541_TOTAL_NUMBER_OF_Y 0x0061 +#define ZINITIX_TOTAL_NUMBER_OF_X 0x0060 +#define ZINITIX_TOTAL_NUMBER_OF_Y 0x0061 -#define BT541_DELAY_RAW_FOR_HOST 0x007f +#define ZINITIX_DELAY_RAW_FOR_HOST 0x007f -#define BT541_BUTTON_SUPPORTED_NUM 0x00B0 -#define BT541_BUTTON_SENSITIVITY 0x00B2 -#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8 +#define ZINITIX_BUTTON_SUPPORTED_NUM 0x00B0 +#define ZINITIX_BUTTON_SENSITIVITY 0x00B2 +#define ZINITIX_DUMMY_BUTTON_SENSITIVITY 0X00C8 -#define BT541_X_RESOLUTION 0x00C0 -#define BT541_Y_RESOLUTION 0x00C1 +#define ZINITIX_X_RESOLUTION 0x00C0 +#define ZINITIX_Y_RESOLUTION 0x00C1 -#define BT541_POINT_STATUS_REG 0x0080 -#define BT541_ICON_STATUS_REG 0x00AA +#define ZINITIX_POINT_STATUS_REG 0x0080 +#define ZINITIX_ICON_STATUS_REG 0x00AA -#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2) +#define ZINITIX_POINT_COORD_REG (ZINITIX_POINT_STATUS_REG + 2) -#define BT541_AFE_FREQUENCY 0x0100 -#define BT541_DND_N_COUNT 0x0122 -#define BT541_DND_U_COUNT 0x0135 +#define ZINITIX_AFE_FREQUENCY 0x0100 +#define ZINITIX_DND_N_COUNT 0x0122 +#define ZINITIX_DND_U_COUNT 0x0135 -#define BT541_RAWDATA_REG 0x0200 +#define ZINITIX_RAWDATA_REG 0x0200 -#define BT541_EEPROM_INFO_REG 0x0018 +#define ZINITIX_EEPROM_INFO_REG 0x0018 -#define BT541_INT_ENABLE_FLAG 0x00f0 -#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 +#define ZINITIX_INT_ENABLE_FLAG 0x00f0 +#define ZINITIX_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 -#define BT541_BTN_WIDTH 0x016d +#define ZINITIX_BTN_WIDTH 0x016d -#define BT541_CHECKSUM_RESULT 0x012c +#define ZINITIX_CHECKSUM_RESULT 0x012c -#define BT541_INIT_FLASH 0x01d0 -#define BT541_WRITE_FLASH 0x01d1 -#define BT541_READ_FLASH 0x01d2 +#define ZINITIX_INIT_FLASH 0x01d0 +#define ZINITIX_WRITE_FLASH 0x01d1 +#define ZINITIX_READ_FLASH 0x01d2 #define ZINITIX_INTERNAL_FLAG_02 0x011e #define ZINITIX_INTERNAL_FLAG_03 0x011f @@ -196,13 +196,13 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) int i; int error; - error = zinitix_write_cmd(client, BT541_SWRESET_CMD); + error = zinitix_write_cmd(client, ZINITIX_SWRESET_CMD); if (error) { dev_err(&client->dev, "Failed to write reset command\n"); return error; } - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, 0x0); + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, 0x0); if (error) { dev_err(&client->dev, "Failed to reset interrupt enable flag\n"); @@ -210,32 +210,32 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) } /* initialize */ - error = zinitix_write_u16(client, BT541_X_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_X_RESOLUTION, bt541->prop.max_x); if (error) return error; - error = zinitix_write_u16(client, BT541_Y_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_Y_RESOLUTION, bt541->prop.max_y); if (error) return error; - error = zinitix_write_u16(client, BT541_SUPPORTED_FINGER_NUM, + error = zinitix_write_u16(client, ZINITIX_SUPPORTED_FINGER_NUM, MAX_SUPPORTED_FINGER_NUM); if (error) return error; - error = zinitix_write_u16(client, BT541_INITIAL_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_INITIAL_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE | BIT_UP); if (error) @@ -243,7 +243,7 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) /* clear queue */ for (i = 0; i < 10; i++) { - zinitix_write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(client, ZINITIX_CLEAR_INT_STATUS_CMD); udelay(10); } @@ -361,7 +361,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) memset(&touch_event, 0, sizeof(struct touch_event)); - error = zinitix_read_data(bt541->client, BT541_POINT_STATUS_REG, + error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG, &touch_event, sizeof(struct touch_event)); if (error) { dev_err(&client->dev, "Failed to read in touchpoint struct\n"); @@ -381,7 +381,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) input_sync(bt541->input_dev); out: - zinitix_write_cmd(bt541->client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(bt541->client, ZINITIX_CLEAR_INT_STATUS_CMD); return IRQ_HANDLED; } diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index acd6d6b47434..09c7ed2650ca 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -83,7 +83,7 @@ struct dm_bufio_client { struct mutex lock; spinlock_t spinlock; - unsigned long spinlock_flags; + bool no_sleep; struct list_head lru[LIST_SIZE]; unsigned long n_buffers[LIST_SIZE]; @@ -93,8 +93,6 @@ struct dm_bufio_client { s8 sectors_per_block_bits; void (*alloc_callback)(struct dm_buffer *); void (*write_callback)(struct dm_buffer *); - bool no_sleep; - struct kmem_cache *slab_buffer; struct kmem_cache *slab_cache; struct dm_io_client *dm_io; @@ -174,7 +172,7 @@ static DEFINE_STATIC_KEY_FALSE(no_sleep_enabled); static void dm_bufio_lock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_lock_irqsave_nested(&c->spinlock, c->spinlock_flags, dm_bufio_in_request()); + spin_lock_bh(&c->spinlock); else mutex_lock_nested(&c->lock, dm_bufio_in_request()); } @@ -182,7 +180,7 @@ static void dm_bufio_lock(struct dm_bufio_client *c) static int dm_bufio_trylock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - return spin_trylock_irqsave(&c->spinlock, c->spinlock_flags); + return spin_trylock_bh(&c->spinlock); else return mutex_trylock(&c->lock); } @@ -190,7 +188,7 @@ static int dm_bufio_trylock(struct dm_bufio_client *c) static void dm_bufio_unlock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_unlock_irqrestore(&c->spinlock, c->spinlock_flags); + spin_unlock_bh(&c->spinlock); else mutex_unlock(&c->lock); } @@ -817,6 +815,10 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) BUG_ON(test_bit(B_WRITING, &b->state)); BUG_ON(test_bit(B_DIRTY, &b->state)); + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep && + unlikely(test_bit(B_READING, &b->state))) + continue; + if (!b->hold_count) { __make_buffer_clean(b); __unlink_buffer(b); @@ -825,6 +827,9 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) cond_resched(); } + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) + return NULL; + list_for_each_entry_reverse(b, &c->lru[LIST_DIRTY], lru_list) { BUG_ON(test_bit(B_READING, &b->state)); @@ -1632,7 +1637,8 @@ static void drop_buffers(struct dm_bufio_client *c) */ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) { - if (!(gfp & __GFP_FS)) { + if (!(gfp & __GFP_FS) || + (static_branch_unlikely(&no_sleep_enabled) && b->c->no_sleep)) { if (test_bit(B_READING, &b->state) || test_bit(B_WRITING, &b->state) || test_bit(B_DIRTY, &b->state)) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 2347e83902f1..94b6cb599db4 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -38,7 +38,7 @@ #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" #define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet" -#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \ +#define DM_VERITY_OPTS_MAX (4 + DM_VERITY_OPTS_FEC + \ DM_VERITY_ROOT_HASH_VERIFICATION_OPTS) static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; @@ -1053,7 +1053,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, struct dm_verity_sig_opts *verify_args, bool only_modifier_opts) { - int r; + int r = 0; unsigned argc; struct dm_target *ti = v->ti; const char *arg_name; @@ -1123,8 +1123,18 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, if (r) return r; continue; + + } else if (only_modifier_opts) { + /* + * Ignore unrecognized opt, could easily be an extra + * argument to an option whose parsing was skipped. + * Normal parsing (@only_modifier_opts=false) will + * properly parse all options (and their extra args). + */ + continue; } + DMERR("Unrecognized verity feature request: %s", arg_name); ti->error = "Unrecognized verity feature request"; return -EINVAL; } while (argc && !r); diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 1fc161d65673..96a003eb7323 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -1594,7 +1594,8 @@ done: default: BUG(); - return -1; + wc_unlock(wc); + return DM_MAPIO_KILL; } } diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index cb48c5ff3dee..c93d2906e4c7 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -875,7 +875,7 @@ static int vcodec_domains_get(struct venus_core *core) } skip_pmdomains: - if (!core->has_opp_table) + if (!core->res->opp_pmdomain) return 0; /* Attach the power domain for setting performance state */ @@ -1007,6 +1007,10 @@ static int core_get_v4(struct venus_core *core) if (ret) return ret; + ret = vcodec_domains_get(core); + if (ret) + return ret; + if (core->res->opp_pmdomain) { ret = devm_pm_opp_of_add_table(dev); if (!ret) { @@ -1017,10 +1021,6 @@ static int core_get_v4(struct venus_core *core) } } - ret = vcodec_domains_get(core); - if (ret) - return ret; - return 0; } diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 908f8d5392b2..85bc936c02f9 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -1395,15 +1395,14 @@ err_msg: static int tegra_emc_opp_table_init(struct tegra_emc *emc) { u32 hw_version = BIT(tegra_sku_info.soc_speedo_id); - struct opp_table *hw_opp_table; - int err; + int opp_token, err; - hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1); - err = PTR_ERR_OR_ZERO(hw_opp_table); - if (err) { + err = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1); + if (err < 0) { dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err); return err; } + opp_token = err; err = dev_pm_opp_of_add_table(emc->dev); if (err) { @@ -1430,7 +1429,7 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc) remove_table: dev_pm_opp_of_remove_table(emc->dev); put_hw_table: - dev_pm_opp_put_supported_hw(hw_opp_table); + dev_pm_opp_put_supported_hw(opp_token); return err; } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 007d43e46dcb..b9dbad3a8af8 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -653,6 +653,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) { struct slave *tx_slave = NULL; + struct net_device *dev; struct arp_pkt *arp; if (!pskb_network_may_pull(skb, sizeof(*arp))) @@ -665,6 +666,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) if (!bond_slave_has_mac_rx(bond, arp->mac_src)) return NULL; + dev = ip_dev_find(dev_net(bond->dev), arp->ip_src); + if (dev) { + if (netif_is_bridge_master(dev)) { + dev_put(dev); + return NULL; + } + dev_put(dev); + } + if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected rx channel */ tx_slave = rlb_choose_channel(skb, bond, arp); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e75acb14d066..50e60843020c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2001,6 +2001,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) new_slave->target_last_arp_rx[i] = new_slave->last_rx; + new_slave->last_tx = new_slave->last_rx; + if (bond->params.miimon && !bond->params.use_carrier) { link_reporting = bond_check_dev_link(bond, slave_dev, 1); @@ -2884,8 +2886,11 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, return; } - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); arp_xmit(skb); + } + return; } @@ -3074,8 +3079,7 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_arp(bond, slave, tip, sip); else if (curr_arp_slave && (arp->ar_op == htons(ARPOP_REPLY)) && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_arp(bond, slave, sip, tip); out_unlock: @@ -3103,8 +3107,10 @@ static void bond_ns_send(struct slave *slave, const struct in6_addr *daddr, } addrconf_addr_solict_mult(daddr, &mcaddr); - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); ndisc_send_skb(skb, &mcaddr, saddr); + } } static void bond_ns_send_all(struct bonding *bond, struct slave *slave) @@ -3246,8 +3252,7 @@ static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_ns(bond, slave, saddr, daddr); else if (curr_arp_slave && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_ns(bond, slave, saddr, daddr); out: @@ -3335,12 +3340,12 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * so it can wait */ bond_for_each_slave_rcu(bond, slave, iter) { - unsigned long trans_start = dev_trans_start(slave->dev); + unsigned long last_tx = slave_last_tx(slave); bond_propose_link_state(slave, BOND_LINK_NOCHANGE); if (slave->link != BOND_LINK_UP) { - if (bond_time_in_interval(bond, trans_start, 1) && + if (bond_time_in_interval(bond, last_tx, 1) && bond_time_in_interval(bond, slave->last_rx, 1)) { bond_propose_link_state(slave, BOND_LINK_UP); @@ -3365,7 +3370,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * when the source ip is 0, so don't take the link down * if we don't know our ip yet */ - if (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) { bond_propose_link_state(slave, BOND_LINK_DOWN); @@ -3431,7 +3436,7 @@ re_arm: */ static int bond_ab_arp_inspect(struct bonding *bond) { - unsigned long trans_start, last_rx; + unsigned long last_tx, last_rx; struct list_head *iter; struct slave *slave; int commit = 0; @@ -3482,9 +3487,9 @@ static int bond_ab_arp_inspect(struct bonding *bond) * - (more than missed_max*delta since receive AND * the bond has an IP address) */ - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (bond_is_active_slave(slave) && - (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, last_rx, bond->params.missed_max))) { bond_propose_link_state(slave, BOND_LINK_DOWN); commit++; @@ -3501,8 +3506,8 @@ static int bond_ab_arp_inspect(struct bonding *bond) */ static void bond_ab_arp_commit(struct bonding *bond) { - unsigned long trans_start; struct list_head *iter; + unsigned long last_tx; struct slave *slave; bond_for_each_slave(bond, slave, iter) { @@ -3511,10 +3516,10 @@ static void bond_ab_arp_commit(struct bonding *bond) continue; case BOND_LINK_UP: - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (rtnl_dereference(bond->curr_active_slave) != slave || (!rtnl_dereference(bond->curr_active_slave) && - bond_time_in_interval(bond, trans_start, 1))) { + bond_time_in_interval(bond, last_tx, 1))) { struct slave *current_arp_slave; current_arp_slave = rtnl_dereference(bond->current_arp_slave); @@ -5333,8 +5338,14 @@ static struct net_device *bond_sk_get_lower_dev(struct net_device *dev, static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *dev) { - if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev))) - return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev); + struct net_device *tls_netdev = rcu_dereference(tls_get_ctx(skb->sk)->netdev); + + /* tls_netdev might become NULL, even if tls_is_sk_tx_device_offloaded + * was true, if tls_device_down is running in parallel, but it's OK, + * because bond_get_slave_by_dev has a NULL check. + */ + if (likely(bond_get_slave_by_dev(bond, tls_netdev))) + return bond_dev_queue_xmit(bond, skb, tls_netdev); return bond_tx_drop(dev, skb); } #endif diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index e750d13c8841..c320de474f40 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -1070,9 +1070,6 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); - /* mask out flags we don't care about */ - intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; - /* receive buffer 0 */ if (intf & CANINTF_RX0IF) { mcp251x_hw_rx(spi, 0); @@ -1082,6 +1079,18 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (mcp251x_is_2510(spi)) mcp251x_write_bits(spi, CANINTF, CANINTF_RX0IF, 0x00); + + /* check if buffer 1 is already known to be full, no need to re-read */ + if (!(intf & CANINTF_RX1IF)) { + u8 intf1, eflag1; + + /* intf needs to be read again to avoid a race condition */ + mcp251x_read_2regs(spi, CANINTF, &intf1, &eflag1); + + /* combine flags from both operations for error handling */ + intf |= intf1; + eflag |= eflag1; + } } /* receive buffer 1 */ @@ -1092,6 +1101,9 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) clear_intf |= CANINTF_RX1IF; } + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + /* any error or tx interrupt we need to clear? */ if (intf & (CANINTF_ERR | CANINTF_TX)) clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index d1e1a459c045..d31191686a54 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -195,7 +195,7 @@ struct __packed ems_cpc_msg { __le32 ts_sec; /* timestamp in seconds */ __le32 ts_nsec; /* timestamp in nano seconds */ - union { + union __packed { u8 generic[64]; struct cpc_can_msg can_msg; struct cpc_can_params can_params; diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 859196898a7d..aadb0bd7c24f 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -610,6 +610,9 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, old_proto_ops = felix->tag_proto_ops; + if (proto_ops == old_proto_ops) + return 0; + err = proto_ops->setup(ds); if (err) goto setup_failed; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 61ed317602e7..b4034b78c0ca 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1137,6 +1137,7 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, { struct tc_taprio_sched_entry *entry; u64 gate_len[OCELOT_NUM_TC]; + u8 gates_ever_opened = 0; int tc, i, n; /* Initialize arrays */ @@ -1164,16 +1165,28 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, for (tc = 0; tc < OCELOT_NUM_TC; tc++) { if (entry->gate_mask & BIT(tc)) { gate_len[tc] += entry->interval; + gates_ever_opened |= BIT(tc); } else { /* Gate closes now, record a potential new * minimum and reinitialize length */ - if (min_gate_len[tc] > gate_len[tc]) + if (min_gate_len[tc] > gate_len[tc] && + gate_len[tc]) min_gate_len[tc] = gate_len[tc]; gate_len[tc] = 0; } } } + + /* min_gate_len[tc] actually tracks minimum *open* gate time, so for + * permanently closed gates, min_gate_len[tc] will still be U64_MAX. + * Therefore they are currently indistinguishable from permanently + * open gates. Overwrite the gate len with 0 when we know they're + * actually permanently closed, i.e. after the loop above. + */ + for (tc = 0; tc < OCELOT_NUM_TC; tc++) + if (!(gates_ever_opened & BIT(tc))) + min_gate_len[tc] = 0; } /* Update QSYS_PORT_MAX_SDU to make sure the static guard bands added by the diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index e11cc29d3264..06508eebb585 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -265,12 +265,10 @@ static void aq_nic_service_timer_cb(struct timer_list *t) static void aq_nic_polling_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, polling_timer); - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_isr(i, (void *)aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_isr(i, (void *)self->aq_vec[i]); mod_timer(&self->polling_timer, jiffies + AQ_CFG_POLLING_TIMER_INTERVAL); @@ -1014,7 +1012,6 @@ int aq_nic_get_regs_count(struct aq_nic_s *self) u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) { - struct aq_vec_s *aq_vec = NULL; struct aq_stats_s *stats; unsigned int count = 0U; unsigned int i = 0U; @@ -1064,11 +1061,11 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) data += i; for (tc = 0U; tc < self->aq_nic_cfg.tcs; tc++) { - for (i = 0U, aq_vec = self->aq_vec[0]; - aq_vec && self->aq_vecs > i; - ++i, aq_vec = self->aq_vec[i]) { + for (i = 0U; self->aq_vecs > i; ++i) { + if (!self->aq_vec[i]) + break; data += count; - count = aq_vec_get_sw_stats(aq_vec, tc, data); + count = aq_vec_get_sw_stats(self->aq_vec[i], tc, data); } } @@ -1382,7 +1379,6 @@ int aq_nic_set_loopback(struct aq_nic_s *self) int aq_nic_stop(struct aq_nic_s *self) { - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; netif_tx_disable(self->ndev); @@ -1400,9 +1396,8 @@ int aq_nic_stop(struct aq_nic_s *self) aq_ptp_irq_free(self); - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_stop(aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_stop(self->aq_vec[i]); aq_ptp_ring_stop(self); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 2dfc1e32bbb3..93580484a3f4 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -189,8 +189,8 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, } slot->skb = skb; - ring->end += nr_frags + 1; netdev_sent_queue(net_dev, skb->len); + ring->end += nr_frags + 1; wmb(); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 14df8cfc2946..059f96f7a96f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -21,7 +21,6 @@ #include "bnxt_ptp.h" #include "bnxt_coredump.h" #include "bnxt_nvm_defs.h" -#include "bnxt_ethtool.h" static void __bnxt_fw_recover(struct bnxt *bp) { diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index c888ddee1fc4..7ded559842e8 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -393,6 +393,9 @@ int bcmgenet_mii_probe(struct net_device *dev) if (priv->internal_phy && !GENET_IS_V5(priv)) dev->phydev->irq = PHY_MAC_INTERRUPT; + /* Indicate that the MAC is responsible for PHY PM */ + dev->phydev->mac_managed_pm = true; + return 0; } diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c index bfee0e4e54b1..da9973b711f4 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c @@ -1932,6 +1932,7 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) int data_len, qidx, ret = 0, mss; struct tls_record_info *record; struct chcr_ktls_info *tx_info; + struct net_device *tls_netdev; struct tls_context *tls_ctx; struct sge_eth_txq *q; struct adapter *adap; @@ -1945,7 +1946,12 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) mss = skb_is_gso(skb) ? skb_shinfo(skb)->gso_size : data_len; tls_ctx = tls_get_ctx(skb->sk); - if (unlikely(tls_ctx->netdev != dev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't quit on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (unlikely(tls_netdev && tls_netdev != dev)) goto out; tx_ctx = chcr_get_ktls_tx_context(tls_ctx); diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index cb069a0af7b9..a5f7152a1716 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -340,14 +340,14 @@ static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count) return 0; } -static void tsnep_tx_unmap(struct tsnep_tx *tx, int count) +static void tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) { struct device *dmadev = tx->adapter->dmadev; struct tsnep_tx_entry *entry; int i; for (i = 0; i < count; i++) { - entry = &tx->entry[(tx->read + i) % TSNEP_RING_SIZE]; + entry = &tx->entry[(index + i) % TSNEP_RING_SIZE]; if (entry->len) { if (i == 0) @@ -395,7 +395,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, retval = tsnep_tx_map(skb, tx, count); if (retval != 0) { - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->write, count); dev_kfree_skb_any(entry->skb); entry->skb = NULL; @@ -464,7 +464,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) if (skb_shinfo(entry->skb)->nr_frags > 0) count += skb_shinfo(entry->skb)->nr_frags; - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->read, count); if ((skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) && (__le32_to_cpu(entry->desc_wb->properties) & @@ -1282,7 +1282,7 @@ MODULE_DEVICE_TABLE(of, tsnep_of_match); static struct platform_driver tsnep_driver = { .driver = { .name = TSNEP, - .of_match_table = of_match_ptr(tsnep_of_match), + .of_match_table = tsnep_of_match, }, .probe = tsnep_probe, .remove = tsnep_remove, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index cd9ec80522e7..75d51572693d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1660,8 +1660,8 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, buf_array[i] = addr; /* tracing point */ - trace_dpaa2_eth_buf_seed(priv->net_dev, - page, DPAA2_ETH_RX_BUF_RAW_SIZE, + trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), + DPAA2_ETH_RX_BUF_RAW_SIZE, addr, priv->rx_buf_size, bpid); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 6809b8b4c556..7282a826d81e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -2580,6 +2580,12 @@ static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA); rvu_reset_lmt_map_tbl(rvu, pcifunc); rvu_detach_rsrcs(rvu, NULL, pcifunc); + /* In scenarios where PF/VF drivers detach NIXLF without freeing MCAM + * entries, check and free the MCAM entries explicitly to avoid leak. + * Since LF is detached use LF number as -1. + */ + rvu_npc_free_mcam_entries(rvu, pcifunc, -1); + mutex_unlock(&rvu->flr_lock); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 583ead4dd246..1e348fd0d930 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1097,6 +1097,9 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + npc_enadis_default_entries(rvu, pcifunc, nixlf, false); /* Delete multicast and promisc MCAM entries */ @@ -1136,6 +1139,9 @@ bool rvu_npc_enable_mcam_by_entry_index(struct rvu *rvu, int entry, int intf, bo void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + /* Enables only broadcast match entry. Promisc/Allmulti are enabled * in set_rx_mode mbox handler. */ @@ -1675,7 +1681,7 @@ static void npc_load_kpu_profile(struct rvu *rvu) * Firmware database method. * Default KPU profile. */ - if (!request_firmware(&fw, kpu_profile, rvu->dev)) { + if (!request_firmware_direct(&fw, kpu_profile, rvu->dev)) { dev_info(rvu->dev, "Loading KPU profile from firmware: %s\n", kpu_profile); rvu->kpu_fwdata = kzalloc(fw->size, GFP_KERNEL); @@ -1939,6 +1945,7 @@ static void rvu_npc_hw_init(struct rvu *rvu, int blkaddr) static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) { + struct npc_mcam_kex *mkex = rvu->kpu.mkex; struct npc_mcam *mcam = &rvu->hw->mcam; struct rvu_hwinfo *hw = rvu->hw; u64 nibble_ena, rx_kex, tx_kex; @@ -1951,15 +1958,15 @@ static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) mcam->counters.max--; mcam->rx_miss_act_cntr = mcam->counters.max; - rx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_RX]; - tx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_TX]; + rx_kex = mkex->keyx_cfg[NIX_INTF_RX]; + tx_kex = mkex->keyx_cfg[NIX_INTF_TX]; nibble_ena = FIELD_GET(NPC_PARSE_NIBBLE, rx_kex); nibble_ena = rvu_npc_get_tx_nibble_cfg(rvu, nibble_ena); if (nibble_ena) { tx_kex &= ~NPC_PARSE_NIBBLE; tx_kex |= FIELD_PREP(NPC_PARSE_NIBBLE, nibble_ena); - npc_mkex_default.keyx_cfg[NIX_INTF_TX] = tx_kex; + mkex->keyx_cfg[NIX_INTF_TX] = tx_kex; } /* Configure RX interfaces */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index a400aa22da79..7c4e1acd0f77 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -467,7 +467,8 @@ do { \ NPC_SCAN_HDR(NPC_VLAN_TAG1, NPC_LID_LB, NPC_LT_LB_CTAG, 2, 2); NPC_SCAN_HDR(NPC_VLAN_TAG2, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 2, 2); NPC_SCAN_HDR(NPC_DMAC, NPC_LID_LA, la_ltype, la_start, 6); - NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start, 6); + /* SMAC follows the DMAC(which is 6 bytes) */ + NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start + 6, 6); /* PF_FUNC is 2 bytes at 0th byte of NPC_LT_LA_IH_NIX_ETHER */ NPC_SCAN_HDR(NPC_PF_FUNC, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 0, 2); } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index fb8db5888d2f..d686c7b6252f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -632,6 +632,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->num_regs++; req->reg[1] = NIX_AF_TL3X_SCHEDULE(schq); req->regval[1] = dwrr_val; + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL2) { parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; req->reg[0] = NIX_AF_TL2X_PARENT(schq); @@ -641,11 +647,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->reg[1] = NIX_AF_TL2X_SCHEDULE(schq); req->regval[1] = TXSCH_TL1_DFLT_RR_PRIO << 24 | dwrr_val; - req->num_regs++; - req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); - /* Enable this queue and backpressure */ - req->regval[2] = BIT_ULL(13) | BIT_ULL(12); - + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL1) { /* Default config for TL1. * For VF this is always ignored. @@ -1591,6 +1598,8 @@ void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, for (schq = 0; schq < rsp->schq[lvl]; schq++) pf->hw.txschq_list[lvl][schq] = rsp->schq_list[lvl][schq]; + + pf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl; } EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index e795f9ee76dd..b28029cc4316 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -195,6 +195,7 @@ struct otx2_hw { u16 sqb_size; /* NIX */ + u8 txschq_link_cfg_lvl; u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; u16 matchall_ipolicer; u32 dwrr_mtu; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c index d87bbb0be7c8..e6f64d890fb3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c @@ -506,7 +506,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, int err; attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; @@ -620,7 +620,7 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 6b6c7044b64a..0aef69527226 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -808,6 +808,7 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, { struct mlx5e_ktls_offload_context_tx *priv_tx; struct mlx5e_sq_stats *stats = sq->stats; + struct net_device *tls_netdev; struct tls_context *tls_ctx; int datalen; u32 seq; @@ -819,7 +820,12 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, mlx5e_tx_mpwqe_ensure_complete(sq); tls_ctx = tls_get_ctx(skb->sk); - if (WARN_ON_ONCE(tls_ctx->netdev != netdev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't WARN on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (WARN_ON_ONCE(tls_netdev && tls_netdev != netdev)) goto err_out; priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index d9bf584234a6..bb1cd4bae82e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -328,7 +328,6 @@ static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) { unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); u8 last_module = max_ports; int i; int err; @@ -357,7 +356,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) } /* Create port objects for each valid entry */ - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { err = mlxsw_m_port_create(mlxsw_m, @@ -367,7 +365,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) goto err_module_to_port_create; } } - devl_unlock(devlink); return 0; @@ -377,7 +374,6 @@ err_module_to_port_create: mlxsw_m_port_remove(mlxsw_m, mlxsw_m->module_to_port[i]); } - devl_unlock(devlink); i = max_ports; err_module_to_port_map: for (i--; i > 0; i--) @@ -390,10 +386,8 @@ err_module_to_port_alloc: static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) { - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); int i; - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { mlxsw_m_port_remove(mlxsw_m, @@ -401,7 +395,6 @@ static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) mlxsw_m_port_module_unmap(mlxsw_m, i); } } - devl_unlock(devlink); kfree(mlxsw_m->module_to_port); kfree(mlxsw_m->ports); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index c922dfab8080..eeb1455a4e5d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -1395,6 +1395,8 @@ nfp_port_get_module_info(struct net_device *netdev, u8 data; port = nfp_port_from_netdev(netdev); + /* update port state to get latest interface */ + set_bit(NFP_PORT_CHANGED, &port->flags); eth_port = nfp_port_get_eth_port(port); if (!eth_port) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 34c0d2ddf9ef..a8286d0032d1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -874,7 +874,6 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, } /* Adjust the start address to be cache size aligned */ - cache->id = id; cache->addr = addr & ~(u64)(cache->size - 1); /* Re-init to the new ID and address */ @@ -894,6 +893,8 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, return NULL; } + cache->id = id; + exit: /* Adjust offset */ *offset = addr - cache->addr; diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index baa1f0a5cc37..b4a4fa0a58f8 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -7,12 +7,12 @@ config NET_VENDOR_WANGXUN bool "Wangxun devices" default y help - If you have a network (Ethernet) card belonging to this class, say Y. + If you have a network (Ethernet) card from Wangxun(R), say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about Intel cards. If you say Y, you will be asked for - your specific card in the following questions. + the questions about Wangxun(R) cards. If you say Y, you will + be asked for your specific card in the following questions. if NET_VENDOR_WANGXUN diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 018d365f9deb..7962c37b3f14 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -797,7 +797,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct geneve_sock *gs4, struct flowi4 *fl4, const struct ip_tunnel_info *info, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + __u8 *full_tos) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -823,6 +824,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, use_cache = false; } fl4->flowi4_tos = RT_TOS(tos); + if (full_tos) + *full_tos = tos; dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { @@ -876,8 +879,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, use_cache = false; } - fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), - info->key.label); + fl6->flowlabel = ip6_make_flowinfo(prio, info->key.label); dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { dst = dst_cache_get_ip6(dst_cache, &fl6->saddr); @@ -911,6 +913,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, const struct ip_tunnel_key *key = &info->key; struct rtable *rt; struct flowi4 fl4; + __u8 full_tos; __u8 tos, ttl; __be16 df = 0; __be16 sport; @@ -921,7 +924,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, &full_tos); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -965,7 +968,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb); + tos = ip_tunnel_ecn_encap(full_tos, ip_hdr(skb), skb); if (geneve->cfg.ttl_inherit) ttl = ip_tunnel_get_ttl(ip_hdr(skb), skb); else @@ -1149,7 +1152,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, NULL); if (IS_ERR(rt)) return PTR_ERR(rt); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f1683ce6b561..ee6087e7b2bf 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -162,6 +162,19 @@ static struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr) return sa; } +static struct macsec_rx_sa *macsec_active_rxsa_get(struct macsec_rx_sc *rx_sc) +{ + struct macsec_rx_sa *sa = NULL; + int an; + + for (an = 0; an < MACSEC_NUM_AN; an++) { + sa = macsec_rxsa_get(rx_sc->sa[an]); + if (sa) + break; + } + return sa; +} + static void free_rx_sc_rcu(struct rcu_head *head) { struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head); @@ -500,18 +513,28 @@ static void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev) skb->protocol = eth_hdr(skb)->h_proto; } +static unsigned int macsec_msdu_len(struct sk_buff *skb) +{ + struct macsec_dev *macsec = macsec_priv(skb->dev); + struct macsec_secy *secy = &macsec->secy; + bool sci_present = macsec_skb_cb(skb)->has_sci; + + return skb->len - macsec_hdr_len(sci_present) - secy->icv_len; +} + static void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc, struct macsec_tx_sa *tx_sa) { + unsigned int msdu_len = macsec_msdu_len(skb); struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats); u64_stats_update_begin(&txsc_stats->syncp); if (tx_sc->encrypt) { - txsc_stats->stats.OutOctetsEncrypted += skb->len; + txsc_stats->stats.OutOctetsEncrypted += msdu_len; txsc_stats->stats.OutPktsEncrypted++; this_cpu_inc(tx_sa->stats->OutPktsEncrypted); } else { - txsc_stats->stats.OutOctetsProtected += skb->len; + txsc_stats->stats.OutOctetsProtected += msdu_len; txsc_stats->stats.OutPktsProtected++; this_cpu_inc(tx_sa->stats->OutPktsProtected); } @@ -541,9 +564,10 @@ static void macsec_encrypt_done(struct crypto_async_request *base, int err) aead_request_free(macsec_skb_cb(skb)->req); rcu_read_lock_bh(); - macsec_encrypt_finish(skb, dev); macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); - len = skb->len; + /* packet is encrypted/protected so tx_bytes must be calculated */ + len = macsec_msdu_len(skb) + 2 * ETH_ALEN; + macsec_encrypt_finish(skb, dev); ret = dev_queue_xmit(skb); count_tx(dev, ret, len); rcu_read_unlock_bh(); @@ -702,6 +726,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, macsec_skb_cb(skb)->req = req; macsec_skb_cb(skb)->tx_sa = tx_sa; + macsec_skb_cb(skb)->has_sci = sci_present; aead_request_set_callback(req, 0, macsec_encrypt_done, skb); dev_hold(skb->dev); @@ -743,15 +768,17 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_dropped++; return false; } if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) { + unsigned int msdu_len = macsec_msdu_len(skb); u64_stats_update_begin(&rxsc_stats->syncp); if (hdr->tci_an & MACSEC_TCI_E) - rxsc_stats->stats.InOctetsDecrypted += skb->len; + rxsc_stats->stats.InOctetsDecrypted += msdu_len; else - rxsc_stats->stats.InOctetsValidated += skb->len; + rxsc_stats->stats.InOctetsValidated += msdu_len; u64_stats_update_end(&rxsc_stats->syncp); } @@ -764,6 +791,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotValid++; u64_stats_update_end(&rxsc_stats->syncp); + this_cpu_inc(rx_sa->stats->InPktsNotValid); + secy->netdev->stats.rx_errors++; return false; } @@ -856,9 +885,9 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) macsec_finalize_skb(skb, macsec->secy.icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, macsec->secy.netdev); - len = skb->len; if (gro_cells_receive(&macsec->gro_cells, skb) == NET_RX_SUCCESS) count_rx(dev, len); @@ -1049,6 +1078,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; continue; } @@ -1158,6 +1188,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsBadTag++; u64_stats_update_end(&secy_stats->syncp); + secy->netdev->stats.rx_errors++; goto drop_nosa; } @@ -1168,11 +1199,15 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) /* If validateFrames is Strict or the C bit in the * SecTAG is set, discard */ + struct macsec_rx_sa *active_rx_sa = macsec_active_rxsa_get(rx_sc); if (hdr->tci_an & MACSEC_TCI_C || secy->validate_frames == MACSEC_VALIDATE_STRICT) { u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotUsingSA++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_errors++; + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsNotUsingSA); goto drop_nosa; } @@ -1182,6 +1217,8 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsUnusedSA++; u64_stats_update_end(&rxsc_stats->syncp); + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsUnusedSA); goto deliver; } @@ -1202,6 +1239,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; goto drop; } } @@ -1230,6 +1268,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) deliver: macsec_finalize_skb(skb, secy->icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, secy->netdev); if (rx_sa) @@ -1237,7 +1276,6 @@ deliver: macsec_rxsc_put(rx_sc); skb_orphan(skb); - len = skb->len; ret = gro_cells_receive(&macsec->gro_cells, skb); if (ret == NET_RX_SUCCESS) count_rx(dev, len); @@ -1279,6 +1317,7 @@ nosci: u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoSCI++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_errors++; continue; } @@ -3404,6 +3443,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + len = skb->len; skb = macsec_encrypt(skb, dev); if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EINPROGRESS) @@ -3414,7 +3454,6 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); macsec_encrypt_finish(skb, dev); - len = skb->len; ret = dev_queue_xmit(skb); count_tx(dev, ret, len); return ret; @@ -3662,6 +3701,7 @@ static void macsec_get_stats64(struct net_device *dev, s->rx_dropped = dev->stats.rx_dropped; s->tx_dropped = dev->stats.tx_dropped; + s->rx_errors = dev->stats.rx_errors; } static int macsec_get_iflink(const struct net_device *dev) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 1e38039c5c56..6939563d3b7c 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -535,7 +535,7 @@ static int dp83867_of_init_io_impedance(struct phy_device *phydev) cell = of_nvmem_cell_get(of_node, "io_impedance_ctrl"); if (IS_ERR(cell)) { ret = PTR_ERR(cell); - if (ret != -ENOENT) + if (ret != -ENOENT && ret != -EOPNOTSUPP) return phydev_err_probe(phydev, ret, "failed to get nvmem cell io_impedance_ctrl\n"); diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 29b1df03f3e8..a87a4b3ffce4 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -190,44 +190,42 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); */ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) { + u16 adv_l_mask, adv_l = 0; + u16 adv_m_mask, adv_m = 0; int changed = 0; - u16 adv_l = 0; - u16 adv_m = 0; int ret; + adv_l_mask = MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP | + MDIO_AN_T1_ADV_L_PAUSE_ASYM; + adv_m_mask = MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L; + switch (phydev->master_slave_set) { case MASTER_SLAVE_CFG_MASTER_FORCE: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_FORCE: adv_l |= MDIO_AN_T1_ADV_L_FORCE_MS; break; case MASTER_SLAVE_CFG_MASTER_PREFERRED: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_PREFERRED: break; case MASTER_SLAVE_CFG_UNKNOWN: case MASTER_SLAVE_CFG_UNSUPPORTED: - return 0; + /* if master/slave role is not specified, do not overwrite it */ + adv_l_mask &= ~MDIO_AN_T1_ADV_L_FORCE_MS; + adv_m_mask &= ~MDIO_AN_T1_ADV_M_MST; + break; default: phydev_warn(phydev, "Unsupported Master/Slave mode\n"); return -EOPNOTSUPP; } - switch (phydev->master_slave_set) { - case MASTER_SLAVE_CFG_MASTER_FORCE: - case MASTER_SLAVE_CFG_MASTER_PREFERRED: - adv_m |= MDIO_AN_T1_ADV_M_MST; - break; - case MASTER_SLAVE_CFG_SLAVE_FORCE: - case MASTER_SLAVE_CFG_SLAVE_PREFERRED: - break; - default: - break; - } - adv_l |= linkmode_adv_to_mii_t1_adv_l_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L, - (MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP - | MDIO_AN_T1_ADV_L_PAUSE_ASYM), adv_l); + adv_l_mask, adv_l); if (ret < 0) return ret; if (ret > 0) @@ -236,7 +234,7 @@ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) adv_m |= linkmode_adv_to_mii_t1_adv_m_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M, - MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L, adv_m); + adv_m_mask, adv_m); if (ret < 0) return ret; if (ret > 0) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a74b320f5b27..0c6efd792690 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -316,6 +316,12 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev) phydev->suspended_by_mdio_bus = 0; + /* If we managed to get here with the PHY state machine in a state other + * than PHY_HALTED this is an indication that something went wrong and + * we should most likely be using MAC managed PM and we are not. + */ + WARN_ON(phydev->state != PHY_HALTED && !phydev->mac_managed_pm); + ret = phy_init_hw(phydev); if (ret < 0) return ret; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index dafd3e9ebbf8..c8791e9b451d 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1111,7 +1111,7 @@ plip_open(struct net_device *dev) /* Any address will do - we take the first. We already have the first two bytes filled with 0xfc, from plip_init_dev(). */ - const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); + const struct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list); if (ifa != NULL) { dev_addr_mod(dev, 2, &ifa->ifa_local, 4); } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index c3d42062559d..9e75ed3f08ce 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -716,10 +716,20 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return total_len; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = virtio_net_hdr_to_skb(skb, &vnet_hdr, tap_is_little_endian(q)); if (err) { + rcu_read_unlock(); drop_reason = SKB_DROP_REASON_DEV_HDR; goto err_kfree; } @@ -732,8 +742,6 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { skb_zcopy_init(skb, msg_control); @@ -742,14 +750,8 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, uarg->callback(NULL, uarg, false); } - if (tap) { - skb->dev = tap->dev; - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + dev_queue_xmit(skb); rcu_read_unlock(); - return total_len; err_kfree: diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 0ad468a00064..aff39bf3161d 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1680,7 +1680,7 @@ static const struct driver_info ax88179_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1693,7 +1693,7 @@ static const struct driver_info ax88178a_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1706,7 +1706,7 @@ static const struct driver_info cypress_GX3_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1719,7 +1719,7 @@ static const struct driver_info dlink_dub1312_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1732,7 +1732,7 @@ static const struct driver_info sitecom_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1745,7 +1745,7 @@ static const struct driver_info samsung_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1758,7 +1758,7 @@ static const struct driver_info lenovo_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1771,7 +1771,7 @@ static const struct driver_info belkin_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1784,7 +1784,7 @@ static const struct driver_info toshiba_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1797,7 +1797,7 @@ static const struct driver_info mct_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1810,7 +1810,7 @@ static const struct driver_info at_umc2000_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1823,7 +1823,7 @@ static const struct driver_info at_umc200_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1836,7 +1836,7 @@ static const struct driver_info at_umc2000sp_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 571a399c195d..709e3c59e340 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1390,6 +1390,8 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */ {QMI_FIXED_INTF(0x1e2d, 0x00b7, 0)}, /* Cinterion MV31 RmNet */ {QMI_FIXED_INTF(0x1e2d, 0x00b9, 0)}, /* Cinterion MV31 RmNet based on new baseline */ + {QMI_FIXED_INTF(0x1e2d, 0x00f3, 0)}, /* Cinterion MV32-W-A RmNet */ + {QMI_FIXED_INTF(0x1e2d, 0x00f4, 0)}, /* Cinterion MV32-W-B RmNet */ {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2cb833b3006a..466da01ba2e3 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -312,7 +312,6 @@ static bool veth_skb_is_eligible_for_gro(const struct net_device *dev, static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) { struct veth_priv *rcv_priv, *priv = netdev_priv(dev); - struct netdev_queue *queue = NULL; struct veth_rq *rq = NULL; struct net_device *rcv; int length = skb->len; @@ -330,7 +329,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) rxq = skb_get_queue_mapping(skb); if (rxq < rcv->real_num_rx_queues) { rq = &rcv_priv->rq[rxq]; - queue = netdev_get_tx_queue(dev, rxq); /* The napi pointer is available when an XDP program is * attached or when GRO is enabled @@ -342,8 +340,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) { - if (queue) - txq_trans_cond_update(queue); if (!use_napi) dev_lstats_add(dev, length); } else { diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ec8e1b3108c3..3b3eebad3977 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1057,8 +1057,11 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, case XDP_TX: stats->xdp_tx++; xdpf = xdp_convert_buff_to_frame(&xdp); - if (unlikely(!xdpf)) + if (unlikely(!xdpf)) { + if (unlikely(xdp_page != page)) + put_page(xdp_page); goto err_xdp; + } err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); if (unlikely(!err)) { xdp_return_frame_rx_napi(xdpf); diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 90811ab851fd..c3285242f74f 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2321,7 +2321,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, fl6.flowi6_oif = oif; fl6.daddr = *daddr; fl6.saddr = *saddr; - fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tos), label); + fl6.flowlabel = ip6_make_flowinfo(tos, label); fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = IPPROTO_UDP; fl6.fl6_dport = dport; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index b89519ab9205..eb1d1ba3a443 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -635,7 +635,7 @@ static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif, conn_info->req_ies_len = 0; } -inline void wilc_handle_disconnect(struct wilc_vif *vif) +void wilc_handle_disconnect(struct wilc_vif *vif) { struct host_if_drv *hif_drv = vif->hif_drv; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.h b/drivers/net/wireless/microchip/wilc1000/hif.h index 69ba1d469e9f..baa2881f4465 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.h +++ b/drivers/net/wireless/microchip/wilc1000/hif.h @@ -215,5 +215,6 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length); void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, struct cfg80211_crypto_settings *crypto); int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index); -inline void wilc_handle_disconnect(struct wilc_vif *vif); +void wilc_handle_disconnect(struct wilc_vif *vif); + #endif diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index d976260eca7a..473a71bbd9c9 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -133,7 +133,8 @@ static void nd_region_release(struct device *dev) put_device(&nvdimm->dev); } free_percpu(nd_region->lane); - memregion_free(nd_region->id); + if (!test_bit(ND_REGION_CXL, &nd_region->flags)) + memregion_free(nd_region->id); kfree(nd_region); } @@ -982,9 +983,14 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, if (!nd_region) return NULL; - nd_region->id = memregion_alloc(GFP_KERNEL); - if (nd_region->id < 0) - goto err_id; + /* CXL pre-assigns memregion ids before creating nvdimm regions */ + if (test_bit(ND_REGION_CXL, &ndr_desc->flags)) { + nd_region->id = ndr_desc->memregion; + } else { + nd_region->id = memregion_alloc(GFP_KERNEL); + if (nd_region->id < 0) + goto err_id; + } nd_region->lane = alloc_percpu(struct nd_percpu_lane); if (!nd_region->lane) @@ -1043,9 +1049,10 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, return nd_region; - err_percpu: - memregion_free(nd_region->id); - err_id: +err_percpu: + if (!test_bit(ND_REGION_CXL, &ndr_desc->flags)) + memregion_free(nd_region->id); +err_id: kfree(nd_region); return NULL; } @@ -1068,6 +1075,13 @@ struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus, } EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create); +void nvdimm_region_delete(struct nd_region *nd_region) +{ + if (nd_region) + nd_device_unregister(&nd_region->dev, ND_SYNC); +} +EXPORT_SYMBOL_GPL(nvdimm_region_delete); + int nvdimm_flush(struct nd_region *nd_region, struct bio *bio) { int rc = 0; diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 84063eaebb91..77d1ba3a4154 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -13,11 +13,12 @@ #include <linux/clk.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/slab.h> #include <linux/device.h> #include <linux/export.h> #include <linux/pm_domain.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/xarray.h> #include "opp.h" @@ -36,6 +37,9 @@ DEFINE_MUTEX(opp_table_lock); /* Flag indicating that opp_tables list is being updated at the moment */ static bool opp_tables_busy; +/* OPP ID allocator */ +static DEFINE_XARRAY_ALLOC1(opp_configs); + static bool _find_opp_dev(const struct device *dev, struct opp_table *opp_table) { struct opp_device *opp_dev; @@ -93,6 +97,18 @@ struct opp_table *_find_opp_table(struct device *dev) return opp_table; } +/* + * Returns true if multiple clocks aren't there, else returns false with WARN. + * + * We don't force clk_count == 1 here as there are users who don't have a clock + * representation in the OPP table and manage the clock configuration themselves + * in an platform specific way. + */ +static bool assert_single_clk(struct opp_table *opp_table) +{ + return !WARN_ON(opp_table->clk_count > 1); +} + /** * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp * @opp: opp for which voltage has to be returned for @@ -114,6 +130,31 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); /** + * dev_pm_opp_get_supplies() - Gets the supply information corresponding to an opp + * @opp: opp for which voltage has to be returned for + * @supplies: Placeholder for copying the supply information. + * + * Return: negative error number on failure, 0 otherwise on success after + * setting @supplies. + * + * This can be used for devices with any number of power supplies. The caller + * must ensure the @supplies array must contain space for each regulator. + */ +int dev_pm_opp_get_supplies(struct dev_pm_opp *opp, + struct dev_pm_opp_supply *supplies) +{ + if (IS_ERR_OR_NULL(opp) || !supplies) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + memcpy(supplies, opp->supplies, + sizeof(*supplies) * opp->opp_table->regulator_count); + return 0; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_supplies); + +/** * dev_pm_opp_get_power() - Gets the power corresponding to an opp * @opp: opp for which power has to be returned for * @@ -152,7 +193,10 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) return 0; } - return opp->rate; + if (!assert_single_clk(opp->opp_table)) + return 0; + + return opp->rates[0]; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); @@ -398,6 +442,154 @@ int dev_pm_opp_get_opp_count(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); +/* Helpers to read keys */ +static unsigned long _read_freq(struct dev_pm_opp *opp, int index) +{ + return opp->rates[0]; +} + +static unsigned long _read_level(struct dev_pm_opp *opp, int index) +{ + return opp->level; +} + +static unsigned long _read_bw(struct dev_pm_opp *opp, int index) +{ + return opp->bandwidth[index].peak; +} + +/* Generic comparison helpers */ +static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, + unsigned long opp_key, unsigned long key) +{ + if (opp_key == key) { + *opp = temp_opp; + return true; + } + + return false; +} + +static bool _compare_ceil(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, + unsigned long opp_key, unsigned long key) +{ + if (opp_key >= key) { + *opp = temp_opp; + return true; + } + + return false; +} + +static bool _compare_floor(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, + unsigned long opp_key, unsigned long key) +{ + if (opp_key > key) + return true; + + *opp = temp_opp; + return false; +} + +/* Generic key finding helpers */ +static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table, + unsigned long *key, int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, + unsigned long opp_key, unsigned long key), + bool (*assert)(struct opp_table *opp_table)) +{ + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + /* Assert that the requirement is met */ + if (assert && !assert(opp_table)) + return ERR_PTR(-EINVAL); + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->available == available) { + if (compare(&opp, temp_opp, read(temp_opp, index), *key)) + break; + } + } + + /* Increment the reference count of OPP */ + if (!IS_ERR(opp)) { + *key = read(opp, index); + dev_pm_opp_get(opp); + } + + mutex_unlock(&opp_table->lock); + + return opp; +} + +static struct dev_pm_opp * +_find_key(struct device *dev, unsigned long *key, int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, + unsigned long opp_key, unsigned long key), + bool (*assert)(struct opp_table *opp_table)) +{ + struct opp_table *opp_table; + struct dev_pm_opp *opp; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, + PTR_ERR(opp_table)); + return ERR_CAST(opp_table); + } + + opp = _opp_table_find_key(opp_table, key, index, available, read, + compare, assert); + + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} + +static struct dev_pm_opp *_find_key_exact(struct device *dev, + unsigned long key, int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*assert)(struct opp_table *opp_table)) +{ + /* + * The value of key will be updated here, but will be ignored as the + * caller doesn't need it. + */ + return _find_key(dev, &key, index, available, read, _compare_exact, + assert); +} + +static struct dev_pm_opp *_opp_table_find_key_ceil(struct opp_table *opp_table, + unsigned long *key, int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*assert)(struct opp_table *opp_table)) +{ + return _opp_table_find_key(opp_table, key, index, available, read, + _compare_ceil, assert); +} + +static struct dev_pm_opp *_find_key_ceil(struct device *dev, unsigned long *key, + int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*assert)(struct opp_table *opp_table)) +{ + return _find_key(dev, key, index, available, read, _compare_ceil, + assert); +} + +static struct dev_pm_opp *_find_key_floor(struct device *dev, + unsigned long *key, int index, bool available, + unsigned long (*read)(struct dev_pm_opp *opp, int index), + bool (*assert)(struct opp_table *opp_table)) +{ + return _find_key(dev, key, index, available, read, _compare_floor, + assert); +} + /** * dev_pm_opp_find_freq_exact() - search for an exact frequency * @dev: device for which we do this operation @@ -422,61 +614,18 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, - unsigned long freq, - bool available) + unsigned long freq, bool available) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - int r = PTR_ERR(opp_table); - - dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); - return ERR_PTR(r); - } - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available == available && - temp_opp->rate == freq) { - opp = temp_opp; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } - - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - return opp; + return _find_key_exact(dev, freq, 0, available, _read_freq, + assert_single_clk); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->rate >= *freq) { - opp = temp_opp; - *freq = opp->rate; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } - - mutex_unlock(&opp_table->lock); - - return opp; + return _opp_table_find_key_ceil(opp_table, freq, 0, true, _read_freq, + assert_single_clk); } /** @@ -500,23 +649,7 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { - struct opp_table *opp_table; - struct dev_pm_opp *opp; - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); - return ERR_PTR(-EINVAL); - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); - - opp = _find_freq_ceil(opp_table, freq); - - dev_pm_opp_put_opp_table(opp_table); - - return opp; + return _find_key_ceil(dev, freq, 0, true, _read_freq, assert_single_clk); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); @@ -541,98 +674,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); - return ERR_PTR(-EINVAL); - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available) { - /* go to the next node, before choosing prev */ - if (temp_opp->rate > *freq) - break; - else - opp = temp_opp; - } - } - - /* Increment the reference count of OPP */ - if (!IS_ERR(opp)) - dev_pm_opp_get(opp); - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - if (!IS_ERR(opp)) - *freq = opp->rate; - - return opp; + return _find_key_floor(dev, freq, 0, true, _read_freq, assert_single_clk); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); /** - * dev_pm_opp_find_freq_ceil_by_volt() - Find OPP with highest frequency for - * target voltage. - * @dev: Device for which we do this operation. - * @u_volt: Target voltage. - * - * Search for OPP with highest (ceil) frequency and has voltage <= u_volt. - * - * Return: matching *opp, else returns ERR_PTR in case of error which should be - * handled using IS_ERR. - * - * Error return values can be: - * EINVAL: bad parameters - * - * The callers are required to call dev_pm_opp_put() for the returned OPP after - * use. - */ -struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, - unsigned long u_volt) -{ - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - if (!dev || !u_volt) { - dev_err(dev, "%s: Invalid argument volt=%lu\n", __func__, - u_volt); - return ERR_PTR(-EINVAL); - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available) { - if (temp_opp->supplies[0].u_volt > u_volt) - break; - opp = temp_opp; - } - } - - /* Increment the reference count of OPP */ - if (!IS_ERR(opp)) - dev_pm_opp_get(opp); - - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - return opp; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_by_volt); - -/** * dev_pm_opp_find_level_exact() - search for an exact level * @dev: device for which we do this operation * @level: level to search for @@ -650,33 +696,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_by_volt); struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - int r = PTR_ERR(opp_table); - - dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); - return ERR_PTR(r); - } - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->level == level) { - opp = temp_opp; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } - - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - return opp; + return _find_key_exact(dev, level, 0, true, _read_level, NULL); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); @@ -698,33 +718,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, unsigned int *level) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - int r = PTR_ERR(opp_table); - - dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); - return ERR_PTR(r); - } - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->level >= *level) { - opp = temp_opp; - *level = opp->level; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } - - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); + unsigned long temp = *level; + struct dev_pm_opp *opp; + opp = _find_key_ceil(dev, &temp, 0, true, _read_level, NULL); + *level = temp; return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil); @@ -732,7 +730,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil); /** * dev_pm_opp_find_bw_ceil() - Search for a rounded ceil bandwidth * @dev: device for which we do this operation - * @freq: start bandwidth + * @bw: start bandwidth * @index: which bandwidth to compare, in case of OPPs with several values * * Search for the matching floor *available* OPP from a starting bandwidth @@ -748,42 +746,14 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil); * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ -struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, - unsigned int *bw, int index) +struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, + int index) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - if (!dev || !bw) { - dev_err(dev, "%s: Invalid argument bw=%p\n", __func__, bw); - return ERR_PTR(-EINVAL); - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); - - if (index >= opp_table->path_count) - return ERR_PTR(-EINVAL); - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->bandwidth) { - if (temp_opp->bandwidth[index].peak >= *bw) { - opp = temp_opp; - *bw = opp->bandwidth[index].peak; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } - } - - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); + unsigned long temp = *bw; + struct dev_pm_opp *opp; + opp = _find_key_ceil(dev, &temp, index, true, _read_bw, NULL); + *bw = temp; return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_bw_ceil); @@ -791,7 +761,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_bw_ceil); /** * dev_pm_opp_find_bw_floor() - Search for a rounded floor bandwidth * @dev: device for which we do this operation - * @freq: start bandwidth + * @bw: start bandwidth * @index: which bandwidth to compare, in case of OPPs with several values * * Search for the matching floor *available* OPP from a starting bandwidth @@ -810,41 +780,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_bw_ceil); struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev, unsigned int *bw, int index) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - if (!dev || !bw) { - dev_err(dev, "%s: Invalid argument bw=%p\n", __func__, bw); - return ERR_PTR(-EINVAL); - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); - - if (index >= opp_table->path_count) - return ERR_PTR(-EINVAL); - - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->bandwidth) { - /* go to the next node, before choosing prev */ - if (temp_opp->bandwidth[index].peak > *bw) - break; - opp = temp_opp; - } - } - - /* Increment the reference count of OPP */ - if (!IS_ERR(opp)) - dev_pm_opp_get(opp); - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - if (!IS_ERR(opp)) - *bw = opp->bandwidth[index].peak; + unsigned long temp = *bw; + struct dev_pm_opp *opp; + opp = _find_key_floor(dev, &temp, index, true, _read_bw, NULL); + *bw = temp; return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_bw_floor); @@ -874,80 +814,97 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, return ret; } -static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk, - unsigned long freq) +static int +_opp_config_clk_single(struct device *dev, struct opp_table *opp_table, + struct dev_pm_opp *opp, void *data, bool scaling_down) { + unsigned long *target = data; + unsigned long freq; int ret; - /* We may reach here for devices which don't change frequency */ - if (IS_ERR(clk)) - return 0; + /* One of target and opp must be available */ + if (target) { + freq = *target; + } else if (opp) { + freq = opp->rates[0]; + } else { + WARN_ON(1); + return -EINVAL; + } - ret = clk_set_rate(clk, freq); + ret = clk_set_rate(opp_table->clk, freq); if (ret) { dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, ret); + } else { + opp_table->rate_clk_single = freq; } return ret; } -static int _generic_set_opp_regulator(struct opp_table *opp_table, - struct device *dev, - struct dev_pm_opp *opp, - unsigned long freq, - int scaling_down) +/* + * Simple implementation for configuring multiple clocks. Configure clocks in + * the order in which they are present in the array while scaling up. + */ +int dev_pm_opp_config_clks_simple(struct device *dev, + struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, + bool scaling_down) { - struct regulator *reg = opp_table->regulators[0]; - struct dev_pm_opp *old_opp = opp_table->current_opp; + int ret, i; + + if (scaling_down) { + for (i = opp_table->clk_count - 1; i >= 0; i--) { + ret = clk_set_rate(opp_table->clks[i], opp->rates[i]); + if (ret) { + dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, + ret); + return ret; + } + } + } else { + for (i = 0; i < opp_table->clk_count; i++) { + ret = clk_set_rate(opp_table->clks[i], opp->rates[i]); + if (ret) { + dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, + ret); + return ret; + } + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_config_clks_simple); + +static int _opp_config_regulator_single(struct device *dev, + struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, + struct regulator **regulators, unsigned int count) +{ + struct regulator *reg = regulators[0]; int ret; /* This function only supports single regulator per device */ - if (WARN_ON(opp_table->regulator_count > 1)) { + if (WARN_ON(count > 1)) { dev_err(dev, "multiple regulators are not supported\n"); return -EINVAL; } - /* Scaling up? Scale voltage before frequency */ - if (!scaling_down) { - ret = _set_opp_voltage(dev, reg, opp->supplies); - if (ret) - goto restore_voltage; - } - - /* Change frequency */ - ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); + ret = _set_opp_voltage(dev, reg, new_opp->supplies); if (ret) - goto restore_voltage; - - /* Scaling down? Scale voltage after frequency */ - if (scaling_down) { - ret = _set_opp_voltage(dev, reg, opp->supplies); - if (ret) - goto restore_freq; - } + return ret; /* * Enable the regulator after setting its voltages, otherwise it breaks * some boot-enabled regulators. */ - if (unlikely(!opp_table->enabled)) { + if (unlikely(!new_opp->opp_table->enabled)) { ret = regulator_enable(reg); if (ret < 0) dev_warn(dev, "Failed to enable regulator: %d", ret); } return 0; - -restore_freq: - if (_generic_set_opp_clk_only(dev, opp_table->clk, old_opp->rate)) - dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", - __func__, old_opp->rate); -restore_voltage: - /* This shouldn't harm even if the voltages weren't updated earlier */ - _set_opp_voltage(dev, reg, old_opp->supplies); - - return ret; } static int _set_opp_bw(const struct opp_table *opp_table, @@ -978,36 +935,6 @@ static int _set_opp_bw(const struct opp_table *opp_table, return 0; } -static int _set_opp_custom(const struct opp_table *opp_table, - struct device *dev, struct dev_pm_opp *opp, - unsigned long freq) -{ - struct dev_pm_set_opp_data *data = opp_table->set_opp_data; - struct dev_pm_opp *old_opp = opp_table->current_opp; - int size; - - /* - * We support this only if dev_pm_opp_set_regulators() was called - * earlier. - */ - if (opp_table->sod_supplies) { - size = sizeof(*old_opp->supplies) * opp_table->regulator_count; - memcpy(data->old_opp.supplies, old_opp->supplies, size); - memcpy(data->new_opp.supplies, opp->supplies, size); - data->regulator_count = opp_table->regulator_count; - } else { - data->regulator_count = 0; - } - - data->regulators = opp_table->regulators; - data->clk = opp_table->clk; - data->dev = dev; - data->old_opp.rate = old_opp->rate; - data->new_opp.rate = freq; - - return opp_table->set_opp(data); -} - static int _set_required_opp(struct device *dev, struct device *pd_dev, struct dev_pm_opp *opp, int i) { @@ -1019,7 +946,7 @@ static int _set_required_opp(struct device *dev, struct device *pd_dev, ret = dev_pm_genpd_set_performance_state(pd_dev, pstate); if (ret) { - dev_err(dev, "Failed to set performance rate of %s: %d (%d)\n", + dev_err(dev, "Failed to set performance state of %s: %d (%d)\n", dev_name(pd_dev), pstate, ret); } @@ -1138,7 +1065,7 @@ static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) } static int _set_opp(struct device *dev, struct opp_table *opp_table, - struct dev_pm_opp *opp, unsigned long freq) + struct dev_pm_opp *opp, void *clk_data, bool forced) { struct dev_pm_opp *old_opp; int scaling_down, ret; @@ -1153,18 +1080,17 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, old_opp = opp_table->current_opp; /* Return early if nothing to do */ - if (old_opp == opp && opp_table->current_rate == freq && - opp_table->enabled) { + if (!forced && old_opp == opp && opp_table->enabled) { dev_dbg(dev, "%s: OPPs are same, nothing to do\n", __func__); return 0; } dev_dbg(dev, "%s: switching OPP: Freq %lu -> %lu Hz, Level %u -> %u, Bw %u -> %u\n", - __func__, opp_table->current_rate, freq, old_opp->level, + __func__, old_opp->rates[0], opp->rates[0], old_opp->level, opp->level, old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0, opp->bandwidth ? opp->bandwidth[0].peak : 0); - scaling_down = _opp_compare_key(old_opp, opp); + scaling_down = _opp_compare_key(opp_table, old_opp, opp); if (scaling_down == -1) scaling_down = 0; @@ -1181,23 +1107,38 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, dev_err(dev, "Failed to set bw: %d\n", ret); return ret; } - } - if (opp_table->set_opp) { - ret = _set_opp_custom(opp_table, dev, opp, freq); - } else if (opp_table->regulators) { - ret = _generic_set_opp_regulator(opp_table, dev, opp, freq, - scaling_down); - } else { - /* Only frequency scaling */ - ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); + if (opp_table->config_regulators) { + ret = opp_table->config_regulators(dev, old_opp, opp, + opp_table->regulators, + opp_table->regulator_count); + if (ret) { + dev_err(dev, "Failed to set regulator voltages: %d\n", + ret); + return ret; + } + } } - if (ret) - return ret; + if (opp_table->config_clks) { + ret = opp_table->config_clks(dev, opp_table, opp, clk_data, scaling_down); + if (ret) + return ret; + } /* Scaling down? Configure required OPPs after frequency */ if (scaling_down) { + if (opp_table->config_regulators) { + ret = opp_table->config_regulators(dev, old_opp, opp, + opp_table->regulators, + opp_table->regulator_count); + if (ret) { + dev_err(dev, "Failed to set regulator voltages: %d\n", + ret); + return ret; + } + } + ret = _set_opp_bw(opp_table, opp, dev); if (ret) { dev_err(dev, "Failed to set bw: %d\n", ret); @@ -1217,7 +1158,6 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, /* Make sure current_opp doesn't get freed */ dev_pm_opp_get(opp); opp_table->current_opp = opp; - opp_table->current_rate = freq; return ret; } @@ -1238,6 +1178,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) struct opp_table *opp_table; unsigned long freq = 0, temp_freq; struct dev_pm_opp *opp = NULL; + bool forced = false; int ret; opp_table = _find_opp_table(dev); @@ -1255,7 +1196,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) * equivalent to a clk_set_rate() */ if (!_get_opp_count(opp_table)) { - ret = _generic_set_opp_clk_only(dev, opp_table->clk, target_freq); + ret = opp_table->config_clks(dev, opp_table, NULL, + &target_freq, false); goto put_opp_table; } @@ -1276,12 +1218,22 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) __func__, freq, ret); goto put_opp_table; } + + /* + * An OPP entry specifies the highest frequency at which other + * properties of the OPP entry apply. Even if the new OPP is + * same as the old one, we may still reach here for a different + * value of the frequency. In such a case, do not abort but + * configure the hardware to the desired frequency forcefully. + */ + forced = opp_table->rate_clk_single != target_freq; } - ret = _set_opp(dev, opp_table, opp, freq); + ret = _set_opp(dev, opp_table, opp, &target_freq, forced); if (target_freq) dev_pm_opp_put(opp); + put_opp_table: dev_pm_opp_put_opp_table(opp_table); return ret; @@ -1309,7 +1261,7 @@ int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) return PTR_ERR(opp_table); } - ret = _set_opp(dev, opp_table, opp, opp ? opp->rate : 0); + ret = _set_opp(dev, opp_table, opp, NULL, false); dev_pm_opp_put_opp_table(opp_table); return ret; @@ -1366,6 +1318,8 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) INIT_LIST_HEAD(&opp_table->dev_list); INIT_LIST_HEAD(&opp_table->lazy); + opp_table->clk = ERR_PTR(-ENODEV); + /* Mark regulator count uninitialized */ opp_table->regulator_count = -1; @@ -1412,20 +1366,38 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, int ret; /* - * Return early if we don't need to get clk or we have already tried it + * Return early if we don't need to get clk or we have already done it * earlier. */ - if (!getclk || IS_ERR(opp_table) || opp_table->clk) + if (!getclk || IS_ERR(opp_table) || !IS_ERR(opp_table->clk) || + opp_table->clks) return opp_table; /* Find clk for the device */ opp_table->clk = clk_get(dev, NULL); ret = PTR_ERR_OR_ZERO(opp_table->clk); - if (!ret) + if (!ret) { + opp_table->config_clks = _opp_config_clk_single; + opp_table->clk_count = 1; return opp_table; + } if (ret == -ENOENT) { + /* + * There are few platforms which don't want the OPP core to + * manage device's clock settings. In such cases neither the + * platform provides the clks explicitly to us, nor the DT + * contains a valid clk entry. The OPP nodes in DT may still + * contain "opp-hz" property though, which we need to parse and + * allow the platform to find an OPP based on freq later on. + * + * This is a simple solution to take care of such corner cases, + * i.e. make the clk_count 1, which lets us allocate space for + * frequency in opp->rates and also parse the entries in DT. + */ + opp_table->clk_count = 1; + dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); return opp_table; } @@ -1528,7 +1500,7 @@ static void _opp_table_kref_release(struct kref *kref) _of_clear_opp_table(opp_table); - /* Release clk */ + /* Release automatically acquired single clk */ if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); @@ -1581,7 +1553,7 @@ static void _opp_kref_release(struct kref *kref) * frequency/voltage list. */ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp); - _of_opp_free_required_opps(opp_table, opp); + _of_clear_opp(opp_table, opp); opp_debug_remove_one(opp); kfree(opp); } @@ -1613,10 +1585,13 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) if (IS_ERR(opp_table)) return; + if (!assert_single_clk(opp_table)) + goto put_table; + mutex_lock(&opp_table->lock); list_for_each_entry(iter, &opp_table->opp_list, node) { - if (iter->rate == freq) { + if (iter->rates[0] == freq) { opp = iter; break; } @@ -1634,6 +1609,7 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) __func__, freq); } +put_table: /* Drop the reference taken by _find_opp_table() */ dev_pm_opp_put_opp_table(opp_table); } @@ -1720,26 +1696,31 @@ void dev_pm_opp_remove_all_dynamic(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic); -struct dev_pm_opp *_opp_allocate(struct opp_table *table) +struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table) { struct dev_pm_opp *opp; - int supply_count, supply_size, icc_size; + int supply_count, supply_size, icc_size, clk_size; /* Allocate space for at least one supply */ - supply_count = table->regulator_count > 0 ? table->regulator_count : 1; + supply_count = opp_table->regulator_count > 0 ? + opp_table->regulator_count : 1; supply_size = sizeof(*opp->supplies) * supply_count; - icc_size = sizeof(*opp->bandwidth) * table->path_count; + clk_size = sizeof(*opp->rates) * opp_table->clk_count; + icc_size = sizeof(*opp->bandwidth) * opp_table->path_count; /* allocate new OPP node and supplies structures */ - opp = kzalloc(sizeof(*opp) + supply_size + icc_size, GFP_KERNEL); - + opp = kzalloc(sizeof(*opp) + supply_size + clk_size + icc_size, GFP_KERNEL); if (!opp) return NULL; - /* Put the supplies at the end of the OPP structure as an empty array */ + /* Put the supplies, bw and clock at the end of the OPP structure */ opp->supplies = (struct dev_pm_opp_supply *)(opp + 1); + + opp->rates = (unsigned long *)(opp->supplies + supply_count); + if (icc_size) - opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->supplies + supply_count); + opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->rates + opp_table->clk_count); + INIT_LIST_HEAD(&opp->node); return opp; @@ -1770,15 +1751,57 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, return true; } -int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2) +static int _opp_compare_rate(struct opp_table *opp_table, + struct dev_pm_opp *opp1, struct dev_pm_opp *opp2) +{ + int i; + + for (i = 0; i < opp_table->clk_count; i++) { + if (opp1->rates[i] != opp2->rates[i]) + return opp1->rates[i] < opp2->rates[i] ? -1 : 1; + } + + /* Same rates for both OPPs */ + return 0; +} + +static int _opp_compare_bw(struct opp_table *opp_table, struct dev_pm_opp *opp1, + struct dev_pm_opp *opp2) { - if (opp1->rate != opp2->rate) - return opp1->rate < opp2->rate ? -1 : 1; - if (opp1->bandwidth && opp2->bandwidth && - opp1->bandwidth[0].peak != opp2->bandwidth[0].peak) - return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 1; + int i; + + for (i = 0; i < opp_table->path_count; i++) { + if (opp1->bandwidth[i].peak != opp2->bandwidth[i].peak) + return opp1->bandwidth[i].peak < opp2->bandwidth[i].peak ? -1 : 1; + } + + /* Same bw for both OPPs */ + return 0; +} + +/* + * Returns + * 0: opp1 == opp2 + * 1: opp1 > opp2 + * -1: opp1 < opp2 + */ +int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1, + struct dev_pm_opp *opp2) +{ + int ret; + + ret = _opp_compare_rate(opp_table, opp1, opp2); + if (ret) + return ret; + + ret = _opp_compare_bw(opp_table, opp1, opp2); + if (ret) + return ret; + if (opp1->level != opp2->level) return opp1->level < opp2->level ? -1 : 1; + + /* Duplicate OPPs */ return 0; } @@ -1798,7 +1821,7 @@ static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, * loop. */ list_for_each_entry(opp, &opp_table->opp_list, node) { - opp_cmp = _opp_compare_key(new_opp, opp); + opp_cmp = _opp_compare_key(opp_table, new_opp, opp); if (opp_cmp > 0) { *head = &opp->node; continue; @@ -1809,8 +1832,8 @@ static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, /* Duplicate OPPs */ dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", - __func__, opp->rate, opp->supplies[0].u_volt, - opp->available, new_opp->rate, + __func__, opp->rates[0], opp->supplies[0].u_volt, + opp->available, new_opp->rates[0], new_opp->supplies[0].u_volt, new_opp->available); /* Should we compare voltages for all regulators here ? */ @@ -1831,7 +1854,7 @@ void _required_opps_available(struct dev_pm_opp *opp, int count) opp->available = false; pr_warn("%s: OPP not supported by required OPP %pOF (%lu)\n", - __func__, opp->required_opps[i]->np, opp->rate); + __func__, opp->required_opps[i]->np, opp->rates[0]); return; } } @@ -1847,7 +1870,7 @@ void _required_opps_available(struct dev_pm_opp *opp, int count) * should be considered an error by the callers of _opp_add(). */ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, - struct opp_table *opp_table, bool rate_not_available) + struct opp_table *opp_table) { struct list_head *head; int ret; @@ -1872,7 +1895,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, if (!_opp_supported_by_regulators(new_opp, opp_table)) { new_opp->available = false; dev_warn(dev, "%s: OPP not supported by regulators (%lu)\n", - __func__, new_opp->rate); + __func__, new_opp->rates[0]); } /* required-opps not fully initialized yet */ @@ -1913,12 +1936,15 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long tol; int ret; + if (!assert_single_clk(opp_table)) + return -EINVAL; + new_opp = _opp_allocate(opp_table); if (!new_opp) return -ENOMEM; /* populate the opp table */ - new_opp->rate = freq; + new_opp->rates[0] = freq; tol = u_volt * opp_table->voltage_tolerance_v1 / 100; new_opp->supplies[0].u_volt = u_volt; new_opp->supplies[0].u_volt_min = u_volt - tol; @@ -1926,7 +1952,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, new_opp->available = true; new_opp->dynamic = dynamic; - ret = _opp_add(dev, new_opp, opp_table, false); + ret = _opp_add(dev, new_opp, opp_table); if (ret) { /* Don't return error for duplicate OPPs */ if (ret == -EBUSY) @@ -1948,7 +1974,7 @@ free_opp: } /** - * dev_pm_opp_set_supported_hw() - Set supported platforms + * _opp_set_supported_hw() - Set supported platforms * @dev: Device for which supported-hw has to be set. * @versions: Array of hierarchy of versions to match. * @count: Number of elements in the array. @@ -1958,87 +1984,42 @@ free_opp: * OPPs, which are available for those versions, based on its 'opp-supported-hw' * property. */ -struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, - const u32 *versions, unsigned int count) +static int _opp_set_supported_hw(struct opp_table *opp_table, + const u32 *versions, unsigned int count) { - struct opp_table *opp_table; - - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; - - /* Make sure there are no concurrent readers while updating opp_table */ - WARN_ON(!list_empty(&opp_table->opp_list)); - /* Another CPU that shares the OPP table has set the property ? */ if (opp_table->supported_hw) - return opp_table; + return 0; opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions), GFP_KERNEL); - if (!opp_table->supported_hw) { - dev_pm_opp_put_opp_table(opp_table); - return ERR_PTR(-ENOMEM); - } + if (!opp_table->supported_hw) + return -ENOMEM; opp_table->supported_hw_count = count; - return opp_table; + return 0; } -EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); /** - * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw - * @opp_table: OPP table returned by dev_pm_opp_set_supported_hw(). + * _opp_put_supported_hw() - Releases resources blocked for supported hw + * @opp_table: OPP table returned by _opp_set_supported_hw(). * * This is required only for the V2 bindings, and is called for a matching - * dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure + * _opp_set_supported_hw(). Until this is called, the opp_table structure * will not be freed. */ -void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) -{ - if (unlikely(!opp_table)) - return; - - kfree(opp_table->supported_hw); - opp_table->supported_hw = NULL; - opp_table->supported_hw_count = 0; - - dev_pm_opp_put_opp_table(opp_table); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); - -static void devm_pm_opp_supported_hw_release(void *data) +static void _opp_put_supported_hw(struct opp_table *opp_table) { - dev_pm_opp_put_supported_hw(data); -} - -/** - * devm_pm_opp_set_supported_hw() - Set supported platforms - * @dev: Device for which supported-hw has to be set. - * @versions: Array of hierarchy of versions to match. - * @count: Number of elements in the array. - * - * This is a resource-managed variant of dev_pm_opp_set_supported_hw(). - * - * Return: 0 on success and errorno otherwise. - */ -int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, - unsigned int count) -{ - struct opp_table *opp_table; - - opp_table = dev_pm_opp_set_supported_hw(dev, versions, count); - if (IS_ERR(opp_table)) - return PTR_ERR(opp_table); - - return devm_add_action_or_reset(dev, devm_pm_opp_supported_hw_release, - opp_table); + if (opp_table->supported_hw) { + kfree(opp_table->supported_hw); + opp_table->supported_hw = NULL; + opp_table->supported_hw_count = 0; + } } -EXPORT_SYMBOL_GPL(devm_pm_opp_set_supported_hw); /** - * dev_pm_opp_set_prop_name() - Set prop-extn name + * _opp_set_prop_name() - Set prop-extn name * @dev: Device for which the prop-name has to be set. * @name: name to postfix to properties. * @@ -2047,53 +2028,36 @@ EXPORT_SYMBOL_GPL(devm_pm_opp_set_supported_hw); * which the extension will apply are opp-microvolt and opp-microamp. OPP core * should postfix the property name with -<name> while looking for them. */ -struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) +static int _opp_set_prop_name(struct opp_table *opp_table, const char *name) { - struct opp_table *opp_table; - - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; - - /* Make sure there are no concurrent readers while updating opp_table */ - WARN_ON(!list_empty(&opp_table->opp_list)); - /* Another CPU that shares the OPP table has set the property ? */ - if (opp_table->prop_name) - return opp_table; - - opp_table->prop_name = kstrdup(name, GFP_KERNEL); if (!opp_table->prop_name) { - dev_pm_opp_put_opp_table(opp_table); - return ERR_PTR(-ENOMEM); + opp_table->prop_name = kstrdup(name, GFP_KERNEL); + if (!opp_table->prop_name) + return -ENOMEM; } - return opp_table; + return 0; } -EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); /** - * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name - * @opp_table: OPP table returned by dev_pm_opp_set_prop_name(). + * _opp_put_prop_name() - Releases resources blocked for prop-name + * @opp_table: OPP table returned by _opp_set_prop_name(). * * This is required only for the V2 bindings, and is called for a matching - * dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure + * _opp_set_prop_name(). Until this is called, the opp_table structure * will not be freed. */ -void dev_pm_opp_put_prop_name(struct opp_table *opp_table) +static void _opp_put_prop_name(struct opp_table *opp_table) { - if (unlikely(!opp_table)) - return; - - kfree(opp_table->prop_name); - opp_table->prop_name = NULL; - - dev_pm_opp_put_opp_table(opp_table); + if (opp_table->prop_name) { + kfree(opp_table->prop_name); + opp_table->prop_name = NULL; + } } -EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); /** - * dev_pm_opp_set_regulators() - Set regulator names for the device + * _opp_set_regulators() - Set regulator names for the device * @dev: Device for which regulator name is being set. * @names: Array of pointers to the names of the regulator. * @count: Number of regulators. @@ -2104,36 +2068,29 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); * * This must be called before any OPPs are initialized for the device. */ -struct opp_table *dev_pm_opp_set_regulators(struct device *dev, - const char * const names[], - unsigned int count) +static int _opp_set_regulators(struct opp_table *opp_table, struct device *dev, + const char * const names[]) { - struct dev_pm_opp_supply *supplies; - struct opp_table *opp_table; + const char * const *temp = names; struct regulator *reg; - int ret, i; + int count = 0, ret, i; - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; + /* Count number of regulators */ + while (*temp++) + count++; - /* This should be called before OPPs are initialized */ - if (WARN_ON(!list_empty(&opp_table->opp_list))) { - ret = -EBUSY; - goto err; - } + if (!count) + return -EINVAL; /* Another CPU that shares the OPP table has set the regulators ? */ if (opp_table->regulators) - return opp_table; + return 0; opp_table->regulators = kmalloc_array(count, sizeof(*opp_table->regulators), GFP_KERNEL); - if (!opp_table->regulators) { - ret = -ENOMEM; - goto err; - } + if (!opp_table->regulators) + return -ENOMEM; for (i = 0; i < count; i++) { reg = regulator_get_optional(dev, names[i]); @@ -2149,21 +2106,11 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, opp_table->regulator_count = count; - supplies = kmalloc_array(count * 2, sizeof(*supplies), GFP_KERNEL); - if (!supplies) { - ret = -ENOMEM; - goto free_regulators; - } - - mutex_lock(&opp_table->lock); - opp_table->sod_supplies = supplies; - if (opp_table->set_opp_data) { - opp_table->set_opp_data->old_opp.supplies = supplies; - opp_table->set_opp_data->new_opp.supplies = supplies + count; - } - mutex_unlock(&opp_table->lock); + /* Set generic config_regulators() for single regulators here */ + if (count == 1) + opp_table->config_regulators = _opp_config_regulator_single; - return opp_table; + return 0; free_regulators: while (i != 0) @@ -2172,26 +2119,20 @@ free_regulators: kfree(opp_table->regulators); opp_table->regulators = NULL; opp_table->regulator_count = -1; -err: - dev_pm_opp_put_opp_table(opp_table); - return ERR_PTR(ret); + return ret; } -EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulators); /** - * dev_pm_opp_put_regulators() - Releases resources blocked for regulator - * @opp_table: OPP table returned from dev_pm_opp_set_regulators(). + * _opp_put_regulators() - Releases resources blocked for regulator + * @opp_table: OPP table returned from _opp_set_regulators(). */ -void dev_pm_opp_put_regulators(struct opp_table *opp_table) +static void _opp_put_regulators(struct opp_table *opp_table) { int i; - if (unlikely(!opp_table)) - return; - if (!opp_table->regulators) - goto put_opp_table; + return; if (opp_table->enabled) { for (i = opp_table->regulator_count - 1; i >= 0; i--) @@ -2201,252 +2142,158 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) for (i = opp_table->regulator_count - 1; i >= 0; i--) regulator_put(opp_table->regulators[i]); - mutex_lock(&opp_table->lock); - if (opp_table->set_opp_data) { - opp_table->set_opp_data->old_opp.supplies = NULL; - opp_table->set_opp_data->new_opp.supplies = NULL; - } - - kfree(opp_table->sod_supplies); - opp_table->sod_supplies = NULL; - mutex_unlock(&opp_table->lock); - kfree(opp_table->regulators); opp_table->regulators = NULL; opp_table->regulator_count = -1; - -put_opp_table: - dev_pm_opp_put_opp_table(opp_table); } -EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); -static void devm_pm_opp_regulators_release(void *data) +static void _put_clks(struct opp_table *opp_table, int count) { - dev_pm_opp_put_regulators(data); -} - -/** - * devm_pm_opp_set_regulators() - Set regulator names for the device - * @dev: Device for which regulator name is being set. - * @names: Array of pointers to the names of the regulator. - * @count: Number of regulators. - * - * This is a resource-managed variant of dev_pm_opp_set_regulators(). - * - * Return: 0 on success and errorno otherwise. - */ -int devm_pm_opp_set_regulators(struct device *dev, - const char * const names[], - unsigned int count) -{ - struct opp_table *opp_table; + int i; - opp_table = dev_pm_opp_set_regulators(dev, names, count); - if (IS_ERR(opp_table)) - return PTR_ERR(opp_table); + for (i = count - 1; i >= 0; i--) + clk_put(opp_table->clks[i]); - return devm_add_action_or_reset(dev, devm_pm_opp_regulators_release, - opp_table); + kfree(opp_table->clks); + opp_table->clks = NULL; } -EXPORT_SYMBOL_GPL(devm_pm_opp_set_regulators); /** - * dev_pm_opp_set_clkname() - Set clk name for the device - * @dev: Device for which clk name is being set. - * @name: Clk name. - * - * In order to support OPP switching, OPP layer needs to get pointer to the - * clock for the device. Simple cases work fine without using this routine (i.e. - * by passing connection-id as NULL), but for a device with multiple clocks - * available, the OPP core needs to know the exact name of the clk to use. + * _opp_set_clknames() - Set clk names for the device + * @dev: Device for which clk names is being set. + * @names: Clk names. + * + * In order to support OPP switching, OPP layer needs to get pointers to the + * clocks for the device. Simple cases work fine without using this routine + * (i.e. by passing connection-id as NULL), but for a device with multiple + * clocks available, the OPP core needs to know the exact names of the clks to + * use. * * This must be called before any OPPs are initialized for the device. */ -struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) +static int _opp_set_clknames(struct opp_table *opp_table, struct device *dev, + const char * const names[], + config_clks_t config_clks) { - struct opp_table *opp_table; - int ret; + const char * const *temp = names; + int count = 0, ret, i; + struct clk *clk; - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; + /* Count number of clks */ + while (*temp++) + count++; - /* This should be called before OPPs are initialized */ - if (WARN_ON(!list_empty(&opp_table->opp_list))) { - ret = -EBUSY; - goto err; - } + /* + * This is a special case where we have a single clock, whose connection + * id name is NULL, i.e. first two entries are NULL in the array. + */ + if (!count && !names[1]) + count = 1; - /* clk shouldn't be initialized at this point */ - if (WARN_ON(opp_table->clk)) { - ret = -EBUSY; - goto err; - } + /* Fail early for invalid configurations */ + if (!count || (!config_clks && count > 1)) + return -EINVAL; - /* Find clk for the device */ - opp_table->clk = clk_get(dev, name); - if (IS_ERR(opp_table->clk)) { - ret = dev_err_probe(dev, PTR_ERR(opp_table->clk), - "%s: Couldn't find clock\n", __func__); - goto err; - } + /* Another CPU that shares the OPP table has set the clkname ? */ + if (opp_table->clks) + return 0; - return opp_table; + opp_table->clks = kmalloc_array(count, sizeof(*opp_table->clks), + GFP_KERNEL); + if (!opp_table->clks) + return -ENOMEM; -err: - dev_pm_opp_put_opp_table(opp_table); + /* Find clks for the device */ + for (i = 0; i < count; i++) { + clk = clk_get(dev, names[i]); + if (IS_ERR(clk)) { + ret = dev_err_probe(dev, PTR_ERR(clk), + "%s: Couldn't find clock with name: %s\n", + __func__, names[i]); + goto free_clks; + } - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_set_clkname); + opp_table->clks[i] = clk; + } -/** - * dev_pm_opp_put_clkname() - Releases resources blocked for clk. - * @opp_table: OPP table returned from dev_pm_opp_set_clkname(). - */ -void dev_pm_opp_put_clkname(struct opp_table *opp_table) -{ - if (unlikely(!opp_table)) - return; + opp_table->clk_count = count; + opp_table->config_clks = config_clks; - clk_put(opp_table->clk); - opp_table->clk = ERR_PTR(-EINVAL); + /* Set generic single clk set here */ + if (count == 1) { + if (!opp_table->config_clks) + opp_table->config_clks = _opp_config_clk_single; - dev_pm_opp_put_opp_table(opp_table); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); + /* + * We could have just dropped the "clk" field and used "clks" + * everywhere. Instead we kept the "clk" field around for + * following reasons: + * + * - avoiding clks[0] everywhere else. + * - not running single clk helpers for multiple clk usecase by + * mistake. + * + * Since this is single-clk case, just update the clk pointer + * too. + */ + opp_table->clk = opp_table->clks[0]; + } -static void devm_pm_opp_clkname_release(void *data) -{ - dev_pm_opp_put_clkname(data); + return 0; + +free_clks: + _put_clks(opp_table, i); + return ret; } /** - * devm_pm_opp_set_clkname() - Set clk name for the device - * @dev: Device for which clk name is being set. - * @name: Clk name. - * - * This is a resource-managed variant of dev_pm_opp_set_clkname(). - * - * Return: 0 on success and errorno otherwise. + * _opp_put_clknames() - Releases resources blocked for clks. + * @opp_table: OPP table returned from _opp_set_clknames(). */ -int devm_pm_opp_set_clkname(struct device *dev, const char *name) +static void _opp_put_clknames(struct opp_table *opp_table) { - struct opp_table *opp_table; + if (!opp_table->clks) + return; - opp_table = dev_pm_opp_set_clkname(dev, name); - if (IS_ERR(opp_table)) - return PTR_ERR(opp_table); + opp_table->config_clks = NULL; + opp_table->clk = ERR_PTR(-ENODEV); - return devm_add_action_or_reset(dev, devm_pm_opp_clkname_release, - opp_table); + _put_clks(opp_table, opp_table->clk_count); } -EXPORT_SYMBOL_GPL(devm_pm_opp_set_clkname); /** - * dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper + * _opp_set_config_regulators_helper() - Register custom set regulator helper. * @dev: Device for which the helper is getting registered. - * @set_opp: Custom set OPP helper. + * @config_regulators: Custom set regulator helper. * - * This is useful to support complex platforms (like platforms with multiple - * regulators per device), instead of the generic OPP set rate helper. + * This is useful to support platforms with multiple regulators per device. * * This must be called before any OPPs are initialized for the device. */ -struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) +static int _opp_set_config_regulators_helper(struct opp_table *opp_table, + struct device *dev, config_regulators_t config_regulators) { - struct dev_pm_set_opp_data *data; - struct opp_table *opp_table; - - if (!set_opp) - return ERR_PTR(-EINVAL); - - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; - - /* This should be called before OPPs are initialized */ - if (WARN_ON(!list_empty(&opp_table->opp_list))) { - dev_pm_opp_put_opp_table(opp_table); - return ERR_PTR(-EBUSY); - } - /* Another CPU that shares the OPP table has set the helper ? */ - if (opp_table->set_opp) - return opp_table; + if (!opp_table->config_regulators) + opp_table->config_regulators = config_regulators; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - mutex_lock(&opp_table->lock); - opp_table->set_opp_data = data; - if (opp_table->sod_supplies) { - data->old_opp.supplies = opp_table->sod_supplies; - data->new_opp.supplies = opp_table->sod_supplies + - opp_table->regulator_count; - } - mutex_unlock(&opp_table->lock); - - opp_table->set_opp = set_opp; - - return opp_table; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); - -/** - * dev_pm_opp_unregister_set_opp_helper() - Releases resources blocked for - * set_opp helper - * @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper(). - * - * Release resources blocked for platform specific set_opp helper. - */ -void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) -{ - if (unlikely(!opp_table)) - return; - - opp_table->set_opp = NULL; - - mutex_lock(&opp_table->lock); - kfree(opp_table->set_opp_data); - opp_table->set_opp_data = NULL; - mutex_unlock(&opp_table->lock); - - dev_pm_opp_put_opp_table(opp_table); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); - -static void devm_pm_opp_unregister_set_opp_helper(void *data) -{ - dev_pm_opp_unregister_set_opp_helper(data); + return 0; } /** - * devm_pm_opp_register_set_opp_helper() - Register custom set OPP helper - * @dev: Device for which the helper is getting registered. - * @set_opp: Custom set OPP helper. - * - * This is a resource-managed version of dev_pm_opp_register_set_opp_helper(). + * _opp_put_config_regulators_helper() - Releases resources blocked for + * config_regulators helper. + * @opp_table: OPP table returned from _opp_set_config_regulators_helper(). * - * Return: 0 on success and errorno otherwise. + * Release resources blocked for platform specific config_regulators helper. */ -int devm_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) +static void _opp_put_config_regulators_helper(struct opp_table *opp_table) { - struct opp_table *opp_table; - - opp_table = dev_pm_opp_register_set_opp_helper(dev, set_opp); - if (IS_ERR(opp_table)) - return PTR_ERR(opp_table); - - return devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, - opp_table); + if (opp_table->config_regulators) + opp_table->config_regulators = NULL; } -EXPORT_SYMBOL_GPL(devm_pm_opp_register_set_opp_helper); -static void _opp_detach_genpd(struct opp_table *opp_table) +static void _detach_genpd(struct opp_table *opp_table) { int index; @@ -2466,7 +2313,7 @@ static void _opp_detach_genpd(struct opp_table *opp_table) } /** - * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer + * _opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer * @dev: Consumer device for which the genpd is getting attached. * @names: Null terminated array of pointers containing names of genpd to attach. * @virt_devs: Pointer to return the array of virtual devices. @@ -2487,30 +2334,23 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * The order of entries in the names array must match the order in which * "required-opps" are added in DT. */ -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, - const char * const *names, struct device ***virt_devs) +static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, + const char * const *names, struct device ***virt_devs) { - struct opp_table *opp_table; struct device *virt_dev; int index = 0, ret = -EINVAL; const char * const *name = names; - opp_table = _add_opp_table(dev, false); - if (IS_ERR(opp_table)) - return opp_table; - if (opp_table->genpd_virt_devs) - return opp_table; + return 0; /* * If the genpd's OPP table isn't already initialized, parsing of the * required-opps fail for dev. We should retry this after genpd's OPP * table is added. */ - if (!opp_table->required_opp_count) { - ret = -EPROBE_DEFER; - goto put_table; - } + if (!opp_table->required_opp_count) + return -EPROBE_DEFER; mutex_lock(&opp_table->genpd_virt_dev_lock); @@ -2528,8 +2368,8 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, } virt_dev = dev_pm_domain_attach_by_name(dev, *name); - if (IS_ERR(virt_dev)) { - ret = PTR_ERR(virt_dev); + if (IS_ERR_OR_NULL(virt_dev)) { + ret = PTR_ERR(virt_dev) ? : -ENODEV; dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret); goto err; } @@ -2543,73 +2383,230 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, *virt_devs = opp_table->genpd_virt_devs; mutex_unlock(&opp_table->genpd_virt_dev_lock); - return opp_table; + return 0; err: - _opp_detach_genpd(opp_table); + _detach_genpd(opp_table); unlock: mutex_unlock(&opp_table->genpd_virt_dev_lock); + return ret; -put_table: - dev_pm_opp_put_opp_table(opp_table); - - return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd); /** - * dev_pm_opp_detach_genpd() - Detach genpd(s) from the device. - * @opp_table: OPP table returned by dev_pm_opp_attach_genpd(). + * _opp_detach_genpd() - Detach genpd(s) from the device. + * @opp_table: OPP table returned by _opp_attach_genpd(). * * This detaches the genpd(s), resets the virtual device pointers, and puts the * OPP table. */ -void dev_pm_opp_detach_genpd(struct opp_table *opp_table) +static void _opp_detach_genpd(struct opp_table *opp_table) { - if (unlikely(!opp_table)) - return; - /* * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting * used in parallel. */ mutex_lock(&opp_table->genpd_virt_dev_lock); - _opp_detach_genpd(opp_table); + _detach_genpd(opp_table); mutex_unlock(&opp_table->genpd_virt_dev_lock); - - dev_pm_opp_put_opp_table(opp_table); } -EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd); -static void devm_pm_opp_detach_genpd(void *data) +static void _opp_clear_config(struct opp_config_data *data) { - dev_pm_opp_detach_genpd(data); + if (data->flags & OPP_CONFIG_GENPD) + _opp_detach_genpd(data->opp_table); + if (data->flags & OPP_CONFIG_REGULATOR) + _opp_put_regulators(data->opp_table); + if (data->flags & OPP_CONFIG_SUPPORTED_HW) + _opp_put_supported_hw(data->opp_table); + if (data->flags & OPP_CONFIG_REGULATOR_HELPER) + _opp_put_config_regulators_helper(data->opp_table); + if (data->flags & OPP_CONFIG_PROP_NAME) + _opp_put_prop_name(data->opp_table); + if (data->flags & OPP_CONFIG_CLK) + _opp_put_clknames(data->opp_table); + + dev_pm_opp_put_opp_table(data->opp_table); + kfree(data); } /** - * devm_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual - * device pointer - * @dev: Consumer device for which the genpd is getting attached. - * @names: Null terminated array of pointers containing names of genpd to attach. - * @virt_devs: Pointer to return the array of virtual devices. + * dev_pm_opp_set_config() - Set OPP configuration for the device. + * @dev: Device for which configuration is being set. + * @config: OPP configuration. * - * This is a resource-managed version of dev_pm_opp_attach_genpd(). + * This allows all device OPP configurations to be performed at once. * - * Return: 0 on success and errorno otherwise. + * This must be called before any OPPs are initialized for the device. This may + * be called multiple times for the same OPP table, for example once for each + * CPU that share the same table. This must be balanced by the same number of + * calls to dev_pm_opp_clear_config() in order to free the OPP table properly. + * + * This returns a token to the caller, which must be passed to + * dev_pm_opp_clear_config() to free the resources later. The value of the + * returned token will be >= 1 for success and negative for errors. The minimum + * value of 1 is chosen here to make it easy for callers to manage the resource. */ -int devm_pm_opp_attach_genpd(struct device *dev, const char * const *names, - struct device ***virt_devs) +int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) { struct opp_table *opp_table; + struct opp_config_data *data; + unsigned int id; + int ret; - opp_table = dev_pm_opp_attach_genpd(dev, names, virt_devs); - if (IS_ERR(opp_table)) + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + opp_table = _add_opp_table(dev, false); + if (IS_ERR(opp_table)) { + kfree(data); return PTR_ERR(opp_table); + } + + data->opp_table = opp_table; + data->flags = 0; + + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } + + /* Configure clocks */ + if (config->clk_names) { + ret = _opp_set_clknames(opp_table, dev, config->clk_names, + config->config_clks); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_CLK; + } else if (config->config_clks) { + /* Don't allow config callback without clocks */ + ret = -EINVAL; + goto err; + } + + /* Configure property names */ + if (config->prop_name) { + ret = _opp_set_prop_name(opp_table, config->prop_name); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_PROP_NAME; + } + + /* Configure config_regulators helper */ + if (config->config_regulators) { + ret = _opp_set_config_regulators_helper(opp_table, dev, + config->config_regulators); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_REGULATOR_HELPER; + } - return devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, - opp_table); + /* Configure supported hardware */ + if (config->supported_hw) { + ret = _opp_set_supported_hw(opp_table, config->supported_hw, + config->supported_hw_count); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_SUPPORTED_HW; + } + + /* Configure supplies */ + if (config->regulator_names) { + ret = _opp_set_regulators(opp_table, dev, + config->regulator_names); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_REGULATOR; + } + + /* Attach genpds */ + if (config->genpd_names) { + ret = _opp_attach_genpd(opp_table, dev, config->genpd_names, + config->virt_devs); + if (ret) + goto err; + + data->flags |= OPP_CONFIG_GENPD; + } + + ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX), + GFP_KERNEL); + if (ret) + goto err; + + return id; + +err: + _opp_clear_config(data); + return ret; } -EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd); +EXPORT_SYMBOL_GPL(dev_pm_opp_set_config); + +/** + * dev_pm_opp_clear_config() - Releases resources blocked for OPP configuration. + * @opp_table: OPP table returned from dev_pm_opp_set_config(). + * + * This allows all device OPP configurations to be cleared at once. This must be + * called once for each call made to dev_pm_opp_set_config(), in order to free + * the OPPs properly. + * + * Currently the first call itself ends up freeing all the OPP configurations, + * while the later ones only drop the OPP table reference. This works well for + * now as we would never want to use an half initialized OPP table and want to + * remove the configurations together. + */ +void dev_pm_opp_clear_config(int token) +{ + struct opp_config_data *data; + + /* + * This lets the callers call this unconditionally and keep their code + * simple. + */ + if (unlikely(token <= 0)) + return; + + data = xa_erase(&opp_configs, token); + if (WARN_ON(!data)) + return; + + _opp_clear_config(data); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_clear_config); + +static void devm_pm_opp_config_release(void *token) +{ + dev_pm_opp_clear_config((unsigned long)token); +} + +/** + * devm_pm_opp_set_config() - Set OPP configuration for the device. + * @dev: Device for which configuration is being set. + * @config: OPP configuration. + * + * This allows all device OPP configurations to be performed at once. + * This is a resource-managed variant of dev_pm_opp_set_config(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) +{ + int token = dev_pm_opp_set_config(dev, config); + + if (token < 0) + return token; + + return devm_add_action_or_reset(dev, devm_pm_opp_config_release, + (void *) ((unsigned long) token)); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_config); /** * dev_pm_opp_xlate_required_opp() - Find required OPP for @src_table OPP. @@ -2795,11 +2792,16 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, return r; } + if (!assert_single_clk(opp_table)) { + r = -EINVAL; + goto put_table; + } + mutex_lock(&opp_table->lock); /* Do we have the frequency? */ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { - if (tmp_opp->rate == freq) { + if (tmp_opp->rates[0] == freq) { opp = tmp_opp; break; } @@ -2866,11 +2868,16 @@ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, return r; } + if (!assert_single_clk(opp_table)) { + r = -EINVAL; + goto put_table; + } + mutex_lock(&opp_table->lock); /* Do we have the frequency? */ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { - if (tmp_opp->rate == freq) { + if (tmp_opp->rates[0] == freq) { opp = tmp_opp; break; } @@ -2897,11 +2904,11 @@ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, opp); dev_pm_opp_put(opp); - goto adjust_put_table; + goto put_table; adjust_unlock: mutex_unlock(&opp_table->lock); -adjust_put_table: +put_table: dev_pm_opp_put_opp_table(opp_table); return r; } diff --git a/drivers/opp/cpu.c b/drivers/opp/cpu.c index 5004335cf0de..3c3506021501 100644 --- a/drivers/opp/cpu.c +++ b/drivers/opp/cpu.c @@ -41,7 +41,7 @@ * the table if any of the mentioned functions have been invoked in the interim. */ int dev_pm_opp_init_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) + struct cpufreq_frequency_table **opp_table) { struct dev_pm_opp *opp; struct cpufreq_frequency_table *freq_table = NULL; @@ -76,7 +76,7 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev, freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; - *table = &freq_table[0]; + *opp_table = &freq_table[0]; out: if (ret) @@ -94,13 +94,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); * Free up the table allocated by dev_pm_opp_init_cpufreq_table */ void dev_pm_opp_free_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) + struct cpufreq_frequency_table **opp_table) { - if (!table) + if (!opp_table) return; - kfree(*table); - *table = NULL; + kfree(*opp_table); + *opp_table = NULL; } EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c index 1b6e5c55c3ed..96a30a032c5f 100644 --- a/drivers/opp/debugfs.c +++ b/drivers/opp/debugfs.c @@ -74,6 +74,24 @@ static void opp_debug_create_bw(struct dev_pm_opp *opp, } } +static void opp_debug_create_clks(struct dev_pm_opp *opp, + struct opp_table *opp_table, + struct dentry *pdentry) +{ + char name[12]; + int i; + + if (opp_table->clk_count == 1) { + debugfs_create_ulong("rate_hz", S_IRUGO, pdentry, &opp->rates[0]); + return; + } + + for (i = 0; i < opp_table->clk_count; i++) { + snprintf(name, sizeof(name), "rate_hz_%d", i); + debugfs_create_ulong(name, S_IRUGO, pdentry, &opp->rates[i]); + } +} + static void opp_debug_create_supplies(struct dev_pm_opp *opp, struct opp_table *opp_table, struct dentry *pdentry) @@ -117,10 +135,11 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) * Get directory name for OPP. * * - Normally rate is unique to each OPP, use it to get unique opp-name. - * - For some devices rate isn't available, use index instead. + * - For some devices rate isn't available or there are multiple, use + * index instead for them. */ - if (likely(opp->rate)) - id = opp->rate; + if (likely(opp_table->clk_count == 1 && opp->rates[0])) + id = opp->rates[0]; else id = _get_opp_count(opp_table); @@ -134,7 +153,6 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo); debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend); debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate); - debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate); debugfs_create_u32("level", S_IRUGO, d, &opp->level); debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, &opp->clock_latency_ns); @@ -142,6 +160,7 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) opp->of_name = of_node_full_name(opp->np); debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name); + opp_debug_create_clks(opp, opp_table, d); opp_debug_create_supplies(opp, opp_table, d); opp_debug_create_bw(opp, opp_table, d); diff --git a/drivers/opp/of.c b/drivers/opp/of.c index eb89c9a75985..605d68673f92 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -242,20 +242,20 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, opp_table->np = opp_np; _opp_table_alloc_required_tables(opp_table, dev, opp_np); - of_node_put(opp_np); } void _of_clear_opp_table(struct opp_table *opp_table) { _opp_table_free_required_tables(opp_table); + of_node_put(opp_table->np); } /* * Release all resources previously acquired with a call to * _of_opp_alloc_required_opps(). */ -void _of_opp_free_required_opps(struct opp_table *opp_table, - struct dev_pm_opp *opp) +static void _of_opp_free_required_opps(struct opp_table *opp_table, + struct dev_pm_opp *opp) { struct dev_pm_opp **required_opps = opp->required_opps; int i; @@ -275,6 +275,12 @@ void _of_opp_free_required_opps(struct opp_table *opp_table, kfree(required_opps); } +void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) +{ + _of_opp_free_required_opps(opp_table, opp); + of_node_put(opp->np); +} + /* Populate all required OPPs which are part of "required-opps" list */ static int _of_opp_alloc_required_opps(struct opp_table *opp_table, struct dev_pm_opp *opp) @@ -767,7 +773,51 @@ void dev_pm_opp_of_remove_table(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); -static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table, +static int _read_rate(struct dev_pm_opp *new_opp, struct opp_table *opp_table, + struct device_node *np) +{ + struct property *prop; + int i, count, ret; + u64 *rates; + + prop = of_find_property(np, "opp-hz", NULL); + if (!prop) + return -ENODEV; + + count = prop->length / sizeof(u64); + if (opp_table->clk_count != count) { + pr_err("%s: Count mismatch between opp-hz and clk_count (%d %d)\n", + __func__, count, opp_table->clk_count); + return -EINVAL; + } + + rates = kmalloc_array(count, sizeof(*rates), GFP_KERNEL); + if (!rates) + return -ENOMEM; + + ret = of_property_read_u64_array(np, "opp-hz", rates, count); + if (ret) { + pr_err("%s: Error parsing opp-hz: %d\n", __func__, ret); + } else { + /* + * Rate is defined as an unsigned long in clk API, and so + * casting explicitly to its type. Must be fixed once rate is 64 + * bit guaranteed in clk API. + */ + for (i = 0; i < count; i++) { + new_opp->rates[i] = (unsigned long)rates[i]; + + /* This will happen for frequencies > 4.29 GHz */ + WARN_ON(new_opp->rates[i] != rates[i]); + } + } + + kfree(rates); + + return ret; +} + +static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table, struct device_node *np, bool peak) { const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps"; @@ -780,9 +830,9 @@ static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table, return -ENODEV; count = prop->length / sizeof(u32); - if (table->path_count != count) { + if (opp_table->path_count != count) { pr_err("%s: Mismatch between %s and paths (%d %d)\n", - __func__, name, count, table->path_count); + __func__, name, count, opp_table->path_count); return -EINVAL; } @@ -808,34 +858,27 @@ out: return ret; } -static int _read_opp_key(struct dev_pm_opp *new_opp, struct opp_table *table, - struct device_node *np, bool *rate_not_available) +static int _read_opp_key(struct dev_pm_opp *new_opp, + struct opp_table *opp_table, struct device_node *np) { bool found = false; - u64 rate; int ret; - ret = of_property_read_u64(np, "opp-hz", &rate); - if (!ret) { - /* - * Rate is defined as an unsigned long in clk API, and so - * casting explicitly to its type. Must be fixed once rate is 64 - * bit guaranteed in clk API. - */ - new_opp->rate = (unsigned long)rate; + ret = _read_rate(new_opp, opp_table, np); + if (!ret) found = true; - } - *rate_not_available = !!ret; + else if (ret != -ENODEV) + return ret; /* * Bandwidth consists of peak and average (optional) values: * opp-peak-kBps = <path1_value path2_value>; * opp-avg-kBps = <path1_value path2_value>; */ - ret = _read_bw(new_opp, table, np, true); + ret = _read_bw(new_opp, opp_table, np, true); if (!ret) { found = true; - ret = _read_bw(new_opp, table, np, false); + ret = _read_bw(new_opp, opp_table, np, false); } /* The properties were found but we failed to parse them */ @@ -881,13 +924,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, struct dev_pm_opp *new_opp; u32 val; int ret; - bool rate_not_available = false; new_opp = _opp_allocate(opp_table); if (!new_opp) return ERR_PTR(-ENOMEM); - ret = _read_opp_key(new_opp, opp_table, np, &rate_not_available); + ret = _read_opp_key(new_opp, opp_table, np); if (ret < 0) { dev_err(dev, "%s: opp key field not found\n", __func__); goto free_opp; @@ -895,14 +937,14 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, /* Check if the OPP supports hardware's hierarchy of versions or not */ if (!_opp_is_supported(dev, opp_table, np)) { - dev_dbg(dev, "OPP not supported by hardware: %lu\n", - new_opp->rate); + dev_dbg(dev, "OPP not supported by hardware: %s\n", + of_node_full_name(np)); goto free_opp; } new_opp->turbo = of_property_read_bool(np, "turbo-mode"); - new_opp->np = np; + new_opp->np = of_node_get(np); new_opp->dynamic = false; new_opp->available = true; @@ -920,7 +962,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, if (opp_table->is_genpd) new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp); - ret = _opp_add(dev, new_opp, opp_table, rate_not_available); + ret = _opp_add(dev, new_opp, opp_table); if (ret) { /* Don't return error for duplicate OPPs */ if (ret == -EBUSY) @@ -931,8 +973,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, /* OPP to select on device suspend */ if (of_property_read_bool(np, "opp-suspend")) { if (opp_table->suspend_opp) { - /* Pick the OPP with higher rate as suspend OPP */ - if (new_opp->rate > opp_table->suspend_opp->rate) { + /* Pick the OPP with higher rate/bw/level as suspend OPP */ + if (_opp_compare_key(opp_table, new_opp, opp_table->suspend_opp) == 1) { opp_table->suspend_opp->suspend = false; new_opp->suspend = true; opp_table->suspend_opp = new_opp; @@ -947,7 +989,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n", - __func__, new_opp->turbo, new_opp->rate, + __func__, new_opp->turbo, new_opp->rates[0], new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min, new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns, new_opp->level); @@ -1084,7 +1126,7 @@ remove_static_opp: return ret; } -static int _of_add_table_indexed(struct device *dev, int index, bool getclk) +static int _of_add_table_indexed(struct device *dev, int index) { struct opp_table *opp_table; int ret, count; @@ -1100,7 +1142,7 @@ static int _of_add_table_indexed(struct device *dev, int index, bool getclk) index = 0; } - opp_table = _add_opp_table_indexed(dev, index, getclk); + opp_table = _add_opp_table_indexed(dev, index, true); if (IS_ERR(opp_table)) return PTR_ERR(opp_table); @@ -1124,11 +1166,11 @@ static void devm_pm_opp_of_table_release(void *data) dev_pm_opp_of_remove_table(data); } -static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk) +static int _devm_of_add_table_indexed(struct device *dev, int index) { int ret; - ret = _of_add_table_indexed(dev, index, getclk); + ret = _of_add_table_indexed(dev, index); if (ret) return ret; @@ -1156,7 +1198,7 @@ static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk */ int devm_pm_opp_of_add_table(struct device *dev) { - return _devm_of_add_table_indexed(dev, 0, true); + return _devm_of_add_table_indexed(dev, 0); } EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table); @@ -1179,7 +1221,7 @@ EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table); */ int dev_pm_opp_of_add_table(struct device *dev) { - return _of_add_table_indexed(dev, 0, true); + return _of_add_table_indexed(dev, 0); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); @@ -1195,7 +1237,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); */ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) { - return _of_add_table_indexed(dev, index, true); + return _of_add_table_indexed(dev, index); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); @@ -1208,42 +1250,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); */ int devm_pm_opp_of_add_table_indexed(struct device *dev, int index) { - return _devm_of_add_table_indexed(dev, index, true); + return _devm_of_add_table_indexed(dev, index); } EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_indexed); -/** - * dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device - * tree without getting clk for device. - * @dev: device pointer used to lookup OPP table. - * @index: Index number. - * - * Register the initial OPP table with the OPP library for given device only - * using the "operating-points-v2" property. Do not try to get the clk for the - * device. - * - * Return: Refer to dev_pm_opp_of_add_table() for return values. - */ -int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) -{ - return _of_add_table_indexed(dev, index, false); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk); - -/** - * devm_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device - * tree without getting clk for device. - * @dev: device pointer used to lookup OPP table. - * @index: Index number. - * - * This is a resource-managed variant of dev_pm_opp_of_add_table_noclk(). - */ -int devm_pm_opp_of_add_table_noclk(struct device *dev, int index) -{ - return _devm_of_add_table_indexed(dev, index, false); -} -EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_noclk); - /* CPU device specific helpers */ /** diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 45e3a55239a1..3a6e077df386 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -28,6 +28,27 @@ extern struct mutex opp_table_lock; extern struct list_head opp_tables, lazy_opp_tables; +/* OPP Config flags */ +#define OPP_CONFIG_CLK BIT(0) +#define OPP_CONFIG_REGULATOR BIT(1) +#define OPP_CONFIG_REGULATOR_HELPER BIT(2) +#define OPP_CONFIG_PROP_NAME BIT(3) +#define OPP_CONFIG_SUPPORTED_HW BIT(4) +#define OPP_CONFIG_GENPD BIT(5) + +/** + * struct opp_config_data - data for set config operations + * @opp_table: OPP table + * @flags: OPP config flags + * + * This structure stores the OPP config information for each OPP table + * configuration by the callers. + */ +struct opp_config_data { + struct opp_table *opp_table; + unsigned int flags; +}; + /* * Internal data structure organization with the OPP layer library is as * follows: @@ -58,7 +79,7 @@ extern struct list_head opp_tables, lazy_opp_tables; * @suspend: true if suspend OPP * @removed: flag indicating that OPP's reference is dropped by OPP core. * @pstate: Device's power domain's performance state. - * @rate: Frequency in hertz + * @rates: Frequencies in hertz * @level: Performance level * @supplies: Power supplies voltage/current values * @bandwidth: Interconnect bandwidth values @@ -81,7 +102,7 @@ struct dev_pm_opp { bool suspend; bool removed; unsigned int pstate; - unsigned long rate; + unsigned long *rates; unsigned int level; struct dev_pm_opp_supply *supplies; @@ -138,7 +159,7 @@ enum opp_table_access { * @clock_latency_ns_max: Max clock latency in nanoseconds. * @parsed_static_opps: Count of devices for which OPPs are initialized from DT. * @shared_opp: OPP is shared between multiple devices. - * @current_rate: Currently configured frequency. + * @rate_clk_single: Currently configured frequency for single clk. * @current_opp: Currently configured OPP for the table. * @suspend_opp: Pointer to OPP to be used during device suspend. * @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers. @@ -149,7 +170,11 @@ enum opp_table_access { * @supported_hw: Array of version number to support. * @supported_hw_count: Number of elements in supported_hw array. * @prop_name: A name to postfix to many DT properties, while parsing them. - * @clk: Device's clock handle + * @config_clks: Platform specific config_clks() callback. + * @clks: Device's clock handles, for multiple clocks. + * @clk: Device's clock handle, for single clock. + * @clk_count: Number of clocks. + * @config_regulators: Platform specific config_regulators() callback. * @regulators: Supply regulators * @regulator_count: Number of power supply regulators. Its value can be -1 * (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt @@ -159,9 +184,6 @@ enum opp_table_access { * @enabled: Set to true if the device's resources are enabled/configured. * @genpd_performance_state: Device's power domain support performance state. * @is_genpd: Marks if the OPP table belongs to a genpd. - * @set_opp: Platform specific set_opp callback - * @sod_supplies: Set opp data supplies - * @set_opp_data: Data to be passed to set_opp callback * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. * @@ -188,7 +210,7 @@ struct opp_table { unsigned int parsed_static_opps; enum opp_table_access shared_opp; - unsigned long current_rate; + unsigned long rate_clk_single; struct dev_pm_opp *current_opp; struct dev_pm_opp *suspend_opp; @@ -200,7 +222,11 @@ struct opp_table { unsigned int *supported_hw; unsigned int supported_hw_count; const char *prop_name; + config_clks_t config_clks; + struct clk **clks; struct clk *clk; + int clk_count; + config_regulators_t config_regulators; struct regulator **regulators; int regulator_count; struct icc_path **paths; @@ -209,10 +235,6 @@ struct opp_table { bool genpd_performance_state; bool is_genpd; - int (*set_opp)(struct dev_pm_set_opp_data *data); - struct dev_pm_opp_supply *sod_supplies; - struct dev_pm_set_opp_data *set_opp_data; - #ifdef CONFIG_DEBUG_FS struct dentry *dentry; char dentry_name[NAME_MAX]; @@ -228,8 +250,8 @@ struct opp_table *_find_opp_table(struct device *dev); struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table); void _opp_free(struct dev_pm_opp *opp); -int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); -int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available); +int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); +int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); @@ -245,14 +267,12 @@ static inline bool lazy_linking_pending(struct opp_table *opp_table) void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index); void _of_clear_opp_table(struct opp_table *opp_table); struct opp_table *_managed_opp(struct device *dev, int index); -void _of_opp_free_required_opps(struct opp_table *opp_table, - struct dev_pm_opp *opp); +void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp); #else static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) {} static inline void _of_clear_opp_table(struct opp_table *opp_table) {} static inline struct opp_table *_managed_opp(struct device *dev, int index) { return NULL; } -static inline void _of_opp_free_required_opps(struct opp_table *opp_table, - struct dev_pm_opp *opp) {} +static inline void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) {} #endif #ifdef CONFIG_DEBUG_FS diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c index bd4771f388ab..8f3f13fbbb25 100644 --- a/drivers/opp/ti-opp-supply.c +++ b/drivers/opp/ti-opp-supply.c @@ -36,11 +36,15 @@ struct ti_opp_supply_optimum_voltage_table { * @vdd_table: Optimized voltage mapping table * @num_vdd_table: number of entries in vdd_table * @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply + * @old_supplies: Placeholder for supplies information for old OPP. + * @new_supplies: Placeholder for supplies information for new OPP. */ struct ti_opp_supply_data { struct ti_opp_supply_optimum_voltage_table *vdd_table; u32 num_vdd_table; u32 vdd_absolute_max_voltage_uv; + struct dev_pm_opp_supply old_supplies[2]; + struct dev_pm_opp_supply new_supplies[2]; }; static struct ti_opp_supply_data opp_data; @@ -266,27 +270,32 @@ static int _opp_set_voltage(struct device *dev, return 0; } -/** - * ti_opp_supply_set_opp() - do the opp supply transition - * @data: information on regulators and new and old opps provided by - * opp core to use in transition - * - * Return: If successful, 0, else appropriate error value. - */ -static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data) +/* Do the opp supply transition */ +static int ti_opp_config_regulators(struct device *dev, + struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, + struct regulator **regulators, unsigned int count) { - struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0]; - struct dev_pm_opp_supply *old_supply_vbb = &data->old_opp.supplies[1]; - struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0]; - struct dev_pm_opp_supply *new_supply_vbb = &data->new_opp.supplies[1]; - struct device *dev = data->dev; - unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; - struct clk *clk = data->clk; - struct regulator *vdd_reg = data->regulators[0]; - struct regulator *vbb_reg = data->regulators[1]; + struct dev_pm_opp_supply *old_supply_vdd = &opp_data.old_supplies[0]; + struct dev_pm_opp_supply *old_supply_vbb = &opp_data.old_supplies[1]; + struct dev_pm_opp_supply *new_supply_vdd = &opp_data.new_supplies[0]; + struct dev_pm_opp_supply *new_supply_vbb = &opp_data.new_supplies[1]; + struct regulator *vdd_reg = regulators[0]; + struct regulator *vbb_reg = regulators[1]; + unsigned long old_freq, freq; int vdd_uv; int ret; + /* We must have two regulators here */ + WARN_ON(count != 2); + + /* Fetch supplies and freq information from OPP core */ + ret = dev_pm_opp_get_supplies(new_opp, opp_data.new_supplies); + WARN_ON(ret); + + old_freq = dev_pm_opp_get_freq(old_opp); + freq = dev_pm_opp_get_freq(new_opp); + WARN_ON(!old_freq || !freq); + vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data, new_supply_vdd->u_volt); @@ -303,39 +312,24 @@ static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data) ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb"); if (ret) goto restore_voltage; - } - - /* Change frequency */ - dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", - __func__, old_freq, freq); - - ret = clk_set_rate(clk, freq); - if (ret) { - dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, - ret); - goto restore_voltage; - } - - /* Scaling down? Scale voltage after frequency */ - if (freq < old_freq) { + } else { ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb"); if (ret) - goto restore_freq; + goto restore_voltage; ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg, "vdd"); if (ret) - goto restore_freq; + goto restore_voltage; } return 0; -restore_freq: - ret = clk_set_rate(clk, old_freq); - if (ret) - dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", - __func__, old_freq); restore_voltage: + /* Fetch old supplies information only if required */ + ret = dev_pm_opp_get_supplies(old_opp, opp_data.old_supplies); + WARN_ON(ret); + /* This shouldn't harm even if the voltages weren't updated earlier */ if (old_supply_vdd->u_volt) { ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb"); @@ -405,9 +399,8 @@ static int ti_opp_supply_probe(struct platform_device *pdev) return ret; } - ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev, - ti_opp_supply_set_opp)); - if (ret) + ret = dev_pm_opp_set_config_regulators(cpu_dev, ti_opp_config_regulators); + if (ret < 0) _free_optimized_voltages(dev, &opp_data); return ret; diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 5cc7cba1941f..55c028af4bd9 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -121,6 +121,9 @@ config XEN_PCIDEV_FRONTEND config PCI_ATS bool +config PCI_DOE + bool + config PCI_ECAM bool diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 0da6b1ebc694..2680e4c92f0a 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o obj-$(CONFIG_VGA_ARB) += vgaarb.o +obj-$(CONFIG_PCI_DOE) += doe.o # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c new file mode 100644 index 000000000000..e402f05068a5 --- /dev/null +++ b/drivers/pci/doe.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Data Object Exchange + * PCIe r6.0, sec 6.30 DOE + * + * Copyright (C) 2021 Huawei + * Jonathan Cameron <Jonathan.Cameron@huawei.com> + * + * Copyright (C) 2022 Intel Corporation + * Ira Weiny <ira.weiny@intel.com> + */ + +#define dev_fmt(fmt) "DOE: " fmt + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/pci-doe.h> +#include <linux/workqueue.h> + +#define PCI_DOE_PROTOCOL_DISCOVERY 0 + +/* Timeout of 1 second from 6.30.2 Operation, PCI Spec r6.0 */ +#define PCI_DOE_TIMEOUT HZ +#define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128) + +#define PCI_DOE_FLAG_CANCEL 0 +#define PCI_DOE_FLAG_DEAD 1 + +/** + * struct pci_doe_mb - State for a single DOE mailbox + * + * This state is used to manage a single DOE mailbox capability. All fields + * should be considered opaque to the consumers and the structure passed into + * the helpers below after being created by devm_pci_doe_create() + * + * @pdev: PCI device this mailbox belongs to + * @cap_offset: Capability offset + * @prots: Array of protocols supported (encoded as long values) + * @wq: Wait queue for work item + * @work_queue: Queue of pci_doe_work items + * @flags: Bit array of PCI_DOE_FLAG_* flags + */ +struct pci_doe_mb { + struct pci_dev *pdev; + u16 cap_offset; + struct xarray prots; + + wait_queue_head_t wq; + struct workqueue_struct *work_queue; + unsigned long flags; +}; + +static int pci_doe_wait(struct pci_doe_mb *doe_mb, unsigned long timeout) +{ + if (wait_event_timeout(doe_mb->wq, + test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags), + timeout)) + return -EIO; + return 0; +} + +static void pci_doe_write_ctrl(struct pci_doe_mb *doe_mb, u32 val) +{ + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + + pci_write_config_dword(pdev, offset + PCI_DOE_CTRL, val); +} + +static int pci_doe_abort(struct pci_doe_mb *doe_mb) +{ + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + unsigned long timeout_jiffies; + + pci_dbg(pdev, "[%x] Issuing Abort\n", offset); + + timeout_jiffies = jiffies + PCI_DOE_TIMEOUT; + pci_doe_write_ctrl(doe_mb, PCI_DOE_CTRL_ABORT); + + do { + int rc; + u32 val; + + rc = pci_doe_wait(doe_mb, PCI_DOE_POLL_INTERVAL); + if (rc) + return rc; + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + + /* Abort success! */ + if (!FIELD_GET(PCI_DOE_STATUS_ERROR, val) && + !FIELD_GET(PCI_DOE_STATUS_BUSY, val)) + return 0; + + } while (!time_after(jiffies, timeout_jiffies)); + + /* Abort has timed out and the MB is dead */ + pci_err(pdev, "[%x] ABORT timed out\n", offset); + return -EIO; +} + +static int pci_doe_send_req(struct pci_doe_mb *doe_mb, + struct pci_doe_task *task) +{ + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + u32 val; + int i; + + /* + * Check the DOE busy bit is not set. If it is set, this could indicate + * someone other than Linux (e.g. firmware) is using the mailbox. Note + * it is expected that firmware and OS will negotiate access rights via + * an, as yet to be defined, method. + */ + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + if (FIELD_GET(PCI_DOE_STATUS_BUSY, val)) + return -EBUSY; + + if (FIELD_GET(PCI_DOE_STATUS_ERROR, val)) + return -EIO; + + /* Write DOE Header */ + val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->prot.vid) | + FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->prot.type); + pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val); + /* Length is 2 DW of header + length of payload in DW */ + pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, + FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, + 2 + task->request_pl_sz / + sizeof(u32))); + for (i = 0; i < task->request_pl_sz / sizeof(u32); i++) + pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, + task->request_pl[i]); + + pci_doe_write_ctrl(doe_mb, PCI_DOE_CTRL_GO); + + return 0; +} + +static bool pci_doe_data_obj_ready(struct pci_doe_mb *doe_mb) +{ + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + u32 val; + + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + if (FIELD_GET(PCI_DOE_STATUS_DATA_OBJECT_READY, val)) + return true; + return false; +} + +static int pci_doe_recv_resp(struct pci_doe_mb *doe_mb, struct pci_doe_task *task) +{ + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + size_t length, payload_length; + u32 val; + int i; + + /* Read the first dword to get the protocol */ + pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val); + if ((FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val) != task->prot.vid) || + (FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val) != task->prot.type)) { + dev_err_ratelimited(&pdev->dev, "[%x] expected [VID, Protocol] = [%04x, %02x], got [%04x, %02x]\n", + doe_mb->cap_offset, task->prot.vid, task->prot.type, + FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val), + FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val)); + return -EIO; + } + + pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); + /* Read the second dword to get the length */ + pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val); + pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); + + length = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, val); + if (length > SZ_1M || length < 2) + return -EIO; + + /* First 2 dwords have already been read */ + length -= 2; + payload_length = min(length, task->response_pl_sz / sizeof(u32)); + /* Read the rest of the response payload */ + for (i = 0; i < payload_length; i++) { + pci_read_config_dword(pdev, offset + PCI_DOE_READ, + &task->response_pl[i]); + /* Prior to the last ack, ensure Data Object Ready */ + if (i == (payload_length - 1) && !pci_doe_data_obj_ready(doe_mb)) + return -EIO; + pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); + } + + /* Flush excess length */ + for (; i < length; i++) { + pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val); + pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); + } + + /* Final error check to pick up on any since Data Object Ready */ + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + if (FIELD_GET(PCI_DOE_STATUS_ERROR, val)) + return -EIO; + + return min(length, task->response_pl_sz / sizeof(u32)) * sizeof(u32); +} + +static void signal_task_complete(struct pci_doe_task *task, int rv) +{ + task->rv = rv; + task->complete(task); +} + +static void signal_task_abort(struct pci_doe_task *task, int rv) +{ + struct pci_doe_mb *doe_mb = task->doe_mb; + struct pci_dev *pdev = doe_mb->pdev; + + if (pci_doe_abort(doe_mb)) { + /* + * If the device can't process an abort; set the mailbox dead + * - no more submissions + */ + pci_err(pdev, "[%x] Abort failed marking mailbox dead\n", + doe_mb->cap_offset); + set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags); + } + signal_task_complete(task, rv); +} + +static void doe_statemachine_work(struct work_struct *work) +{ + struct pci_doe_task *task = container_of(work, struct pci_doe_task, + work); + struct pci_doe_mb *doe_mb = task->doe_mb; + struct pci_dev *pdev = doe_mb->pdev; + int offset = doe_mb->cap_offset; + unsigned long timeout_jiffies; + u32 val; + int rc; + + if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) { + signal_task_complete(task, -EIO); + return; + } + + /* Send request */ + rc = pci_doe_send_req(doe_mb, task); + if (rc) { + /* + * The specification does not provide any guidance on how to + * resolve conflicting requests from other entities. + * Furthermore, it is likely that busy will not be detected + * most of the time. Flag any detection of status busy with an + * error. + */ + if (rc == -EBUSY) + dev_err_ratelimited(&pdev->dev, "[%x] busy detected; another entity is sending conflicting requests\n", + offset); + signal_task_abort(task, rc); + return; + } + + timeout_jiffies = jiffies + PCI_DOE_TIMEOUT; + /* Poll for response */ +retry_resp: + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + if (FIELD_GET(PCI_DOE_STATUS_ERROR, val)) { + signal_task_abort(task, -EIO); + return; + } + + if (!FIELD_GET(PCI_DOE_STATUS_DATA_OBJECT_READY, val)) { + if (time_after(jiffies, timeout_jiffies)) { + signal_task_abort(task, -EIO); + return; + } + rc = pci_doe_wait(doe_mb, PCI_DOE_POLL_INTERVAL); + if (rc) { + signal_task_abort(task, rc); + return; + } + goto retry_resp; + } + + rc = pci_doe_recv_resp(doe_mb, task); + if (rc < 0) { + signal_task_abort(task, rc); + return; + } + + signal_task_complete(task, rc); +} + +static void pci_doe_task_complete(struct pci_doe_task *task) +{ + complete(task->private); +} + +static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 *index, u16 *vid, + u8 *protocol) +{ + u32 request_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, + *index); + u32 response_pl; + DECLARE_COMPLETION_ONSTACK(c); + struct pci_doe_task task = { + .prot.vid = PCI_VENDOR_ID_PCI_SIG, + .prot.type = PCI_DOE_PROTOCOL_DISCOVERY, + .request_pl = &request_pl, + .request_pl_sz = sizeof(request_pl), + .response_pl = &response_pl, + .response_pl_sz = sizeof(response_pl), + .complete = pci_doe_task_complete, + .private = &c, + }; + int rc; + + rc = pci_doe_submit_task(doe_mb, &task); + if (rc < 0) + return rc; + + wait_for_completion(&c); + + if (task.rv != sizeof(response_pl)) + return -EIO; + + *vid = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, response_pl); + *protocol = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL, + response_pl); + *index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, + response_pl); + + return 0; +} + +static void *pci_doe_xa_prot_entry(u16 vid, u8 prot) +{ + return xa_mk_value((vid << 8) | prot); +} + +static int pci_doe_cache_protocols(struct pci_doe_mb *doe_mb) +{ + u8 index = 0; + u8 xa_idx = 0; + + do { + int rc; + u16 vid; + u8 prot; + + rc = pci_doe_discovery(doe_mb, &index, &vid, &prot); + if (rc) + return rc; + + pci_dbg(doe_mb->pdev, + "[%x] Found protocol %d vid: %x prot: %x\n", + doe_mb->cap_offset, xa_idx, vid, prot); + + rc = xa_insert(&doe_mb->prots, xa_idx++, + pci_doe_xa_prot_entry(vid, prot), GFP_KERNEL); + if (rc) + return rc; + } while (index); + + return 0; +} + +static void pci_doe_xa_destroy(void *mb) +{ + struct pci_doe_mb *doe_mb = mb; + + xa_destroy(&doe_mb->prots); +} + +static void pci_doe_destroy_workqueue(void *mb) +{ + struct pci_doe_mb *doe_mb = mb; + + destroy_workqueue(doe_mb->work_queue); +} + +static void pci_doe_flush_mb(void *mb) +{ + struct pci_doe_mb *doe_mb = mb; + + /* Stop all pending work items from starting */ + set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags); + + /* Cancel an in progress work item, if necessary */ + set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags); + wake_up(&doe_mb->wq); + + /* Flush all work items */ + flush_workqueue(doe_mb->work_queue); +} + +/** + * pcim_doe_create_mb() - Create a DOE mailbox object + * + * @pdev: PCI device to create the DOE mailbox for + * @cap_offset: Offset of the DOE mailbox + * + * Create a single mailbox object to manage the mailbox protocol at the + * cap_offset specified. + * + * RETURNS: created mailbox object on success + * ERR_PTR(-errno) on failure + */ +struct pci_doe_mb *pcim_doe_create_mb(struct pci_dev *pdev, u16 cap_offset) +{ + struct pci_doe_mb *doe_mb; + struct device *dev = &pdev->dev; + int rc; + + doe_mb = devm_kzalloc(dev, sizeof(*doe_mb), GFP_KERNEL); + if (!doe_mb) + return ERR_PTR(-ENOMEM); + + doe_mb->pdev = pdev; + doe_mb->cap_offset = cap_offset; + init_waitqueue_head(&doe_mb->wq); + + xa_init(&doe_mb->prots); + rc = devm_add_action(dev, pci_doe_xa_destroy, doe_mb); + if (rc) + return ERR_PTR(rc); + + doe_mb->work_queue = alloc_ordered_workqueue("%s %s DOE [%x]", 0, + dev_driver_string(&pdev->dev), + pci_name(pdev), + doe_mb->cap_offset); + if (!doe_mb->work_queue) { + pci_err(pdev, "[%x] failed to allocate work queue\n", + doe_mb->cap_offset); + return ERR_PTR(-ENOMEM); + } + rc = devm_add_action_or_reset(dev, pci_doe_destroy_workqueue, doe_mb); + if (rc) + return ERR_PTR(rc); + + /* Reset the mailbox by issuing an abort */ + rc = pci_doe_abort(doe_mb); + if (rc) { + pci_err(pdev, "[%x] failed to reset mailbox with abort command : %d\n", + doe_mb->cap_offset, rc); + return ERR_PTR(rc); + } + + /* + * The state machine and the mailbox should be in sync now; + * Set up mailbox flush prior to using the mailbox to query protocols. + */ + rc = devm_add_action_or_reset(dev, pci_doe_flush_mb, doe_mb); + if (rc) + return ERR_PTR(rc); + + rc = pci_doe_cache_protocols(doe_mb); + if (rc) { + pci_err(pdev, "[%x] failed to cache protocols : %d\n", + doe_mb->cap_offset, rc); + return ERR_PTR(rc); + } + + return doe_mb; +} +EXPORT_SYMBOL_GPL(pcim_doe_create_mb); + +/** + * pci_doe_supports_prot() - Return if the DOE instance supports the given + * protocol + * @doe_mb: DOE mailbox capability to query + * @vid: Protocol Vendor ID + * @type: Protocol type + * + * RETURNS: True if the DOE mailbox supports the protocol specified + */ +bool pci_doe_supports_prot(struct pci_doe_mb *doe_mb, u16 vid, u8 type) +{ + unsigned long index; + void *entry; + + /* The discovery protocol must always be supported */ + if (vid == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_PROTOCOL_DISCOVERY) + return true; + + xa_for_each(&doe_mb->prots, index, entry) + if (entry == pci_doe_xa_prot_entry(vid, type)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(pci_doe_supports_prot); + +/** + * pci_doe_submit_task() - Submit a task to be processed by the state machine + * + * @doe_mb: DOE mailbox capability to submit to + * @task: task to be queued + * + * Submit a DOE task (request/response) to the DOE mailbox to be processed. + * Returns upon queueing the task object. If the queue is full this function + * will sleep until there is room in the queue. + * + * task->complete will be called when the state machine is done processing this + * task. + * + * Excess data will be discarded. + * + * RETURNS: 0 when task has been successfully queued, -ERRNO on error + */ +int pci_doe_submit_task(struct pci_doe_mb *doe_mb, struct pci_doe_task *task) +{ + if (!pci_doe_supports_prot(doe_mb, task->prot.vid, task->prot.type)) + return -EINVAL; + + /* + * DOE requests must be a whole number of DW and the response needs to + * be big enough for at least 1 DW + */ + if (task->request_pl_sz % sizeof(u32) || + task->response_pl_sz < sizeof(u32)) + return -EINVAL; + + if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) + return -EIO; + + task->doe_mb = doe_mb; + INIT_WORK(&task->work, doe_statemachine_work); + queue_work(doe_mb->work_queue, &task->work); + return 0; +} +EXPORT_SYMBOL_GPL(pci_doe_submit_task); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9884d8b29d3b..c5286b027f00 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2315,7 +2315,7 @@ EXPORT_SYMBOL(pci_alloc_dev); static bool pci_bus_crs_vendor_id(u32 l) { - return (l & 0xffff) == 0x0001; + return (l & 0xffff) == PCI_VENDOR_ID_PCI_SIG; } static bool pci_bus_wait_crs(struct pci_bus *bus, int devfn, u32 *l, diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index bff144c97e66..1cf74b0c42e5 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -311,7 +311,7 @@ config PINCTRL_MICROCHIP_SGPIO LED controller. config PINCTRL_OCELOT - bool "Pinctrl driver for the Microsemi Ocelot and Jaguar2 SoCs" + tristate "Pinctrl driver for the Microsemi Ocelot and Jaguar2 SoCs" depends on OF depends on HAS_IOMEM select GPIOLIB diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pinmux-aspeed.h index 4d7548686f39..aaa78a613196 100644 --- a/drivers/pinctrl/aspeed/pinmux-aspeed.h +++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h @@ -632,7 +632,7 @@ struct aspeed_pin_desc { SIG_EXPR_LIST_ALIAS(pin, sig, group) /** - * Similar to the above, but for pins with a dual expressions (DE) and + * Similar to the above, but for pins with a dual expressions (DE) * and a single group (SG) of pins. * * @pin: The pin the signal will be routed to diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index dad453054776..7857e612a100 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -507,7 +507,7 @@ static void bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc, } } -static void bcm2835_gpio_irq_enable(struct irq_data *data) +static void bcm2835_gpio_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); @@ -516,13 +516,15 @@ static void bcm2835_gpio_irq_enable(struct irq_data *data) unsigned bank = GPIO_REG_OFFSET(gpio); unsigned long flags; + gpiochip_enable_irq(chip, gpio); + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); set_bit(offset, &pc->enabled_irq_map[bank]); bcm2835_gpio_irq_config(pc, gpio, true); raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); } -static void bcm2835_gpio_irq_disable(struct irq_data *data) +static void bcm2835_gpio_irq_mask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); @@ -537,6 +539,8 @@ static void bcm2835_gpio_irq_disable(struct irq_data *data) bcm2835_gpio_set_bit(pc, GPEDS0, gpio); clear_bit(offset, &pc->enabled_irq_map[bank]); raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); + + gpiochip_disable_irq(chip, gpio); } static int __bcm2835_gpio_irq_set_type_disabled(struct bcm2835_pinctrl *pc, @@ -693,16 +697,15 @@ static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on) return ret; } -static struct irq_chip bcm2835_gpio_irq_chip = { +static const struct irq_chip bcm2835_gpio_irq_chip = { .name = MODULE_NAME, - .irq_enable = bcm2835_gpio_irq_enable, - .irq_disable = bcm2835_gpio_irq_disable, .irq_set_type = bcm2835_gpio_irq_set_type, .irq_ack = bcm2835_gpio_irq_ack, - .irq_mask = bcm2835_gpio_irq_disable, - .irq_unmask = bcm2835_gpio_irq_enable, + .irq_mask = bcm2835_gpio_irq_mask, + .irq_unmask = bcm2835_gpio_irq_unmask, .irq_set_wake = bcm2835_gpio_irq_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, + .flags = (IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE), + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev) @@ -1280,7 +1283,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev) pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range); girq = &pc->gpio_chip.irq; - girq->chip = &bcm2835_gpio_irq_chip; + gpio_irq_chip_set_chip(girq, &bcm2835_gpio_irq_chip); girq->parent_handler = bcm2835_gpio_irq_handler; girq->num_parents = BCM2835_NUM_IRQS; girq->parents = devm_kcalloc(dev, BCM2835_NUM_IRQS, diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index ffe39336fcac..9e57f4c62e60 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -126,7 +126,7 @@ struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) - if (pctldev->dev->of_node == np) { + if (device_match_of_node(pctldev->dev, np)) { mutex_unlock(&pinctrldev_list_mutex); return pctldev; } diff --git a/drivers/pinctrl/freescale/pinctrl-imx93.c b/drivers/pinctrl/freescale/pinctrl-imx93.c index 417e41b37a6f..91b3ee1e6fa9 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx93.c +++ b/drivers/pinctrl/freescale/pinctrl-imx93.c @@ -247,6 +247,7 @@ static const struct of_device_id imx93_pinctrl_of_match[] = { { .compatible = "fsl,imx93-iomuxc", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx93_pinctrl_of_match); static int imx93_pinctrl_probe(struct platform_device *pdev) { diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig index e5ec8b8956da..078eec8af4a4 100644 --- a/drivers/pinctrl/intel/Kconfig +++ b/drivers/pinctrl/intel/Kconfig @@ -151,6 +151,14 @@ config PINCTRL_LEWISBURG This pinctrl driver provides an interface that allows configuring of Intel Lewisburg pins and using them as GPIOs. +config PINCTRL_METEORLAKE + tristate "Intel Meteor Lake pinctrl and GPIO driver" + depends on ACPI + select PINCTRL_INTEL + help + This pinctrl driver provides an interface that allows configuring + of Intel Meteor Lake pins and using them as GPIOs. + config PINCTRL_SUNRISEPOINT tristate "Intel Sunrisepoint pinctrl and GPIO driver" depends on ACPI diff --git a/drivers/pinctrl/intel/Makefile b/drivers/pinctrl/intel/Makefile index 181ffcf34d62..bb87e7bc7b20 100644 --- a/drivers/pinctrl/intel/Makefile +++ b/drivers/pinctrl/intel/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_PINCTRL_ICELAKE) += pinctrl-icelake.o obj-$(CONFIG_PINCTRL_JASPERLAKE) += pinctrl-jasperlake.o obj-$(CONFIG_PINCTRL_LAKEFIELD) += pinctrl-lakefield.o obj-$(CONFIG_PINCTRL_LEWISBURG) += pinctrl-lewisburg.o +obj-$(CONFIG_PINCTRL_METEORLAKE) += pinctrl-meteorlake.o obj-$(CONFIG_PINCTRL_SUNRISEPOINT) += pinctrl-sunrisepoint.o obj-$(CONFIG_PINCTRL_TIGERLAKE) += pinctrl-tigerlake.o diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 31f8f271628c..67db79f38051 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -603,7 +603,7 @@ static const char *byt_get_group_name(struct pinctrl_dev *pctldev, { struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctldev); - return vg->soc->groups[selector].name; + return vg->soc->groups[selector].grp.name; } static int byt_get_group_pins(struct pinctrl_dev *pctldev, @@ -613,8 +613,8 @@ static int byt_get_group_pins(struct pinctrl_dev *pctldev, { struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctldev); - *pins = vg->soc->groups[selector].pins; - *num_pins = vg->soc->groups[selector].npins; + *pins = vg->soc->groups[selector].grp.pins; + *num_pins = vg->soc->groups[selector].grp.npins; return 0; } @@ -662,15 +662,15 @@ static void byt_set_group_simple_mux(struct intel_pinctrl *vg, raw_spin_lock_irqsave(&byt_lock, flags); - for (i = 0; i < group.npins; i++) { + for (i = 0; i < group.grp.npins; i++) { void __iomem *padcfg0; u32 value; - padcfg0 = byt_gpio_reg(vg, group.pins[i], BYT_CONF0_REG); + padcfg0 = byt_gpio_reg(vg, group.grp.pins[i], BYT_CONF0_REG); if (!padcfg0) { dev_warn(vg->dev, "Group %s, pin %i not muxed (no padcfg0)\n", - group.name, i); + group.grp.name, i); continue; } @@ -692,15 +692,15 @@ static void byt_set_group_mixed_mux(struct intel_pinctrl *vg, raw_spin_lock_irqsave(&byt_lock, flags); - for (i = 0; i < group.npins; i++) { + for (i = 0; i < group.grp.npins; i++) { void __iomem *padcfg0; u32 value; - padcfg0 = byt_gpio_reg(vg, group.pins[i], BYT_CONF0_REG); + padcfg0 = byt_gpio_reg(vg, group.grp.pins[i], BYT_CONF0_REG); if (!padcfg0) { dev_warn(vg->dev, "Group %s, pin %i not muxed (no padcfg0)\n", - group.name, i); + group.grp.name, i); continue; } diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 26b2a425d201..5c4fd16e5b01 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -627,7 +627,7 @@ static const char *chv_get_group_name(struct pinctrl_dev *pctldev, { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - return pctrl->soc->groups[group].name; + return pctrl->soc->groups[group].grp.name; } static int chv_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, @@ -635,8 +635,8 @@ static int chv_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - *pins = pctrl->soc->groups[group].pins; - *npins = pctrl->soc->groups[group].npins; + *pins = pctrl->soc->groups[group].grp.pins; + *npins = pctrl->soc->groups[group].grp.npins; return 0; } @@ -721,16 +721,16 @@ static int chv_pinmux_set_mux(struct pinctrl_dev *pctldev, raw_spin_lock_irqsave(&chv_lock, flags); /* Check first that the pad is not locked */ - for (i = 0; i < grp->npins; i++) { - if (chv_pad_locked(pctrl, grp->pins[i])) { + for (i = 0; i < grp->grp.npins; i++) { + if (chv_pad_locked(pctrl, grp->grp.pins[i])) { raw_spin_unlock_irqrestore(&chv_lock, flags); - dev_warn(dev, "unable to set mode for locked pin %u\n", grp->pins[i]); + dev_warn(dev, "unable to set mode for locked pin %u\n", grp->grp.pins[i]); return -EBUSY; } } - for (i = 0; i < grp->npins; i++) { - int pin = grp->pins[i]; + for (i = 0; i < grp->grp.npins; i++) { + int pin = grp->grp.pins[i]; unsigned int mode; bool invert_oe; u32 value; diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index fd093e36c3a8..52ecd66ce357 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -279,7 +279,7 @@ static const char *intel_get_group_name(struct pinctrl_dev *pctldev, { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - return pctrl->soc->groups[group].name; + return pctrl->soc->groups[group].grp.name; } static int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, @@ -287,8 +287,8 @@ static int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - *pins = pctrl->soc->groups[group].pins; - *npins = pctrl->soc->groups[group].npins; + *pins = pctrl->soc->groups[group].grp.pins; + *npins = pctrl->soc->groups[group].grp.npins; return 0; } @@ -391,19 +391,19 @@ static int intel_pinmux_set_mux(struct pinctrl_dev *pctldev, * All pins in the groups needs to be accessible and writable * before we can enable the mux for this group. */ - for (i = 0; i < grp->npins; i++) { - if (!intel_pad_usable(pctrl, grp->pins[i])) { + for (i = 0; i < grp->grp.npins; i++) { + if (!intel_pad_usable(pctrl, grp->grp.pins[i])) { raw_spin_unlock_irqrestore(&pctrl->lock, flags); return -EBUSY; } } /* Now enable the mux setting for each pin in the group */ - for (i = 0; i < grp->npins; i++) { + for (i = 0; i < grp->grp.npins; i++) { void __iomem *padcfg0; u32 value; - padcfg0 = intel_get_padcfg(pctrl, grp->pins[i], PADCFG0); + padcfg0 = intel_get_padcfg(pctrl, grp->grp.pins[i], PADCFG0); value = readl(padcfg0); value &= ~PADCFG0_PMODE_MASK; diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h index 710341bb67cc..65628423bf63 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.h +++ b/drivers/pinctrl/intel/pinctrl-intel.h @@ -24,17 +24,12 @@ struct device; /** * struct intel_pingroup - Description about group of pins - * @name: Name of the groups - * @pins: All pins in this group - * @npins: Number of pins in this groups - * @mode: Native mode in which the group is muxed out @pins. Used if @modes - * is %NULL. + * @grp: Generic data of the pin group (name and pins) + * @mode: Native mode in which the group is muxed out @pins. Used if @modes is %NULL. * @modes: If not %NULL this will hold mode for each pin in @pins */ struct intel_pingroup { - const char *name; - const unsigned int *pins; - size_t npins; + struct pingroup grp; unsigned short mode; const unsigned int *modes; }; @@ -156,15 +151,11 @@ struct intel_community { * a single integer or an array of integers in which case mode is per * pin. */ -#define PIN_GROUP(n, p, m) \ - { \ - .name = (n), \ - .pins = (p), \ - .npins = ARRAY_SIZE((p)), \ - .mode = __builtin_choose_expr( \ - __builtin_constant_p((m)), (m), 0), \ - .modes = __builtin_choose_expr( \ - __builtin_constant_p((m)), NULL, (m)), \ +#define PIN_GROUP(n, p, m) \ + { \ + .grp = PINCTRL_PINGROUP((n), (p), ARRAY_SIZE((p))), \ + .mode = __builtin_choose_expr(__builtin_constant_p((m)), (m), 0), \ + .modes = __builtin_choose_expr(__builtin_constant_p((m)), NULL, (m)), \ } #define FUNCTION(n, g) \ diff --git a/drivers/pinctrl/intel/pinctrl-lynxpoint.c b/drivers/pinctrl/intel/pinctrl-lynxpoint.c index 4fb39eb30902..5d1abee30f8f 100644 --- a/drivers/pinctrl/intel/pinctrl-lynxpoint.c +++ b/drivers/pinctrl/intel/pinctrl-lynxpoint.c @@ -282,7 +282,7 @@ static const char *lp_get_group_name(struct pinctrl_dev *pctldev, { struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); - return lg->soc->groups[selector].name; + return lg->soc->groups[selector].grp.name; } static int lp_get_group_pins(struct pinctrl_dev *pctldev, @@ -292,8 +292,8 @@ static int lp_get_group_pins(struct pinctrl_dev *pctldev, { struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); - *pins = lg->soc->groups[selector].pins; - *num_pins = lg->soc->groups[selector].npins; + *pins = lg->soc->groups[selector].grp.pins; + *num_pins = lg->soc->groups[selector].grp.npins; return 0; } @@ -366,8 +366,8 @@ static int lp_pinmux_set_mux(struct pinctrl_dev *pctldev, raw_spin_lock_irqsave(&lg->lock, flags); /* Now enable the mux setting for each pin in the group */ - for (i = 0; i < grp->npins; i++) { - void __iomem *reg = lp_gpio_reg(&lg->chip, grp->pins[i], LP_CONFIG1); + for (i = 0; i < grp->grp.npins; i++) { + void __iomem *reg = lp_gpio_reg(&lg->chip, grp->grp.pins[i], LP_CONFIG1); u32 value; value = ioread32(reg); diff --git a/drivers/pinctrl/intel/pinctrl-merrifield.c b/drivers/pinctrl/intel/pinctrl-merrifield.c index 3ae141e0b421..5e752818adb4 100644 --- a/drivers/pinctrl/intel/pinctrl-merrifield.c +++ b/drivers/pinctrl/intel/pinctrl-merrifield.c @@ -520,7 +520,7 @@ static const char *mrfld_get_group_name(struct pinctrl_dev *pctldev, { struct mrfld_pinctrl *mp = pinctrl_dev_get_drvdata(pctldev); - return mp->groups[group].name; + return mp->groups[group].grp.name; } static int mrfld_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, @@ -528,8 +528,8 @@ static int mrfld_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, { struct mrfld_pinctrl *mp = pinctrl_dev_get_drvdata(pctldev); - *pins = mp->groups[group].pins; - *npins = mp->groups[group].npins; + *pins = mp->groups[group].grp.pins; + *npins = mp->groups[group].grp.npins; return 0; } @@ -604,15 +604,15 @@ static int mrfld_pinmux_set_mux(struct pinctrl_dev *pctldev, * All pins in the groups needs to be accessible and writable * before we can enable the mux for this group. */ - for (i = 0; i < grp->npins; i++) { - if (!mrfld_buf_available(mp, grp->pins[i])) + for (i = 0; i < grp->grp.npins; i++) { + if (!mrfld_buf_available(mp, grp->grp.pins[i])) return -EBUSY; } /* Now enable the mux setting for each pin in the group */ raw_spin_lock_irqsave(&mp->lock, flags); - for (i = 0; i < grp->npins; i++) - mrfld_update_bufcfg(mp, grp->pins[i], bits, mask); + for (i = 0; i < grp->grp.npins; i++) + mrfld_update_bufcfg(mp, grp->grp.pins[i], bits, mask); raw_spin_unlock_irqrestore(&mp->lock, flags); return 0; diff --git a/drivers/pinctrl/intel/pinctrl-meteorlake.c b/drivers/pinctrl/intel/pinctrl-meteorlake.c new file mode 100644 index 000000000000..9576dcd1cb29 --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-meteorlake.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Meteor Lake PCH pinctrl/GPIO driver + * + * Copyright (C) 2022, Intel Corporation + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-intel.h" + +#define MTL_PAD_OWN 0x0b0 +#define MTL_PADCFGLOCK 0x110 +#define MTL_HOSTSW_OWN 0x140 +#define MTL_GPI_IS 0x200 +#define MTL_GPI_IE 0x210 + +#define MTL_GPP(r, s, e, g) \ + { \ + .reg_num = (r), \ + .base = (s), \ + .size = ((e) - (s) + 1), \ + .gpio_base = (g), \ + } + +#define MTL_COMMUNITY(b, s, e, g) \ + { \ + .barno = (b), \ + .padown_offset = MTL_PAD_OWN, \ + .padcfglock_offset = MTL_PADCFGLOCK, \ + .hostown_offset = MTL_HOSTSW_OWN, \ + .is_offset = MTL_GPI_IS, \ + .ie_offset = MTL_GPI_IE, \ + .pin_base = (s), \ + .npins = ((e) - (s) + 1), \ + .gpps = (g), \ + .ngpps = ARRAY_SIZE(g), \ + } + +/* Meteor Lake-P */ +static const struct pinctrl_pin_desc mtlp_pins[] = { + /* CPU */ + PINCTRL_PIN(0, "PECI"), + PINCTRL_PIN(1, "UFS_RESET_B"), + PINCTRL_PIN(2, "VIDSOUT"), + PINCTRL_PIN(3, "VIDSCK"), + PINCTRL_PIN(4, "VIDALERT_B"), + /* GPP_V */ + PINCTRL_PIN(5, "BATLOW_B"), + PINCTRL_PIN(6, "AC_PRESENT"), + PINCTRL_PIN(7, "SOC_WAKE_B"), + PINCTRL_PIN(8, "PWRBTN_B"), + PINCTRL_PIN(9, "SLP_S3_B"), + PINCTRL_PIN(10, "SLP_S4_B"), + PINCTRL_PIN(11, "SLP_A_B"), + PINCTRL_PIN(12, "GPP_V_7"), + PINCTRL_PIN(13, "SUSCLK"), + PINCTRL_PIN(14, "SLP_WLAN_B"), + PINCTRL_PIN(15, "SLP_S5_B"), + PINCTRL_PIN(16, "LANPHYPC"), + PINCTRL_PIN(17, "SLP_LAN_B"), + PINCTRL_PIN(18, "GPP_V_13"), + PINCTRL_PIN(19, "WAKE_B"), + PINCTRL_PIN(20, "GPP_V_15"), + PINCTRL_PIN(21, "GPP_V_16"), + PINCTRL_PIN(22, "GPP_V_17"), + PINCTRL_PIN(23, "GPP_V_18"), + PINCTRL_PIN(24, "CATERR_B"), + PINCTRL_PIN(25, "PROCHOT_B"), + PINCTRL_PIN(26, "THERMTRIP_B"), + PINCTRL_PIN(27, "DSI_DE_TE_2_GENLOCK_REF"), + PINCTRL_PIN(28, "DSI_DE_TE_1_DISP_UTILS"), + /* GPP_C */ + PINCTRL_PIN(29, "SMBCLK"), + PINCTRL_PIN(30, "SMBDATA"), + PINCTRL_PIN(31, "SMBALERT_B"), + PINCTRL_PIN(32, "SML0CLK"), + PINCTRL_PIN(33, "SML0DATA"), + PINCTRL_PIN(34, "GPP_C_5"), + PINCTRL_PIN(35, "GPP_C_6"), + PINCTRL_PIN(36, "GPP_C_7"), + PINCTRL_PIN(37, "GPP_C_8"), + PINCTRL_PIN(38, "GPP_C_9"), + PINCTRL_PIN(39, "GPP_C_10"), + PINCTRL_PIN(40, "GPP_C_11"), + PINCTRL_PIN(41, "GPP_C_12"), + PINCTRL_PIN(42, "GPP_C_13"), + PINCTRL_PIN(43, "GPP_C_14"), + PINCTRL_PIN(44, "GPP_C_15"), + PINCTRL_PIN(45, "GPP_C_16"), + PINCTRL_PIN(46, "GPP_C_17"), + PINCTRL_PIN(47, "GPP_C_18"), + PINCTRL_PIN(48, "GPP_C_19"), + PINCTRL_PIN(49, "GPP_C_20"), + PINCTRL_PIN(50, "GPP_C_21"), + PINCTRL_PIN(51, "GPP_C_22"), + PINCTRL_PIN(52, "GPP_C_23"), + /* GPP_A */ + PINCTRL_PIN(53, "ESPI_IO_0"), + PINCTRL_PIN(54, "ESPI_IO_1"), + PINCTRL_PIN(55, "ESPI_IO_2"), + PINCTRL_PIN(56, "ESPI_IO_3"), + PINCTRL_PIN(57, "ESPI_CS0_B"), + PINCTRL_PIN(58, "ESPI_CLK"), + PINCTRL_PIN(59, "ESPI_RESET_B"), + PINCTRL_PIN(60, "GPP_A_7"), + PINCTRL_PIN(61, "GPP_A_8"), + PINCTRL_PIN(62, "GPP_A_9"), + PINCTRL_PIN(63, "GPP_A_10"), + PINCTRL_PIN(64, "GPP_A_11"), + PINCTRL_PIN(65, "GPP_A_12"), + PINCTRL_PIN(66, "ESPI_CS1_B"), + PINCTRL_PIN(67, "ESPI_CS2_B"), + PINCTRL_PIN(68, "ESPI_CS3_B"), + PINCTRL_PIN(69, "ESPI_ALERT0_B"), + PINCTRL_PIN(70, "ESPI_ALERT1_B"), + PINCTRL_PIN(71, "ESPI_ALERT2_B"), + PINCTRL_PIN(72, "ESPI_ALERT3_B"), + PINCTRL_PIN(73, "GPP_A_20"), + PINCTRL_PIN(74, "GPP_A_21"), + PINCTRL_PIN(75, "GPP_A_22"), + PINCTRL_PIN(76, "GPP_A_23"), + PINCTRL_PIN(77, "ESPI_CLK_LOOPBK"), + /* GPP_E */ + PINCTRL_PIN(78, "GPP_E_0"), + PINCTRL_PIN(79, "GPP_E_1"), + PINCTRL_PIN(80, "GPP_E_2"), + PINCTRL_PIN(81, "GPP_E_3"), + PINCTRL_PIN(82, "GPP_E_4"), + PINCTRL_PIN(83, "GPP_E_5"), + PINCTRL_PIN(84, "GPP_E_6"), + PINCTRL_PIN(85, "GPP_E_7"), + PINCTRL_PIN(86, "GPP_E_8"), + PINCTRL_PIN(87, "GPP_E_9"), + PINCTRL_PIN(88, "GPP_E_10"), + PINCTRL_PIN(89, "GPP_E_11"), + PINCTRL_PIN(90, "GPP_E_12"), + PINCTRL_PIN(91, "GPP_E_13"), + PINCTRL_PIN(92, "GPP_E_14"), + PINCTRL_PIN(93, "SLP_DRAM_B"), + PINCTRL_PIN(94, "GPP_E_16"), + PINCTRL_PIN(95, "GPP_E_17"), + PINCTRL_PIN(96, "GPP_E_18"), + PINCTRL_PIN(97, "GPP_E_19"), + PINCTRL_PIN(98, "GPP_E_20"), + PINCTRL_PIN(99, "GPP_E_21"), + PINCTRL_PIN(100, "DNX_FORCE_RELOAD"), + PINCTRL_PIN(101, "GPP_E_23"), + PINCTRL_PIN(102, "THC0_GSPI0_CLK_LOOPBK"), + /* GPP_H */ + PINCTRL_PIN(103, "GPP_H_0"), + PINCTRL_PIN(104, "GPP_H_1"), + PINCTRL_PIN(105, "GPP_H_2"), + PINCTRL_PIN(106, "GPP_H_3"), + PINCTRL_PIN(107, "GPP_H_4"), + PINCTRL_PIN(108, "GPP_H_5"), + PINCTRL_PIN(109, "GPP_H_6"), + PINCTRL_PIN(110, "GPP_H_7"), + PINCTRL_PIN(111, "GPP_H_8"), + PINCTRL_PIN(112, "GPP_H_9"), + PINCTRL_PIN(113, "GPP_H_10"), + PINCTRL_PIN(114, "GPP_H_11"), + PINCTRL_PIN(115, "GPP_H_12"), + PINCTRL_PIN(116, "CPU_C10_GATE_B"), + PINCTRL_PIN(117, "GPP_H_14"), + PINCTRL_PIN(118, "GPP_H_15"), + PINCTRL_PIN(119, "GPP_H_16"), + PINCTRL_PIN(120, "GPP_H_17"), + PINCTRL_PIN(121, "GPP_H_18"), + PINCTRL_PIN(122, "GPP_H_19"), + PINCTRL_PIN(123, "GPP_H_20"), + PINCTRL_PIN(124, "GPP_H_21"), + PINCTRL_PIN(125, "GPP_H_22"), + PINCTRL_PIN(126, "GPP_H_23"), + PINCTRL_PIN(127, "LPI3C1_CLK_LOOPBK"), + PINCTRL_PIN(128, "I3C0_CLK_LOOPBK"), + /* GPP_F */ + PINCTRL_PIN(129, "CNV_BRI_DT"), + PINCTRL_PIN(130, "CNV_BRI_RSP"), + PINCTRL_PIN(131, "CNV_RGI_DT"), + PINCTRL_PIN(132, "CNV_RGI_RSP"), + PINCTRL_PIN(133, "CNV_RF_RESET_B"), + PINCTRL_PIN(134, "CRF_CLKREQ"), + PINCTRL_PIN(135, "GPP_F_6"), + PINCTRL_PIN(136, "FUSA_DIAGTEST_EN"), + PINCTRL_PIN(137, "FUSA_DIAGTEST_MODE"), + PINCTRL_PIN(138, "BOOTMPC"), + PINCTRL_PIN(139, "GPP_F_10"), + PINCTRL_PIN(140, "GPP_F_11"), + PINCTRL_PIN(141, "GSXDOUT"), + PINCTRL_PIN(142, "GSXSLOAD"), + PINCTRL_PIN(143, "GSXDIN"), + PINCTRL_PIN(144, "GSXSRESETB"), + PINCTRL_PIN(145, "GSXCLK"), + PINCTRL_PIN(146, "GMII_MDC_0"), + PINCTRL_PIN(147, "GMII_MDIO_0"), + PINCTRL_PIN(148, "GPP_F_19"), + PINCTRL_PIN(149, "GPP_F_20"), + PINCTRL_PIN(150, "GPP_F_21"), + PINCTRL_PIN(151, "GPP_F_22"), + PINCTRL_PIN(152, "GPP_F_23"), + PINCTRL_PIN(153, "THC1_GSPI1_CLK_LOOPBK"), + PINCTRL_PIN(154, "GSPI0A_CLK_LOOPBK"), + /* SPI0 */ + PINCTRL_PIN(155, "SPI0_IO_2"), + PINCTRL_PIN(156, "SPI0_IO_3"), + PINCTRL_PIN(157, "SPI0_MOSI_IO_0"), + PINCTRL_PIN(158, "SPI0_MISO_IO_1"), + PINCTRL_PIN(159, "SPI0_TPM_CS_B"), + PINCTRL_PIN(160, "SPI0_FLASH_0_CS_B"), + PINCTRL_PIN(161, "SPI0_FLASH_1_CS_B"), + PINCTRL_PIN(162, "SPI0_CLK"), + PINCTRL_PIN(163, "L_BKLTEN"), + PINCTRL_PIN(164, "L_BKLTCTL"), + PINCTRL_PIN(165, "L_VDDEN"), + PINCTRL_PIN(166, "SYS_PWROK"), + PINCTRL_PIN(167, "SYS_RESET_B"), + PINCTRL_PIN(168, "MLK_RST_B"), + PINCTRL_PIN(169, "SPI0_CLK_LOOPBK"), + /* vGPIO_3 */ + PINCTRL_PIN(170, "ESPI_USB_OCB_0"), + PINCTRL_PIN(171, "ESPI_USB_OCB_1"), + PINCTRL_PIN(172, "ESPI_USB_OCB_2"), + PINCTRL_PIN(173, "ESPI_USB_OCB_3"), + PINCTRL_PIN(174, "USB_CPU_OCB_0"), + PINCTRL_PIN(175, "USB_CPU_OCB_1"), + PINCTRL_PIN(176, "USB_CPU_OCB_2"), + PINCTRL_PIN(177, "USB_CPU_OCB_3"), + PINCTRL_PIN(178, "TS0_IN_INT"), + PINCTRL_PIN(179, "TS1_IN_INT"), + PINCTRL_PIN(180, "THC0_WOT_INT"), + PINCTRL_PIN(181, "THC1_WOT_INT"), + PINCTRL_PIN(182, "THC0_WHC_INT"), + PINCTRL_PIN(183, "THC1_WHC_INT"), + /* GPP_S */ + PINCTRL_PIN(184, "GPP_S_0"), + PINCTRL_PIN(185, "GPP_S_1"), + PINCTRL_PIN(186, "GPP_S_2"), + PINCTRL_PIN(187, "GPP_S_3"), + PINCTRL_PIN(188, "GPP_S_4"), + PINCTRL_PIN(189, "GPP_S_5"), + PINCTRL_PIN(190, "GPP_S_6"), + PINCTRL_PIN(191, "GPP_S_7"), + /* JTAG */ + PINCTRL_PIN(192, "JTAG_MBPB0"), + PINCTRL_PIN(193, "JTAG_MBPB1"), + PINCTRL_PIN(194, "JTAG_MBPB2"), + PINCTRL_PIN(195, "JTAG_MBPB3"), + PINCTRL_PIN(196, "JTAG_TDO"), + PINCTRL_PIN(197, "PRDY_B"), + PINCTRL_PIN(198, "PREQ_B"), + PINCTRL_PIN(199, "JTAG_TDI"), + PINCTRL_PIN(200, "JTAG_TMS"), + PINCTRL_PIN(201, "JTAG_TCK"), + PINCTRL_PIN(202, "DBG_PMODE"), + PINCTRL_PIN(203, "JTAG_TRST_B"), + /* GPP_B */ + PINCTRL_PIN(204, "ADM_VID_0"), + PINCTRL_PIN(205, "ADM_VID_1"), + PINCTRL_PIN(206, "GPP_B_2"), + PINCTRL_PIN(207, "GPP_B_3"), + PINCTRL_PIN(208, "GPP_B_4"), + PINCTRL_PIN(209, "GPP_B_5"), + PINCTRL_PIN(210, "GPP_B_6"), + PINCTRL_PIN(211, "GPP_B_7"), + PINCTRL_PIN(212, "GPP_B_8"), + PINCTRL_PIN(213, "GPP_B_9"), + PINCTRL_PIN(214, "GPP_B_10"), + PINCTRL_PIN(215, "GPP_B_11"), + PINCTRL_PIN(216, "SLP_S0_B"), + PINCTRL_PIN(217, "PLTRST_B"), + PINCTRL_PIN(218, "GPP_B_14"), + PINCTRL_PIN(219, "GPP_B_15"), + PINCTRL_PIN(220, "GPP_B_16"), + PINCTRL_PIN(221, "GPP_B_17"), + PINCTRL_PIN(222, "GPP_B_18"), + PINCTRL_PIN(223, "GPP_B_19"), + PINCTRL_PIN(224, "GPP_B_20"), + PINCTRL_PIN(225, "GPP_B_21"), + PINCTRL_PIN(226, "GPP_B_22"), + PINCTRL_PIN(227, "GPP_B_23"), + PINCTRL_PIN(228, "ISH_I3C0_CLK_LOOPBK"), + /* GPP_D */ + PINCTRL_PIN(229, "GPP_D_0"), + PINCTRL_PIN(230, "GPP_D_1"), + PINCTRL_PIN(231, "GPP_D_2"), + PINCTRL_PIN(232, "GPP_D_3"), + PINCTRL_PIN(233, "GPP_D_4"), + PINCTRL_PIN(234, "GPP_D_5"), + PINCTRL_PIN(235, "GPP_D_6"), + PINCTRL_PIN(236, "GPP_D_7"), + PINCTRL_PIN(237, "GPP_D_8"), + PINCTRL_PIN(238, "GPP_D_9"), + PINCTRL_PIN(239, "HDA_BCLK"), + PINCTRL_PIN(240, "HDA_SYNC"), + PINCTRL_PIN(241, "HDA_SDO"), + PINCTRL_PIN(242, "HDA_SDI_0"), + PINCTRL_PIN(243, "GPP_D_14"), + PINCTRL_PIN(244, "GPP_D_15"), + PINCTRL_PIN(245, "GPP_D_16"), + PINCTRL_PIN(246, "HDA_RST_B"), + PINCTRL_PIN(247, "GPP_D_18"), + PINCTRL_PIN(248, "GPP_D_19"), + PINCTRL_PIN(249, "GPP_D_20"), + PINCTRL_PIN(250, "UFS_REFCLK"), + PINCTRL_PIN(251, "BPKI3C_SDA"), + PINCTRL_PIN(252, "BPKI3C_SCL"), + PINCTRL_PIN(253, "BOOTHALT_B"), + /* vGPIO */ + PINCTRL_PIN(254, "CNV_BTEN"), + PINCTRL_PIN(255, "CNV_BT_HOST_WAKEB"), + PINCTRL_PIN(256, "CNV_BT_IF_SELECT"), + PINCTRL_PIN(257, "vCNV_BT_UART_TXD"), + PINCTRL_PIN(258, "vCNV_BT_UART_RXD"), + PINCTRL_PIN(259, "vCNV_BT_UART_CTS_B"), + PINCTRL_PIN(260, "vCNV_BT_UART_RTS_B"), + PINCTRL_PIN(261, "vCNV_MFUART1_TXD"), + PINCTRL_PIN(262, "vCNV_MFUART1_RXD"), + PINCTRL_PIN(263, "vCNV_MFUART1_CTS_B"), + PINCTRL_PIN(264, "vCNV_MFUART1_RTS_B"), + PINCTRL_PIN(265, "vUART0_TXD"), + PINCTRL_PIN(266, "vUART0_RXD"), + PINCTRL_PIN(267, "vUART0_CTS_B"), + PINCTRL_PIN(268, "vUART0_RTS_B"), + PINCTRL_PIN(269, "vISH_UART0_TXD"), + PINCTRL_PIN(270, "vISH_UART0_RXD"), + PINCTRL_PIN(271, "vISH_UART0_CTS_B"), + PINCTRL_PIN(272, "vISH_UART0_RTS_B"), + PINCTRL_PIN(273, "vCNV_BT_I2S_BCLK"), + PINCTRL_PIN(274, "vCNV_BT_I2S_WS_SYNC"), + PINCTRL_PIN(275, "vCNV_BT_I2S_SDO"), + PINCTRL_PIN(276, "vCNV_BT_I2S_SDI"), + PINCTRL_PIN(277, "vI2S2_SCLK"), + PINCTRL_PIN(278, "vI2S2_SFRM"), + PINCTRL_PIN(279, "vI2S2_TXD"), + PINCTRL_PIN(280, "vI2S2_RXD"), + PINCTRL_PIN(281, "vCNV_BT_I2S_BCLK_2"), + PINCTRL_PIN(282, "vCNV_BT_I2S_WS_SYNC_2"), + PINCTRL_PIN(283, "vCNV_BT_I2S_SDO_2"), + PINCTRL_PIN(284, "vCNV_BT_I2S_SDI_2"), + PINCTRL_PIN(285, "vI2S2_SCLK_2"), + PINCTRL_PIN(286, "vI2S2_SFRM_2"), + PINCTRL_PIN(287, "vI2S2_TXD_2"), + PINCTRL_PIN(288, "vI2S2_RXD_2"), +}; + +static const struct intel_padgroup mtlp_community0_gpps[] = { + MTL_GPP(0, 0, 4, 0), /* CPU */ + MTL_GPP(1, 5, 28, 32), /* GPP_V */ + MTL_GPP(2, 29, 52, 64), /* GPP_C */ +}; + +static const struct intel_padgroup mtlp_community1_gpps[] = { + MTL_GPP(0, 53, 77, 96), /* GPP_A */ + MTL_GPP(1, 78, 102, 128), /* GPP_E */ +}; + +static const struct intel_padgroup mtlp_community3_gpps[] = { + MTL_GPP(0, 103, 128, 160), /* GPP_H */ + MTL_GPP(1, 129, 154, 192), /* GPP_F */ + MTL_GPP(2, 155, 169, 224), /* SPI0 */ + MTL_GPP(3, 170, 183, 256), /* vGPIO_3 */ +}; + +static const struct intel_padgroup mtlp_community4_gpps[] = { + MTL_GPP(0, 184, 191, 288), /* GPP_S */ + MTL_GPP(1, 192, 203, 320), /* JTAG */ +}; + +static const struct intel_padgroup mtlp_community5_gpps[] = { + MTL_GPP(0, 204, 228, 352), /* GPP_B */ + MTL_GPP(1, 229, 253, 384), /* GPP_D */ + MTL_GPP(2, 254, 285, 416), /* vGPIO_0 */ + MTL_GPP(3, 286, 288, 448), /* vGPIO_1 */ +}; + +static const struct intel_community mtlp_communities[] = { + MTL_COMMUNITY(0, 0, 52, mtlp_community0_gpps), + MTL_COMMUNITY(1, 53, 102, mtlp_community1_gpps), + MTL_COMMUNITY(2, 103, 183, mtlp_community3_gpps), + MTL_COMMUNITY(3, 184, 203, mtlp_community4_gpps), + MTL_COMMUNITY(4, 204, 288, mtlp_community5_gpps), +}; + +static const struct intel_pinctrl_soc_data mtlp_soc_data = { + .pins = mtlp_pins, + .npins = ARRAY_SIZE(mtlp_pins), + .communities = mtlp_communities, + .ncommunities = ARRAY_SIZE(mtlp_communities), +}; + +static const struct acpi_device_id mtl_pinctrl_acpi_match[] = { + { "INTC1083", (kernel_ulong_t)&mtlp_soc_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, mtl_pinctrl_acpi_match); + +static INTEL_PINCTRL_PM_OPS(mtl_pinctrl_pm_ops); + +static struct platform_driver mtl_pinctrl_driver = { + .probe = intel_pinctrl_probe_by_hid, + .driver = { + .name = "meteorlake-pinctrl", + .acpi_match_table = mtl_pinctrl_acpi_match, + .pm = &mtl_pinctrl_pm_ops, + }, +}; +module_platform_driver(mtl_pinctrl_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Meteor Lake PCH pinctrl/GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8192.c b/drivers/pinctrl/mediatek/pinctrl-mt8192.c index acccde9262ba..78c02b7c81f0 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8192.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8192.c @@ -1107,24 +1107,10 @@ static const struct mtk_pin_field_calc mt8192_pin_pupd_range[] = { PIN_FIELD_BASE(54, 54, 1, 0x0060, 0x10, 2, 1), PIN_FIELD_BASE(55, 55, 1, 0x0060, 0x10, 4, 1), PIN_FIELD_BASE(56, 56, 1, 0x0060, 0x10, 3, 1), - PIN_FIELD_BASE(118, 118, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(119, 119, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(120, 120, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(121, 121, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(122, 122, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(123, 123, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(124, 124, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(125, 125, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(139, 139, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(140, 140, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(141, 141, 4, 0x00e0, 0x10, 31, 1), - PIN_FIELD_BASE(142, 142, 4, 0x00e0, 0x10, 31, 1), PIN_FIELD_BASE(152, 152, 7, 0x0090, 0x10, 3, 1), PIN_FIELD_BASE(153, 153, 7, 0x0090, 0x10, 2, 1), PIN_FIELD_BASE(154, 154, 7, 0x0090, 0x10, 0, 1), PIN_FIELD_BASE(155, 155, 7, 0x0090, 0x10, 1, 1), - PIN_FIELD_BASE(160, 160, 7, 0x00f0, 0x10, 31, 1), - PIN_FIELD_BASE(161, 161, 7, 0x00f0, 0x10, 31, 1), PIN_FIELD_BASE(183, 183, 9, 0x0030, 0x10, 1, 1), PIN_FIELD_BASE(184, 184, 9, 0x0030, 0x10, 2, 1), PIN_FIELD_BASE(185, 185, 9, 0x0030, 0x10, 4, 1), @@ -1137,12 +1123,6 @@ static const struct mtk_pin_field_calc mt8192_pin_pupd_range[] = { PIN_FIELD_BASE(192, 192, 9, 0x0030, 0x10, 0, 1), PIN_FIELD_BASE(193, 193, 9, 0x0030, 0x10, 5, 1), PIN_FIELD_BASE(194, 194, 9, 0x0030, 0x10, 11, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0070, 0x10, 31, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0070, 0x10, 31, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0070, 0x10, 31, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0070, 0x10, 31, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0070, 0x10, 31, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0070, 0x10, 31, 1), }; static const struct mtk_pin_field_calc mt8192_pin_r0_range[] = { @@ -1164,24 +1144,10 @@ static const struct mtk_pin_field_calc mt8192_pin_r0_range[] = { PIN_FIELD_BASE(54, 54, 1, 0x0080, 0x10, 2, 1), PIN_FIELD_BASE(55, 55, 1, 0x0080, 0x10, 4, 1), PIN_FIELD_BASE(56, 56, 1, 0x0080, 0x10, 3, 1), - PIN_FIELD_BASE(118, 118, 4, 0x00e0, 0x10, 0, 1), - PIN_FIELD_BASE(119, 119, 4, 0x00e0, 0x10, 12, 1), - PIN_FIELD_BASE(120, 120, 4, 0x00e0, 0x10, 10, 1), - PIN_FIELD_BASE(121, 121, 4, 0x00e0, 0x10, 22, 1), - PIN_FIELD_BASE(122, 122, 4, 0x00e0, 0x10, 8, 1), - PIN_FIELD_BASE(123, 123, 4, 0x00e0, 0x10, 20, 1), - PIN_FIELD_BASE(124, 124, 4, 0x00e0, 0x10, 6, 1), - PIN_FIELD_BASE(125, 125, 4, 0x00e0, 0x10, 18, 1), - PIN_FIELD_BASE(139, 139, 4, 0x00e0, 0x10, 4, 1), - PIN_FIELD_BASE(140, 140, 4, 0x00e0, 0x10, 16, 1), - PIN_FIELD_BASE(141, 141, 4, 0x00e0, 0x10, 2, 1), - PIN_FIELD_BASE(142, 142, 4, 0x00e0, 0x10, 14, 1), PIN_FIELD_BASE(152, 152, 7, 0x00c0, 0x10, 3, 1), PIN_FIELD_BASE(153, 153, 7, 0x00c0, 0x10, 2, 1), PIN_FIELD_BASE(154, 154, 7, 0x00c0, 0x10, 0, 1), PIN_FIELD_BASE(155, 155, 7, 0x00c0, 0x10, 1, 1), - PIN_FIELD_BASE(160, 160, 7, 0x00f0, 0x10, 0, 1), - PIN_FIELD_BASE(161, 161, 7, 0x00f0, 0x10, 2, 1), PIN_FIELD_BASE(183, 183, 9, 0x0040, 0x10, 1, 1), PIN_FIELD_BASE(184, 184, 9, 0x0040, 0x10, 2, 1), PIN_FIELD_BASE(185, 185, 9, 0x0040, 0x10, 4, 1), @@ -1194,12 +1160,6 @@ static const struct mtk_pin_field_calc mt8192_pin_r0_range[] = { PIN_FIELD_BASE(192, 192, 9, 0x0040, 0x10, 0, 1), PIN_FIELD_BASE(193, 193, 9, 0x0040, 0x10, 5, 1), PIN_FIELD_BASE(194, 194, 9, 0x0040, 0x10, 11, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0070, 0x10, 2, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0070, 0x10, 6, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0070, 0x10, 0, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0070, 0x10, 2, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0070, 0x10, 0, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0070, 0x10, 4, 1), }; static const struct mtk_pin_field_calc mt8192_pin_r1_range[] = { @@ -1221,24 +1181,10 @@ static const struct mtk_pin_field_calc mt8192_pin_r1_range[] = { PIN_FIELD_BASE(54, 54, 1, 0x0090, 0x10, 2, 1), PIN_FIELD_BASE(55, 55, 1, 0x0090, 0x10, 4, 1), PIN_FIELD_BASE(56, 56, 1, 0x0090, 0x10, 3, 1), - PIN_FIELD_BASE(118, 118, 4, 0x00e0, 0x10, 1, 1), - PIN_FIELD_BASE(119, 119, 4, 0x00e0, 0x10, 13, 1), - PIN_FIELD_BASE(120, 120, 4, 0x00e0, 0x10, 11, 1), - PIN_FIELD_BASE(121, 121, 4, 0x00e0, 0x10, 23, 1), - PIN_FIELD_BASE(122, 122, 4, 0x00e0, 0x10, 9, 1), - PIN_FIELD_BASE(123, 123, 4, 0x00e0, 0x10, 21, 1), - PIN_FIELD_BASE(124, 124, 4, 0x00e0, 0x10, 7, 1), - PIN_FIELD_BASE(125, 125, 4, 0x00e0, 0x10, 19, 1), - PIN_FIELD_BASE(139, 139, 4, 0x00e0, 0x10, 5, 1), - PIN_FIELD_BASE(140, 140, 4, 0x00e0, 0x10, 17, 1), - PIN_FIELD_BASE(141, 141, 4, 0x00e0, 0x10, 3, 1), - PIN_FIELD_BASE(142, 142, 4, 0x00e0, 0x10, 15, 1), PIN_FIELD_BASE(152, 152, 7, 0x00d0, 0x10, 3, 1), PIN_FIELD_BASE(153, 153, 7, 0x00d0, 0x10, 2, 1), PIN_FIELD_BASE(154, 154, 7, 0x00d0, 0x10, 0, 1), PIN_FIELD_BASE(155, 155, 7, 0x00d0, 0x10, 1, 1), - PIN_FIELD_BASE(160, 160, 7, 0x00f0, 0x10, 1, 1), - PIN_FIELD_BASE(161, 161, 7, 0x00f0, 0x10, 3, 1), PIN_FIELD_BASE(183, 183, 9, 0x0050, 0x10, 1, 1), PIN_FIELD_BASE(184, 184, 9, 0x0050, 0x10, 2, 1), PIN_FIELD_BASE(185, 185, 9, 0x0050, 0x10, 4, 1), @@ -1251,83 +1197,169 @@ static const struct mtk_pin_field_calc mt8192_pin_r1_range[] = { PIN_FIELD_BASE(192, 192, 9, 0x0050, 0x10, 0, 1), PIN_FIELD_BASE(193, 193, 9, 0x0050, 0x10, 5, 1), PIN_FIELD_BASE(194, 194, 9, 0x0050, 0x10, 11, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0070, 0x10, 3, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0070, 0x10, 7, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0070, 0x10, 1, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0070, 0x10, 3, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0070, 0x10, 1, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0070, 0x10, 5, 1), }; -static const struct mtk_pin_field_calc mt8192_pin_e1e0en_range[] = { - PIN_FIELD_BASE(118, 118, 4, 0x0040, 0x10, 0, 1), - PIN_FIELD_BASE(119, 119, 4, 0x0040, 0x10, 18, 1), - PIN_FIELD_BASE(120, 120, 4, 0x0040, 0x10, 15, 1), - PIN_FIELD_BASE(121, 121, 4, 0x0050, 0x10, 3, 1), - PIN_FIELD_BASE(122, 122, 4, 0x0040, 0x10, 12, 1), - PIN_FIELD_BASE(123, 123, 4, 0x0050, 0x10, 0, 1), - PIN_FIELD_BASE(124, 124, 4, 0x0040, 0x10, 9, 1), - PIN_FIELD_BASE(125, 125, 4, 0x0040, 0x10, 27, 1), - PIN_FIELD_BASE(139, 139, 4, 0x0040, 0x10, 6, 1), - PIN_FIELD_BASE(140, 140, 4, 0x0040, 0x10, 24, 1), - PIN_FIELD_BASE(141, 141, 4, 0x0040, 0x10, 3, 1), - PIN_FIELD_BASE(142, 142, 4, 0x0040, 0x10, 21, 1), - PIN_FIELD_BASE(160, 160, 7, 0x0030, 0x10, 0, 1), - PIN_FIELD_BASE(161, 161, 7, 0x0030, 0x10, 3, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0010, 0x10, 3, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0010, 0x10, 9, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0020, 0x10, 0, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0020, 0x10, 3, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0010, 0x10, 0, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0010, 0x10, 6, 1), -}; +static const struct mtk_pin_field_calc mt8192_pin_drv_adv_range[] = { + PIN_FIELD_BASE(89, 89, 2, 0x0040, 0x10, 0, 5), + PIN_FIELD_BASE(90, 90, 2, 0x0040, 0x10, 5, 5), -static const struct mtk_pin_field_calc mt8192_pin_e0_range[] = { - PIN_FIELD_BASE(118, 118, 4, 0x0040, 0x10, 1, 1), - PIN_FIELD_BASE(119, 119, 4, 0x0040, 0x10, 19, 1), - PIN_FIELD_BASE(120, 120, 4, 0x0040, 0x10, 16, 1), - PIN_FIELD_BASE(121, 121, 4, 0x0050, 0x10, 4, 1), - PIN_FIELD_BASE(122, 122, 4, 0x0040, 0x10, 13, 1), - PIN_FIELD_BASE(123, 123, 4, 0x0050, 0x10, 1, 1), - PIN_FIELD_BASE(124, 124, 4, 0x0040, 0x10, 10, 1), - PIN_FIELD_BASE(125, 125, 4, 0x0040, 0x10, 28, 1), - PIN_FIELD_BASE(139, 139, 4, 0x0040, 0x10, 7, 1), - PIN_FIELD_BASE(140, 140, 4, 0x0040, 0x10, 25, 1), - PIN_FIELD_BASE(141, 141, 4, 0x0040, 0x10, 4, 1), - PIN_FIELD_BASE(142, 142, 4, 0x0040, 0x10, 22, 1), - PIN_FIELD_BASE(160, 160, 7, 0x0030, 0x10, 1, 1), - PIN_FIELD_BASE(161, 161, 7, 0x0030, 0x10, 4, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0010, 0x10, 4, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0010, 0x10, 10, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0020, 0x10, 1, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0020, 0x10, 4, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0010, 0x10, 1, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0010, 0x10, 7, 1), + PIN_FIELD_BASE(118, 118, 4, 0x0040, 0x10, 0, 3), + PIN_FIELD_BASE(119, 119, 4, 0x0040, 0x10, 18, 3), + PIN_FIELD_BASE(120, 120, 4, 0x0040, 0x10, 15, 3), + PIN_FIELD_BASE(121, 121, 4, 0x0050, 0x10, 3, 3), + PIN_FIELD_BASE(122, 122, 4, 0x0040, 0x10, 12, 3), + PIN_FIELD_BASE(123, 123, 4, 0x0050, 0x10, 0, 3), + PIN_FIELD_BASE(124, 124, 4, 0x0040, 0x10, 9, 3), + PIN_FIELD_BASE(125, 125, 4, 0x0040, 0x10, 27, 3), + PIN_FIELD_BASE(139, 139, 4, 0x0040, 0x10, 6, 3), + PIN_FIELD_BASE(140, 140, 4, 0x0040, 0x10, 24, 3), + PIN_FIELD_BASE(141, 141, 4, 0x0040, 0x10, 3, 3), + PIN_FIELD_BASE(142, 142, 4, 0x0040, 0x10, 21, 3), + PIN_FIELD_BASE(160, 160, 7, 0x0030, 0x10, 0, 3), + PIN_FIELD_BASE(161, 161, 7, 0x0030, 0x10, 3, 3), + PIN_FIELD_BASE(200, 200, 8, 0x0010, 0x10, 3, 3), + PIN_FIELD_BASE(201, 201, 8, 0x0010, 0x10, 9, 3), + PIN_FIELD_BASE(202, 202, 5, 0x0020, 0x10, 0, 3), + PIN_FIELD_BASE(203, 203, 5, 0x0020, 0x10, 3, 3), + PIN_FIELD_BASE(204, 204, 8, 0x0010, 0x10, 0, 3), + PIN_FIELD_BASE(205, 205, 8, 0x0010, 0x10, 6, 3), }; -static const struct mtk_pin_field_calc mt8192_pin_e1_range[] = { - PIN_FIELD_BASE(118, 118, 4, 0x0040, 0x10, 2, 1), - PIN_FIELD_BASE(119, 119, 4, 0x0040, 0x10, 20, 1), - PIN_FIELD_BASE(120, 120, 4, 0x0040, 0x10, 17, 1), - PIN_FIELD_BASE(121, 121, 4, 0x0050, 0x10, 5, 1), - PIN_FIELD_BASE(122, 122, 4, 0x0040, 0x10, 14, 1), - PIN_FIELD_BASE(123, 123, 4, 0x0050, 0x10, 2, 1), - PIN_FIELD_BASE(124, 124, 4, 0x0040, 0x10, 11, 1), - PIN_FIELD_BASE(125, 125, 4, 0x0040, 0x10, 29, 1), - PIN_FIELD_BASE(139, 139, 4, 0x0040, 0x10, 8, 1), - PIN_FIELD_BASE(140, 140, 4, 0x0040, 0x10, 26, 1), - PIN_FIELD_BASE(141, 141, 4, 0x0040, 0x10, 5, 1), - PIN_FIELD_BASE(142, 142, 4, 0x0040, 0x10, 23, 1), - PIN_FIELD_BASE(160, 160, 7, 0x0030, 0x10, 2, 1), - PIN_FIELD_BASE(161, 161, 7, 0x0030, 0x10, 5, 1), - PIN_FIELD_BASE(200, 200, 8, 0x0010, 0x10, 5, 1), - PIN_FIELD_BASE(201, 201, 8, 0x0010, 0x10, 11, 1), - PIN_FIELD_BASE(202, 202, 5, 0x0020, 0x10, 2, 1), - PIN_FIELD_BASE(203, 203, 5, 0x0020, 0x10, 5, 1), - PIN_FIELD_BASE(204, 204, 8, 0x0010, 0x10, 2, 1), - PIN_FIELD_BASE(205, 205, 8, 0x0010, 0x10, 8, 1), +static const struct mtk_pin_field_calc mt8192_pin_rsel_range[] = { + PIN_FIELD_BASE(118, 118, 4, 0x00e0, 0x10, 0, 2), + PIN_FIELD_BASE(119, 119, 4, 0x00e0, 0x10, 12, 2), + PIN_FIELD_BASE(120, 120, 4, 0x00e0, 0x10, 10, 2), + PIN_FIELD_BASE(121, 121, 4, 0x00e0, 0x10, 22, 2), + PIN_FIELD_BASE(122, 122, 4, 0x00e0, 0x10, 8, 2), + PIN_FIELD_BASE(123, 123, 4, 0x00e0, 0x10, 20, 2), + PIN_FIELD_BASE(124, 124, 4, 0x00e0, 0x10, 6, 2), + PIN_FIELD_BASE(125, 125, 4, 0x00e0, 0x10, 18, 2), + PIN_FIELD_BASE(139, 139, 4, 0x00e0, 0x10, 4, 2), + PIN_FIELD_BASE(140, 140, 4, 0x00e0, 0x10, 16, 2), + PIN_FIELD_BASE(141, 141, 4, 0x00e0, 0x10, 2, 2), + PIN_FIELD_BASE(142, 142, 4, 0x00e0, 0x10, 14, 2), + PIN_FIELD_BASE(160, 160, 7, 0x00f0, 0x10, 0, 2), + PIN_FIELD_BASE(161, 161, 7, 0x00f0, 0x10, 2, 2), + PIN_FIELD_BASE(200, 200, 8, 0x0070, 0x10, 2, 2), + PIN_FIELD_BASE(201, 201, 8, 0x0070, 0x10, 6, 2), + PIN_FIELD_BASE(202, 202, 5, 0x0070, 0x10, 0, 2), + PIN_FIELD_BASE(203, 203, 5, 0x0070, 0x10, 2, 2), + PIN_FIELD_BASE(204, 204, 8, 0x0070, 0x10, 0, 2), + PIN_FIELD_BASE(205, 205, 8, 0x0070, 0x10, 4, 2), }; +static const unsigned int mt8192_pull_type[] = { + MTK_PULL_PU_PD_TYPE,/*0*/ MTK_PULL_PU_PD_TYPE,/*1*/ + MTK_PULL_PU_PD_TYPE,/*2*/ MTK_PULL_PU_PD_TYPE,/*3*/ + MTK_PULL_PU_PD_TYPE,/*4*/ MTK_PULL_PU_PD_TYPE,/*5*/ + MTK_PULL_PU_PD_TYPE,/*6*/ MTK_PULL_PU_PD_TYPE,/*7*/ + MTK_PULL_PU_PD_TYPE,/*8*/ MTK_PULL_PU_PD_TYPE,/*9*/ + MTK_PULL_PUPD_R1R0_TYPE,/*10*/ MTK_PULL_PUPD_R1R0_TYPE,/*11*/ + MTK_PULL_PUPD_R1R0_TYPE,/*12*/ MTK_PULL_PUPD_R1R0_TYPE,/*13*/ + MTK_PULL_PUPD_R1R0_TYPE,/*14*/ MTK_PULL_PUPD_R1R0_TYPE,/*15*/ + MTK_PULL_PU_PD_TYPE,/*16*/ MTK_PULL_PU_PD_TYPE,/*17*/ + MTK_PULL_PU_PD_TYPE,/*18*/ MTK_PULL_PU_PD_TYPE,/*19*/ + MTK_PULL_PU_PD_TYPE,/*20*/ MTK_PULL_PU_PD_TYPE,/*21*/ + MTK_PULL_PU_PD_TYPE,/*22*/ MTK_PULL_PU_PD_TYPE,/*23*/ + MTK_PULL_PU_PD_TYPE,/*24*/ MTK_PULL_PU_PD_TYPE,/*25*/ + MTK_PULL_PU_PD_TYPE,/*26*/ MTK_PULL_PU_PD_TYPE,/*27*/ + MTK_PULL_PU_PD_TYPE,/*28*/ MTK_PULL_PU_PD_TYPE,/*29*/ + MTK_PULL_PU_PD_TYPE,/*30*/ MTK_PULL_PU_PD_TYPE,/*31*/ + MTK_PULL_PU_PD_TYPE,/*32*/ MTK_PULL_PU_PD_TYPE,/*33*/ + MTK_PULL_PU_PD_TYPE,/*34*/ MTK_PULL_PU_PD_TYPE,/*35*/ + MTK_PULL_PU_PD_TYPE,/*36*/ MTK_PULL_PU_PD_TYPE,/*37*/ + MTK_PULL_PU_PD_TYPE,/*38*/ MTK_PULL_PU_PD_TYPE,/*39*/ + MTK_PULL_PU_PD_TYPE,/*40*/ MTK_PULL_PU_PD_TYPE,/*41*/ + MTK_PULL_PU_PD_TYPE,/*42*/ MTK_PULL_PU_PD_TYPE,/*43*/ + MTK_PULL_PU_PD_TYPE,/*44*/ MTK_PULL_PUPD_R1R0_TYPE,/*45*/ + MTK_PULL_PUPD_R1R0_TYPE,/*46*/ MTK_PULL_PUPD_R1R0_TYPE,/*47*/ + MTK_PULL_PUPD_R1R0_TYPE,/*48*/ MTK_PULL_PUPD_R1R0_TYPE,/*49*/ + MTK_PULL_PUPD_R1R0_TYPE,/*50*/ MTK_PULL_PUPD_R1R0_TYPE,/*51*/ + MTK_PULL_PUPD_R1R0_TYPE,/*52*/ MTK_PULL_PUPD_R1R0_TYPE,/*53*/ + MTK_PULL_PUPD_R1R0_TYPE,/*54*/ MTK_PULL_PUPD_R1R0_TYPE,/*55*/ + MTK_PULL_PUPD_R1R0_TYPE,/*56*/ MTK_PULL_PU_PD_TYPE,/*57*/ + MTK_PULL_PU_PD_TYPE,/*58*/ MTK_PULL_PU_PD_TYPE,/*59*/ + MTK_PULL_PU_PD_TYPE,/*60*/ MTK_PULL_PU_PD_TYPE,/*61*/ + MTK_PULL_PU_PD_TYPE,/*62*/ MTK_PULL_PU_PD_TYPE,/*63*/ + MTK_PULL_PU_PD_TYPE,/*64*/ MTK_PULL_PU_PD_TYPE,/*65*/ + MTK_PULL_PU_PD_TYPE,/*66*/ MTK_PULL_PU_PD_TYPE,/*67*/ + MTK_PULL_PU_PD_TYPE,/*68*/ MTK_PULL_PU_PD_TYPE,/*69*/ + MTK_PULL_PU_PD_TYPE,/*70*/ MTK_PULL_PU_PD_TYPE,/*71*/ + MTK_PULL_PU_PD_TYPE,/*72*/ MTK_PULL_PU_PD_TYPE,/*73*/ + MTK_PULL_PU_PD_TYPE,/*74*/ MTK_PULL_PU_PD_TYPE,/*75*/ + MTK_PULL_PU_PD_TYPE,/*76*/ MTK_PULL_PU_PD_TYPE,/*77*/ + MTK_PULL_PU_PD_TYPE,/*78*/ MTK_PULL_PU_PD_TYPE,/*79*/ + MTK_PULL_PU_PD_TYPE,/*80*/ MTK_PULL_PU_PD_TYPE,/*81*/ + MTK_PULL_PU_PD_TYPE,/*82*/ MTK_PULL_PU_PD_TYPE,/*83*/ + MTK_PULL_PU_PD_TYPE,/*84*/ MTK_PULL_PU_PD_TYPE,/*85*/ + MTK_PULL_PU_PD_TYPE,/*86*/ MTK_PULL_PU_PD_TYPE,/*87*/ + MTK_PULL_PU_PD_TYPE,/*88*/ MTK_PULL_PU_PD_TYPE,/*89*/ + MTK_PULL_PU_PD_TYPE,/*90*/ MTK_PULL_PU_PD_TYPE,/*91*/ + MTK_PULL_PU_PD_TYPE,/*92*/ MTK_PULL_PU_PD_TYPE,/*93*/ + MTK_PULL_PU_PD_TYPE,/*94*/ MTK_PULL_PU_PD_TYPE,/*95*/ + MTK_PULL_PU_PD_TYPE,/*96*/ MTK_PULL_PU_PD_TYPE,/*97*/ + MTK_PULL_PU_PD_TYPE,/*98*/ MTK_PULL_PU_PD_TYPE,/*99*/ + MTK_PULL_PU_PD_TYPE,/*100*/ MTK_PULL_PU_PD_TYPE,/*101*/ + MTK_PULL_PU_PD_TYPE,/*102*/ MTK_PULL_PU_PD_TYPE,/*103*/ + MTK_PULL_PU_PD_TYPE,/*104*/ MTK_PULL_PU_PD_TYPE,/*105*/ + MTK_PULL_PU_PD_TYPE,/*106*/ MTK_PULL_PU_PD_TYPE,/*107*/ + MTK_PULL_PU_PD_TYPE,/*108*/ MTK_PULL_PU_PD_TYPE,/*109*/ + MTK_PULL_PU_PD_TYPE,/*110*/ MTK_PULL_PU_PD_TYPE,/*111*/ + MTK_PULL_PU_PD_TYPE,/*112*/ MTK_PULL_PU_PD_TYPE,/*113*/ + MTK_PULL_PU_PD_TYPE,/*114*/ MTK_PULL_PU_PD_TYPE,/*115*/ + MTK_PULL_PU_PD_TYPE,/*116*/ MTK_PULL_PU_PD_TYPE,/*117*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*118*/ MTK_PULL_PU_PD_RSEL_TYPE,/*119*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*120*/ MTK_PULL_PU_PD_RSEL_TYPE,/*121*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*122*/ MTK_PULL_PU_PD_RSEL_TYPE,/*123*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*124*/ MTK_PULL_PU_PD_RSEL_TYPE,/*125*/ + MTK_PULL_PU_PD_TYPE,/*126*/ MTK_PULL_PU_PD_TYPE,/*127*/ + MTK_PULL_PU_PD_TYPE,/*128*/ MTK_PULL_PU_PD_TYPE,/*129*/ + MTK_PULL_PU_PD_TYPE,/*130*/ MTK_PULL_PU_PD_TYPE,/*131*/ + MTK_PULL_PU_PD_TYPE,/*132*/ MTK_PULL_PU_PD_TYPE,/*133*/ + MTK_PULL_PU_PD_TYPE,/*134*/ MTK_PULL_PU_PD_TYPE,/*135*/ + MTK_PULL_PU_PD_TYPE,/*136*/ MTK_PULL_PU_PD_TYPE,/*137*/ + MTK_PULL_PU_PD_TYPE,/*138*/ MTK_PULL_PU_PD_RSEL_TYPE,/*139*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*140*/ MTK_PULL_PU_PD_RSEL_TYPE,/*141*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*142*/ MTK_PULL_PU_PD_TYPE,/*143*/ + MTK_PULL_PU_PD_TYPE,/*144*/ MTK_PULL_PU_PD_TYPE,/*145*/ + MTK_PULL_PU_PD_TYPE,/*146*/ MTK_PULL_PU_PD_TYPE,/*147*/ + MTK_PULL_PU_PD_TYPE,/*148*/ MTK_PULL_PU_PD_TYPE,/*149*/ + MTK_PULL_PU_PD_TYPE,/*150*/ MTK_PULL_PU_PD_TYPE,/*151*/ + MTK_PULL_PUPD_R1R0_TYPE,/*152*/ MTK_PULL_PUPD_R1R0_TYPE,/*153*/ + MTK_PULL_PUPD_R1R0_TYPE,/*154*/ MTK_PULL_PUPD_R1R0_TYPE,/*155*/ + MTK_PULL_PU_PD_TYPE,/*156*/ MTK_PULL_PU_PD_TYPE,/*157*/ + MTK_PULL_PU_PD_TYPE,/*158*/ MTK_PULL_PU_PD_TYPE,/*159*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*160*/ MTK_PULL_PU_PD_RSEL_TYPE,/*161*/ + MTK_PULL_PU_PD_TYPE,/*162*/ MTK_PULL_PU_PD_TYPE,/*163*/ + MTK_PULL_PU_PD_TYPE,/*164*/ MTK_PULL_PU_PD_TYPE,/*165*/ + MTK_PULL_PU_PD_TYPE,/*166*/ MTK_PULL_PU_PD_TYPE,/*167*/ + MTK_PULL_PU_PD_TYPE,/*168*/ MTK_PULL_PU_PD_TYPE,/*169*/ + MTK_PULL_PU_PD_TYPE,/*170*/ MTK_PULL_PU_PD_TYPE,/*171*/ + MTK_PULL_PU_PD_TYPE,/*172*/ MTK_PULL_PU_PD_TYPE,/*173*/ + MTK_PULL_PU_PD_TYPE,/*174*/ MTK_PULL_PU_PD_TYPE,/*175*/ + MTK_PULL_PU_PD_TYPE,/*176*/ MTK_PULL_PU_PD_TYPE,/*177*/ + MTK_PULL_PU_PD_TYPE,/*178*/ MTK_PULL_PU_PD_TYPE,/*179*/ + MTK_PULL_PU_PD_TYPE,/*180*/ MTK_PULL_PU_PD_TYPE,/*181*/ + MTK_PULL_PU_PD_TYPE,/*182*/ MTK_PULL_PUPD_R1R0_TYPE,/*183*/ + MTK_PULL_PUPD_R1R0_TYPE,/*184*/ MTK_PULL_PUPD_R1R0_TYPE,/*185*/ + MTK_PULL_PUPD_R1R0_TYPE,/*186*/ MTK_PULL_PUPD_R1R0_TYPE,/*187*/ + MTK_PULL_PUPD_R1R0_TYPE,/*188*/ MTK_PULL_PUPD_R1R0_TYPE,/*189*/ + MTK_PULL_PUPD_R1R0_TYPE,/*190*/ MTK_PULL_PUPD_R1R0_TYPE,/*191*/ + MTK_PULL_PUPD_R1R0_TYPE,/*192*/ MTK_PULL_PUPD_R1R0_TYPE,/*193*/ + MTK_PULL_PUPD_R1R0_TYPE,/*194*/ MTK_PULL_PU_PD_TYPE,/*195*/ + MTK_PULL_PU_PD_TYPE,/*196*/ MTK_PULL_PU_PD_TYPE,/*197*/ + MTK_PULL_PU_PD_TYPE,/*198*/ MTK_PULL_PU_PD_TYPE,/*199*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*200*/ MTK_PULL_PU_PD_RSEL_TYPE,/*201*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*202*/ MTK_PULL_PU_PD_RSEL_TYPE,/*203*/ + MTK_PULL_PU_PD_RSEL_TYPE,/*204*/ MTK_PULL_PU_PD_RSEL_TYPE,/*205*/ + MTK_PULL_PU_PD_TYPE,/*206*/ MTK_PULL_PU_PD_TYPE,/*207*/ + MTK_PULL_PU_PD_TYPE,/*208*/ MTK_PULL_PU_PD_TYPE,/*209*/ + MTK_PULL_PU_PD_TYPE,/*210*/ MTK_PULL_PU_PD_TYPE,/*211*/ + MTK_PULL_PU_PD_TYPE,/*212*/ MTK_PULL_PU_PD_TYPE,/*213*/ + MTK_PULL_PU_PD_TYPE,/*214*/ MTK_PULL_PU_PD_TYPE,/*215*/ + MTK_PULL_PU_PD_TYPE,/*216*/ MTK_PULL_PU_PD_TYPE,/*217*/ + MTK_PULL_PU_PD_TYPE,/*218*/ MTK_PULL_PU_PD_TYPE,/*219*/ +}; static const char * const mt8192_pinctrl_register_base_names[] = { "iocfg0", "iocfg_rm", "iocfg_bm", "iocfg_bl", "iocfg_br", @@ -1355,9 +1387,8 @@ static const struct mtk_pin_reg_calc mt8192_reg_cals[PINCTRL_PIN_REG_MAX] = { [PINCTRL_PIN_REG_PUPD] = MTK_RANGE(mt8192_pin_pupd_range), [PINCTRL_PIN_REG_R0] = MTK_RANGE(mt8192_pin_r0_range), [PINCTRL_PIN_REG_R1] = MTK_RANGE(mt8192_pin_r1_range), - [PINCTRL_PIN_REG_DRV_EN] = MTK_RANGE(mt8192_pin_e1e0en_range), - [PINCTRL_PIN_REG_DRV_E0] = MTK_RANGE(mt8192_pin_e0_range), - [PINCTRL_PIN_REG_DRV_E1] = MTK_RANGE(mt8192_pin_e1_range), + [PINCTRL_PIN_REG_DRV_ADV] = MTK_RANGE(mt8192_pin_drv_adv_range), + [PINCTRL_PIN_REG_RSEL] = MTK_RANGE(mt8192_pin_rsel_range), }; static const struct mtk_pin_soc mt8192_data = { @@ -1367,17 +1398,16 @@ static const struct mtk_pin_soc mt8192_data = { .ngrps = ARRAY_SIZE(mtk_pins_mt8192), .base_names = mt8192_pinctrl_register_base_names, .nbase_names = ARRAY_SIZE(mt8192_pinctrl_register_base_names), + .pull_type = mt8192_pull_type, .eint_hw = &mt8192_eint_hw, .nfuncs = 8, .gpio_m = 0, .bias_set_combo = mtk_pinconf_bias_set_combo, .bias_get_combo = mtk_pinconf_bias_get_combo, - .drive_set = mtk_pinconf_drive_set_raw, - .drive_get = mtk_pinconf_drive_get_raw, - .adv_pull_get = mtk_pinconf_adv_pull_get, - .adv_pull_set = mtk_pinconf_adv_pull_set, - .adv_drive_get = mtk_pinconf_adv_drive_get, - .adv_drive_set = mtk_pinconf_adv_drive_set, + .drive_set = mtk_pinconf_drive_set_rev1, + .drive_get = mtk_pinconf_drive_get_rev1, + .adv_drive_get = mtk_pinconf_adv_drive_get_raw, + .adv_drive_set = mtk_pinconf_adv_drive_set_raw, }; static const struct of_device_id mt8192_pinctrl_of_match[] = { diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index a1f93859e7ca..8ef0a97d2bf5 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -96,10 +96,12 @@ static struct mvebu_pinctrl_group *mvebu_pinctrl_find_group_by_name( struct mvebu_pinctrl *pctl, const char *name) { unsigned n; + for (n = 0; n < pctl->num_groups; n++) { if (strcmp(name, pctl->groups[n].name) == 0) return &pctl->groups[n]; } + return NULL; } @@ -108,6 +110,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_val( unsigned long config) { unsigned n; + for (n = 0; n < grp->num_settings; n++) { if (config == grp->settings[n].val) { if (!pctl->variant || (pctl->variant & @@ -115,6 +118,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_val( return &grp->settings[n]; } } + return NULL; } @@ -123,6 +127,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_name( const char *name) { unsigned n; + for (n = 0; n < grp->num_settings; n++) { if (strcmp(name, grp->settings[n].name) == 0) { if (!pctl->variant || (pctl->variant & @@ -130,6 +135,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_name( return &grp->settings[n]; } } + return NULL; } @@ -137,6 +143,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_gpio_setting( struct mvebu_pinctrl *pctl, struct mvebu_pinctrl_group *grp) { unsigned n; + for (n = 0; n < grp->num_settings; n++) { if (grp->settings[n].flags & (MVEBU_SETTING_GPO | MVEBU_SETTING_GPI)) { @@ -145,6 +152,7 @@ static struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_gpio_setting( return &grp->settings[n]; } } + return NULL; } @@ -152,10 +160,12 @@ static struct mvebu_pinctrl_function *mvebu_pinctrl_find_function_by_name( struct mvebu_pinctrl *pctl, const char *name) { unsigned n; + for (n = 0; n < pctl->num_functions; n++) { if (strcmp(name, pctl->functions[n].name) == 0) return &pctl->functions[n]; } + return NULL; } diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index 640e50d94f27..f5014d09d81a 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -1421,8 +1421,10 @@ static int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, has_config = nmk_pinctrl_dt_get_config(np, &configs); np_config = of_parse_phandle(np, "ste,config", 0); - if (np_config) + if (np_config) { has_config |= nmk_pinctrl_dt_get_config(np_config, &configs); + of_node_put(np_config); + } if (has_config) { const char *gpio_name; const char *pin; diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 0645c2c24f50..4691a33bc374 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -6,8 +6,6 @@ * Authors: Ken Xue <Ken.Xue@amd.com> * Wu, Jeff <Jeff.Wu@amd.com> * - * Contact Information: Nehal Shah <Nehal-bakulchandra.Shah@amd.com> - * Shyam Sundar S K <Shyam-sundar.S-k@amd.com> */ #include <linux/err.h> @@ -31,6 +29,7 @@ #include <linux/bitops.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinmux.h> #include "core.h" #include "pinctrl-utils.h" @@ -203,8 +202,6 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) struct amd_gpio *gpio_dev = gpiochip_get_data(gc); bool tmr_out_unit; - unsigned int time; - unsigned int unit; bool tmr_large; char *level_trig; @@ -218,13 +215,13 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) char *pull_up_sel; char *pull_up_enable; char *pull_down_enable; - char *output_value; - char *output_enable; + char *orientation; char debounce_value[40]; char *debounce_enable; for (bank = 0; bank < gpio_dev->hwbank_num; bank++) { - seq_printf(s, "GPIO bank%d\t", bank); + unsigned int time = 0; + unsigned int unit = 0; switch (bank) { case 0: @@ -247,8 +244,9 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) /* Illegal bank number, ignore */ continue; } + seq_printf(s, "GPIO bank%d\n", bank); for (; i < pin_num; i++) { - seq_printf(s, "pin%d\t", i); + seq_printf(s, "📌%d\t", i); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + i * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); @@ -256,84 +254,91 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) if (pin_reg & BIT(INTERRUPT_ENABLE_OFF)) { u8 level = (pin_reg >> ACTIVE_LEVEL_OFF) & ACTIVE_LEVEL_MASK; - interrupt_enable = "interrupt is enabled|"; + interrupt_enable = "+"; if (level == ACTIVE_LEVEL_HIGH) - active_level = "Active high|"; + active_level = "↑"; else if (level == ACTIVE_LEVEL_LOW) - active_level = "Active low|"; + active_level = "↓"; else if (!(pin_reg & BIT(LEVEL_TRIG_OFF)) && level == ACTIVE_LEVEL_BOTH) - active_level = "Active on both|"; + active_level = "b"; else - active_level = "Unknown Active level|"; + active_level = "?"; if (pin_reg & BIT(LEVEL_TRIG_OFF)) - level_trig = "Level trigger|"; + level_trig = "level"; else - level_trig = "Edge trigger|"; + level_trig = " edge"; } else { - interrupt_enable = - "interrupt is disabled|"; - active_level = " "; - level_trig = " "; + interrupt_enable = "∅"; + active_level = "∅"; + level_trig = " ∅"; } if (pin_reg & BIT(INTERRUPT_MASK_OFF)) - interrupt_mask = - "interrupt is unmasked|"; + interrupt_mask = "-"; else - interrupt_mask = - "interrupt is masked|"; + interrupt_mask = "+"; + seq_printf(s, "int %s (🎭 %s)| active-%s| %s-🔫| ", + interrupt_enable, + interrupt_mask, + active_level, + level_trig); if (pin_reg & BIT(WAKE_CNTRL_OFF_S0I3)) - wake_cntrl0 = "enable wakeup in S0i3 state|"; + wake_cntrl0 = "+"; else - wake_cntrl0 = "disable wakeup in S0i3 state|"; + wake_cntrl0 = "∅"; + seq_printf(s, "S0i3 🌅 %s| ", wake_cntrl0); if (pin_reg & BIT(WAKE_CNTRL_OFF_S3)) - wake_cntrl1 = "enable wakeup in S3 state|"; + wake_cntrl1 = "+"; else - wake_cntrl1 = "disable wakeup in S3 state|"; + wake_cntrl1 = "∅"; + seq_printf(s, "S3 🌅 %s| ", wake_cntrl1); if (pin_reg & BIT(WAKE_CNTRL_OFF_S4)) - wake_cntrl2 = "enable wakeup in S4/S5 state|"; + wake_cntrl2 = "+"; else - wake_cntrl2 = "disable wakeup in S4/S5 state|"; + wake_cntrl2 = "∅"; + seq_printf(s, "S4/S5 🌅 %s| ", wake_cntrl2); if (pin_reg & BIT(PULL_UP_ENABLE_OFF)) { - pull_up_enable = "pull-up is enabled|"; + pull_up_enable = "+"; if (pin_reg & BIT(PULL_UP_SEL_OFF)) - pull_up_sel = "8k pull-up|"; + pull_up_sel = "8k"; else - pull_up_sel = "4k pull-up|"; + pull_up_sel = "4k"; } else { - pull_up_enable = "pull-up is disabled|"; - pull_up_sel = " "; + pull_up_enable = "∅"; + pull_up_sel = " "; } + seq_printf(s, "pull-↑ %s (%s)| ", + pull_up_enable, + pull_up_sel); if (pin_reg & BIT(PULL_DOWN_ENABLE_OFF)) - pull_down_enable = "pull-down is enabled|"; + pull_down_enable = "+"; else - pull_down_enable = "Pull-down is disabled|"; + pull_down_enable = "∅"; + seq_printf(s, "pull-↓ %s| ", pull_down_enable); if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) { - pin_sts = " "; - output_enable = "output is enabled|"; + pin_sts = "output"; if (pin_reg & BIT(OUTPUT_VALUE_OFF)) - output_value = "output is high|"; + orientation = "↑"; else - output_value = "output is low|"; + orientation = "↓"; } else { - output_enable = "output is disabled|"; - output_value = " "; - + pin_sts = "input "; if (pin_reg & BIT(PIN_STS_OFF)) - pin_sts = "input is high|"; + orientation = "↑"; else - pin_sts = "input is low|"; + orientation = "↓"; } + seq_printf(s, "%s %s| ", pin_sts, orientation); db_cntrl = (DB_CNTRl_MASK << DB_CNTRL_OFF) & pin_reg; if (db_cntrl) { @@ -352,27 +357,18 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) unit = 61; } if ((DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF) == db_cntrl) - debounce_enable = "debouncing filter (high and low) enabled|"; + debounce_enable = "b +"; else if ((DB_TYPE_PRESERVE_LOW_GLITCH << DB_CNTRL_OFF) == db_cntrl) - debounce_enable = "debouncing filter (low) enabled|"; + debounce_enable = "↓ +"; else - debounce_enable = "debouncing filter (high) enabled|"; + debounce_enable = "↑ +"; - snprintf(debounce_value, sizeof(debounce_value), - "debouncing timeout is %u (us)|", time * unit); } else { - debounce_enable = "debouncing filter disabled|"; - snprintf(debounce_value, sizeof(debounce_value), " "); + debounce_enable = " ∅"; } - - seq_printf(s, "%s %s %s %s %s %s\n" - " %s %s %s %s %s %s %s %s %s 0x%x\n", - level_trig, active_level, interrupt_enable, - interrupt_mask, wake_cntrl0, wake_cntrl1, - wake_cntrl2, pin_sts, pull_up_sel, - pull_up_enable, pull_down_enable, - output_value, output_enable, - debounce_enable, debounce_value, pin_reg); + snprintf(debounce_value, sizeof(debounce_value), "%u", time * unit); + seq_printf(s, "debounce %s (⏰ %sus)| ", debounce_enable, debounce_value); + seq_printf(s, " 0x%x\n", pin_reg); } } } @@ -917,6 +913,7 @@ static int amd_gpio_suspend(struct device *dev) { struct amd_gpio *gpio_dev = dev_get_drvdata(dev); struct pinctrl_desc *desc = gpio_dev->pctrl->desc; + unsigned long flags; int i; for (i = 0; i < desc->npins; i++) { @@ -925,7 +922,9 @@ static int amd_gpio_suspend(struct device *dev) if (!amd_gpio_should_save(gpio_dev, pin)) continue; - gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4); + raw_spin_lock_irqsave(&gpio_dev->lock, flags); + gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin * 4) & ~PIN_IRQ_PENDING; + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } return 0; @@ -935,6 +934,7 @@ static int amd_gpio_resume(struct device *dev) { struct amd_gpio *gpio_dev = dev_get_drvdata(dev); struct pinctrl_desc *desc = gpio_dev->pctrl->desc; + unsigned long flags; int i; for (i = 0; i < desc->npins; i++) { @@ -943,7 +943,10 @@ static int amd_gpio_resume(struct device *dev) if (!amd_gpio_should_save(gpio_dev, pin)) continue; - writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4); + raw_spin_lock_irqsave(&gpio_dev->lock, flags); + gpio_dev->saved_regs[i] |= readl(gpio_dev->base + pin * 4) & PIN_IRQ_PENDING; + writel(gpio_dev->saved_regs[i], gpio_dev->base + pin * 4); + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } return 0; @@ -955,14 +958,115 @@ static const struct dev_pm_ops amd_gpio_pm_ops = { }; #endif +static int amd_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(pmx_functions); +} + +static const char *amd_get_fname(struct pinctrl_dev *pctrldev, unsigned int selector) +{ + return pmx_functions[selector].name; +} + +static int amd_get_groups(struct pinctrl_dev *pctrldev, unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctrldev); + + if (!gpio_dev->iomux_base) { + dev_err(&gpio_dev->pdev->dev, "iomux function %d group not supported\n", selector); + return -EINVAL; + } + + *groups = pmx_functions[selector].groups; + *num_groups = pmx_functions[selector].ngroups; + return 0; +} + +static int amd_set_mux(struct pinctrl_dev *pctrldev, unsigned int function, unsigned int group) +{ + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctrldev); + struct device *dev = &gpio_dev->pdev->dev; + struct pin_desc *pd; + int ind, index; + + if (!gpio_dev->iomux_base) + return -EINVAL; + + for (index = 0; index < NSELECTS; index++) { + if (strcmp(gpio_dev->groups[group].name, pmx_functions[function].groups[index])) + continue; + + if (readb(gpio_dev->iomux_base + pmx_functions[function].index) == + FUNCTION_INVALID) { + dev_err(dev, "IOMUX_GPIO 0x%x not present or supported\n", + pmx_functions[function].index); + return -EINVAL; + } + + writeb(index, gpio_dev->iomux_base + pmx_functions[function].index); + + if (index != (readb(gpio_dev->iomux_base + pmx_functions[function].index) & + FUNCTION_MASK)) { + dev_err(dev, "IOMUX_GPIO 0x%x not present or supported\n", + pmx_functions[function].index); + return -EINVAL; + } + + for (ind = 0; ind < gpio_dev->groups[group].npins; ind++) { + if (strncmp(gpio_dev->groups[group].name, "IMX_F", strlen("IMX_F"))) + continue; + + pd = pin_desc_get(gpio_dev->pctrl, gpio_dev->groups[group].pins[ind]); + pd->mux_owner = gpio_dev->groups[group].name; + } + break; + } + + return 0; +} + +static const struct pinmux_ops amd_pmxops = { + .get_functions_count = amd_get_functions_count, + .get_function_name = amd_get_fname, + .get_function_groups = amd_get_groups, + .set_mux = amd_set_mux, +}; + static struct pinctrl_desc amd_pinctrl_desc = { .pins = kerncz_pins, .npins = ARRAY_SIZE(kerncz_pins), .pctlops = &amd_pinctrl_ops, + .pmxops = &amd_pmxops, .confops = &amd_pinconf_ops, .owner = THIS_MODULE, }; +static void amd_get_iomux_res(struct amd_gpio *gpio_dev) +{ + struct pinctrl_desc *desc = &amd_pinctrl_desc; + struct device *dev = &gpio_dev->pdev->dev; + int index; + + index = device_property_match_string(dev, "pinctrl-resource-names", "iomux"); + if (index < 0) { + dev_warn(dev, "failed to get iomux index\n"); + goto out_no_pinmux; + } + + gpio_dev->iomux_base = devm_platform_ioremap_resource(gpio_dev->pdev, index); + if (IS_ERR(gpio_dev->iomux_base)) { + dev_warn(dev, "Failed to get iomux %d io resource\n", index); + goto out_no_pinmux; + } + + return; + +out_no_pinmux: + desc->pmxops = NULL; +} + static int amd_gpio_probe(struct platform_device *pdev) { int ret = 0; @@ -977,17 +1081,12 @@ static int amd_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&gpio_dev->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + gpio_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(gpio_dev->base)) { dev_err(&pdev->dev, "Failed to get gpio io resource.\n"); - return -EINVAL; + return PTR_ERR(gpio_dev->base); } - gpio_dev->base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!gpio_dev->base) - return -ENOMEM; - gpio_dev->irq = platform_get_irq(pdev, 0); if (gpio_dev->irq < 0) return gpio_dev->irq; @@ -1020,6 +1119,7 @@ static int amd_gpio_probe(struct platform_device *pdev) gpio_dev->ngroups = ARRAY_SIZE(kerncz_groups); amd_pinctrl_desc.name = dev_name(&pdev->dev); + amd_get_iomux_res(gpio_dev); gpio_dev->pctrl = devm_pinctrl_register(&pdev->dev, &amd_pinctrl_desc, gpio_dev); if (IS_ERR(gpio_dev->pctrl)) { diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h index 1d4317073654..c8635998465d 100644 --- a/drivers/pinctrl/pinctrl-amd.h +++ b/drivers/pinctrl/pinctrl-amd.h @@ -74,23 +74,24 @@ #define CLR_INTR_STAT 0x1UL -struct amd_pingroup { - const char *name; - const unsigned *pins; - unsigned npins; -}; +#define NSELECTS 0x4 + +#define FUNCTION_MASK GENMASK(1, 0) +#define FUNCTION_INVALID GENMASK(7, 0) struct amd_function { const char *name; - const char * const *groups; + const char * const groups[NSELECTS]; unsigned ngroups; + int index; }; struct amd_gpio { raw_spinlock_t lock; void __iomem *base; + void __iomem *iomux_base; - const struct amd_pingroup *groups; + const struct pingroup *groups; u32 ngroups; struct pinctrl_dev *pctrl; struct gpio_chip gc; @@ -288,45 +289,1332 @@ static const struct pinctrl_pin_desc kerncz_pins[] = { PINCTRL_PIN(183, "GPIO_183"), }; -static const unsigned i2c0_pins[] = {145, 146}; -static const unsigned i2c1_pins[] = {147, 148}; -static const unsigned i2c2_pins[] = {113, 114}; -static const unsigned i2c3_pins[] = {19, 20}; +#define AMD_PINS(...) (const unsigned int []){__VA_ARGS__} + +enum amd_functions { + IMX_F0_GPIO0, + IMX_F1_GPIO0, + IMX_F2_GPIO0, + IMX_F3_GPIO0, + IMX_F0_GPIO1, + IMX_F1_GPIO1, + IMX_F2_GPIO1, + IMX_F3_GPIO1, + IMX_F0_GPIO2, + IMX_F1_GPIO2, + IMX_F2_GPIO2, + IMX_F3_GPIO2, + IMX_F0_GPIO3, + IMX_F1_GPIO3, + IMX_F2_GPIO3, + IMX_F3_GPIO3, + IMX_F0_GPIO4, + IMX_F1_GPIO4, + IMX_F2_GPIO4, + IMX_F3_GPIO4, + IMX_F0_GPIO5, + IMX_F1_GPIO5, + IMX_F2_GPIO5, + IMX_F3_GPIO5, + IMX_F0_GPIO6, + IMX_F1_GPIO6, + IMX_F2_GPIO6, + IMX_F3_GPIO6, + IMX_F0_GPIO7, + IMX_F1_GPIO7, + IMX_F2_GPIO7, + IMX_F3_GPIO7, + IMX_F0_GPIO8, + IMX_F1_GPIO8, + IMX_F2_GPIO8, + IMX_F3_GPIO8, + IMX_F0_GPIO9, + IMX_F1_GPIO9, + IMX_F2_GPIO9, + IMX_F3_GPIO9, + IMX_F0_GPIO10, + IMX_F1_GPIO10, + IMX_F2_GPIO10, + IMX_F3_GPIO10, + IMX_F0_GPIO11, + IMX_F1_GPIO11, + IMX_F2_GPIO11, + IMX_F3_GPIO11, + IMX_F0_GPIO12, + IMX_F1_GPIO12, + IMX_F2_GPIO12, + IMX_F3_GPIO12, + IMX_F0_GPIO13, + IMX_F1_GPIO13, + IMX_F2_GPIO13, + IMX_F3_GPIO13, + IMX_F0_GPIO14, + IMX_F1_GPIO14, + IMX_F2_GPIO14, + IMX_F3_GPIO14, + IMX_F0_GPIO15, + IMX_F1_GPIO15, + IMX_F2_GPIO15, + IMX_F3_GPIO15, + IMX_F0_GPIO16, + IMX_F1_GPIO16, + IMX_F2_GPIO16, + IMX_F3_GPIO16, + IMX_F0_GPIO17, + IMX_F1_GPIO17, + IMX_F2_GPIO17, + IMX_F3_GPIO17, + IMX_F0_GPIO18, + IMX_F1_GPIO18, + IMX_F2_GPIO18, + IMX_F3_GPIO18, + IMX_F0_GPIO19, + IMX_F1_GPIO19, + IMX_F2_GPIO19, + IMX_F3_GPIO19, + IMX_F0_GPIO20, + IMX_F1_GPIO20, + IMX_F2_GPIO20, + IMX_F3_GPIO20, + IMX_F0_GPIO21, + IMX_F1_GPIO21, + IMX_F2_GPIO21, + IMX_F3_GPIO21, + IMX_F0_GPIO22, + IMX_F1_GPIO22, + IMX_F2_GPIO22, + IMX_F3_GPIO22, + IMX_F0_GPIO23, + IMX_F1_GPIO23, + IMX_F2_GPIO23, + IMX_F3_GPIO23, + IMX_F0_GPIO24, + IMX_F1_GPIO24, + IMX_F2_GPIO24, + IMX_F3_GPIO24, + IMX_F0_GPIO25, + IMX_F1_GPIO25, + IMX_F2_GPIO25, + IMX_F3_GPIO25, + IMX_F0_GPIO26, + IMX_F1_GPIO26, + IMX_F2_GPIO26, + IMX_F3_GPIO26, + IMX_F0_GPIO27, + IMX_F1_GPIO27, + IMX_F2_GPIO27, + IMX_F3_GPIO27, + IMX_F0_GPIO28, + IMX_F1_GPIO28, + IMX_F2_GPIO28, + IMX_F3_GPIO28, + IMX_F0_GPIO29, + IMX_F1_GPIO29, + IMX_F2_GPIO29, + IMX_F3_GPIO29, + IMX_F0_GPIO30, + IMX_F1_GPIO30, + IMX_F2_GPIO30, + IMX_F3_GPIO30, + IMX_F0_GPIO31, + IMX_F1_GPIO31, + IMX_F2_GPIO31, + IMX_F3_GPIO31, + IMX_F0_GPIO32, + IMX_F1_GPIO32, + IMX_F2_GPIO32, + IMX_F3_GPIO32, + IMX_F0_GPIO33, + IMX_F1_GPIO33, + IMX_F2_GPIO33, + IMX_F3_GPIO33, + IMX_F0_GPIO34, + IMX_F1_GPIO34, + IMX_F2_GPIO34, + IMX_F3_GPIO34, + IMX_F0_GPIO35, + IMX_F1_GPIO35, + IMX_F2_GPIO35, + IMX_F3_GPIO35, + IMX_F0_GPIO36, + IMX_F1_GPIO36, + IMX_F2_GPIO36, + IMX_F3_GPIO36, + IMX_F0_GPIO37, + IMX_F1_GPIO37, + IMX_F2_GPIO37, + IMX_F3_GPIO37, + IMX_F0_GPIO38, + IMX_F1_GPIO38, + IMX_F2_GPIO38, + IMX_F3_GPIO38, + IMX_F0_GPIO39, + IMX_F1_GPIO39, + IMX_F2_GPIO39, + IMX_F3_GPIO39, + IMX_F0_GPIO40, + IMX_F1_GPIO40, + IMX_F2_GPIO40, + IMX_F3_GPIO40, + IMX_F0_GPIO41, + IMX_F1_GPIO41, + IMX_F2_GPIO41, + IMX_F3_GPIO41, + IMX_F0_GPIO42, + IMX_F1_GPIO42, + IMX_F2_GPIO42, + IMX_F3_GPIO42, + IMX_F0_GPIO43, + IMX_F1_GPIO43, + IMX_F2_GPIO43, + IMX_F3_GPIO43, + IMX_F0_GPIO44, + IMX_F1_GPIO44, + IMX_F2_GPIO44, + IMX_F3_GPIO44, + IMX_F0_GPIO45, + IMX_F1_GPIO45, + IMX_F2_GPIO45, + IMX_F3_GPIO45, + IMX_F0_GPIO46, + IMX_F1_GPIO46, + IMX_F2_GPIO46, + IMX_F3_GPIO46, + IMX_F0_GPIO47, + IMX_F1_GPIO47, + IMX_F2_GPIO47, + IMX_F3_GPIO47, + IMX_F0_GPIO48, + IMX_F1_GPIO48, + IMX_F2_GPIO48, + IMX_F3_GPIO48, + IMX_F0_GPIO49, + IMX_F1_GPIO49, + IMX_F2_GPIO49, + IMX_F3_GPIO49, + IMX_F0_GPIO50, + IMX_F1_GPIO50, + IMX_F2_GPIO50, + IMX_F3_GPIO50, + IMX_F0_GPIO51, + IMX_F1_GPIO51, + IMX_F2_GPIO51, + IMX_F3_GPIO51, + IMX_F0_GPIO52, + IMX_F1_GPIO52, + IMX_F2_GPIO52, + IMX_F3_GPIO52, + IMX_F0_GPIO53, + IMX_F1_GPIO53, + IMX_F2_GPIO53, + IMX_F3_GPIO53, + IMX_F0_GPIO54, + IMX_F1_GPIO54, + IMX_F2_GPIO54, + IMX_F3_GPIO54, + IMX_F0_GPIO55, + IMX_F1_GPIO55, + IMX_F2_GPIO55, + IMX_F3_GPIO55, + IMX_F0_GPIO56, + IMX_F1_GPIO56, + IMX_F2_GPIO56, + IMX_F3_GPIO56, + IMX_F0_GPIO57, + IMX_F1_GPIO57, + IMX_F2_GPIO57, + IMX_F3_GPIO57, + IMX_F0_GPIO58, + IMX_F1_GPIO58, + IMX_F2_GPIO58, + IMX_F3_GPIO58, + IMX_F0_GPIO59, + IMX_F1_GPIO59, + IMX_F2_GPIO59, + IMX_F3_GPIO59, + IMX_F0_GPIO60, + IMX_F1_GPIO60, + IMX_F2_GPIO60, + IMX_F3_GPIO60, + IMX_F0_GPIO61, + IMX_F1_GPIO61, + IMX_F2_GPIO61, + IMX_F3_GPIO61, + IMX_F0_GPIO62, + IMX_F1_GPIO62, + IMX_F2_GPIO62, + IMX_F3_GPIO62, + IMX_F0_GPIO64, + IMX_F1_GPIO64, + IMX_F2_GPIO64, + IMX_F3_GPIO64, + IMX_F0_GPIO65, + IMX_F1_GPIO65, + IMX_F2_GPIO65, + IMX_F3_GPIO65, + IMX_F0_GPIO66, + IMX_F1_GPIO66, + IMX_F2_GPIO66, + IMX_F3_GPIO66, + IMX_F0_GPIO67, + IMX_F1_GPIO67, + IMX_F2_GPIO67, + IMX_F3_GPIO67, + IMX_F0_GPIO68, + IMX_F1_GPIO68, + IMX_F2_GPIO68, + IMX_F3_GPIO68, + IMX_F0_GPIO69, + IMX_F1_GPIO69, + IMX_F2_GPIO69, + IMX_F3_GPIO69, + IMX_F0_GPIO70, + IMX_F1_GPIO70, + IMX_F2_GPIO70, + IMX_F3_GPIO70, + IMX_F0_GPIO71, + IMX_F1_GPIO71, + IMX_F2_GPIO71, + IMX_F3_GPIO71, + IMX_F0_GPIO72, + IMX_F1_GPIO72, + IMX_F2_GPIO72, + IMX_F3_GPIO72, + IMX_F0_GPIO73, + IMX_F1_GPIO73, + IMX_F2_GPIO73, + IMX_F3_GPIO73, + IMX_F0_GPIO74, + IMX_F1_GPIO74, + IMX_F2_GPIO74, + IMX_F3_GPIO74, + IMX_F0_GPIO75, + IMX_F1_GPIO75, + IMX_F2_GPIO75, + IMX_F3_GPIO75, + IMX_F0_GPIO76, + IMX_F1_GPIO76, + IMX_F2_GPIO76, + IMX_F3_GPIO76, + IMX_F0_GPIO77, + IMX_F1_GPIO77, + IMX_F2_GPIO77, + IMX_F3_GPIO77, + IMX_F0_GPIO78, + IMX_F1_GPIO78, + IMX_F2_GPIO78, + IMX_F3_GPIO78, + IMX_F0_GPIO79, + IMX_F1_GPIO79, + IMX_F2_GPIO79, + IMX_F3_GPIO79, + IMX_F0_GPIO80, + IMX_F1_GPIO80, + IMX_F2_GPIO80, + IMX_F3_GPIO80, + IMX_F0_GPIO81, + IMX_F1_GPIO81, + IMX_F2_GPIO81, + IMX_F3_GPIO81, + IMX_F0_GPIO82, + IMX_F1_GPIO82, + IMX_F2_GPIO82, + IMX_F3_GPIO82, + IMX_F0_GPIO83, + IMX_F1_GPIO83, + IMX_F2_GPIO83, + IMX_F3_GPIO83, + IMX_F0_GPIO84, + IMX_F1_GPIO84, + IMX_F2_GPIO84, + IMX_F3_GPIO84, + IMX_F0_GPIO85, + IMX_F1_GPIO85, + IMX_F2_GPIO85, + IMX_F3_GPIO85, + IMX_F0_GPIO86, + IMX_F1_GPIO86, + IMX_F2_GPIO86, + IMX_F3_GPIO86, + IMX_F0_GPIO87, + IMX_F1_GPIO87, + IMX_F2_GPIO87, + IMX_F3_GPIO87, + IMX_F0_GPIO88, + IMX_F1_GPIO88, + IMX_F2_GPIO88, + IMX_F3_GPIO88, + IMX_F0_GPIO89, + IMX_F1_GPIO89, + IMX_F2_GPIO89, + IMX_F3_GPIO89, + IMX_F0_GPIO90, + IMX_F1_GPIO90, + IMX_F2_GPIO90, + IMX_F3_GPIO90, + IMX_F0_GPIO91, + IMX_F1_GPIO91, + IMX_F2_GPIO91, + IMX_F3_GPIO91, + IMX_F0_GPIO92, + IMX_F1_GPIO92, + IMX_F2_GPIO92, + IMX_F3_GPIO92, + IMX_F0_GPIO93, + IMX_F1_GPIO93, + IMX_F2_GPIO93, + IMX_F3_GPIO93, + IMX_F0_GPIO94, + IMX_F1_GPIO94, + IMX_F2_GPIO94, + IMX_F3_GPIO94, + IMX_F0_GPIO95, + IMX_F1_GPIO95, + IMX_F2_GPIO95, + IMX_F3_GPIO95, + IMX_F0_GPIO96, + IMX_F1_GPIO96, + IMX_F2_GPIO96, + IMX_F3_GPIO96, + IMX_F0_GPIO97, + IMX_F1_GPIO97, + IMX_F2_GPIO97, + IMX_F3_GPIO97, + IMX_F0_GPIO98, + IMX_F1_GPIO98, + IMX_F2_GPIO98, + IMX_F3_GPIO98, + IMX_F0_GPIO99, + IMX_F1_GPIO99, + IMX_F2_GPIO99, + IMX_F3_GPIO99, + IMX_F0_GPIO100, + IMX_F1_GPIO100, + IMX_F2_GPIO100, + IMX_F3_GPIO100, + IMX_F0_GPIO101, + IMX_F1_GPIO101, + IMX_F2_GPIO101, + IMX_F3_GPIO101, + IMX_F0_GPIO102, + IMX_F1_GPIO102, + IMX_F2_GPIO102, + IMX_F3_GPIO102, + IMX_F0_GPIO103, + IMX_F1_GPIO103, + IMX_F2_GPIO103, + IMX_F3_GPIO103, + IMX_F0_GPIO104, + IMX_F1_GPIO104, + IMX_F2_GPIO104, + IMX_F3_GPIO104, + IMX_F0_GPIO105, + IMX_F1_GPIO105, + IMX_F2_GPIO105, + IMX_F3_GPIO105, + IMX_F0_GPIO106, + IMX_F1_GPIO106, + IMX_F2_GPIO106, + IMX_F3_GPIO106, + IMX_F0_GPIO107, + IMX_F1_GPIO107, + IMX_F2_GPIO107, + IMX_F3_GPIO107, + IMX_F0_GPIO108, + IMX_F1_GPIO108, + IMX_F2_GPIO108, + IMX_F3_GPIO108, + IMX_F0_GPIO109, + IMX_F1_GPIO109, + IMX_F2_GPIO109, + IMX_F3_GPIO109, + IMX_F0_GPIO110, + IMX_F1_GPIO110, + IMX_F2_GPIO110, + IMX_F3_GPIO110, + IMX_F0_GPIO111, + IMX_F1_GPIO111, + IMX_F2_GPIO111, + IMX_F3_GPIO111, + IMX_F0_GPIO112, + IMX_F1_GPIO112, + IMX_F2_GPIO112, + IMX_F3_GPIO112, + IMX_F0_GPIO113, + IMX_F1_GPIO113, + IMX_F2_GPIO113, + IMX_F3_GPIO113, + IMX_F0_GPIO114, + IMX_F1_GPIO114, + IMX_F2_GPIO114, + IMX_F3_GPIO114, + IMX_F0_GPIO115, + IMX_F1_GPIO115, + IMX_F2_GPIO115, + IMX_F3_GPIO115, + IMX_F0_GPIO116, + IMX_F1_GPIO116, + IMX_F2_GPIO116, + IMX_F3_GPIO116, + IMX_F0_GPIO117, + IMX_F1_GPIO117, + IMX_F2_GPIO117, + IMX_F3_GPIO117, + IMX_F0_GPIO118, + IMX_F1_GPIO118, + IMX_F2_GPIO118, + IMX_F3_GPIO118, + IMX_F0_GPIO119, + IMX_F1_GPIO119, + IMX_F2_GPIO119, + IMX_F3_GPIO119, + IMX_F0_GPIO120, + IMX_F1_GPIO120, + IMX_F2_GPIO120, + IMX_F3_GPIO120, + IMX_F0_GPIO121, + IMX_F1_GPIO121, + IMX_F2_GPIO121, + IMX_F3_GPIO121, + IMX_F0_GPIO122, + IMX_F1_GPIO122, + IMX_F2_GPIO122, + IMX_F3_GPIO122, + IMX_F0_GPIO123, + IMX_F1_GPIO123, + IMX_F2_GPIO123, + IMX_F3_GPIO123, + IMX_F0_GPIO124, + IMX_F1_GPIO124, + IMX_F2_GPIO124, + IMX_F3_GPIO124, + IMX_F0_GPIO125, + IMX_F1_GPIO125, + IMX_F2_GPIO125, + IMX_F3_GPIO125, + IMX_F0_GPIO126, + IMX_F1_GPIO126, + IMX_F2_GPIO126, + IMX_F3_GPIO126, + IMX_F0_GPIO127, + IMX_F1_GPIO127, + IMX_F2_GPIO127, + IMX_F3_GPIO127, + IMX_F0_GPIO128, + IMX_F1_GPIO128, + IMX_F2_GPIO128, + IMX_F3_GPIO128, + IMX_F0_GPIO129, + IMX_F1_GPIO129, + IMX_F2_GPIO129, + IMX_F3_GPIO129, + IMX_F0_GPIO130, + IMX_F1_GPIO130, + IMX_F2_GPIO130, + IMX_F3_GPIO130, + IMX_F0_GPIO131, + IMX_F1_GPIO131, + IMX_F2_GPIO131, + IMX_F3_GPIO131, + IMX_F0_GPIO132, + IMX_F1_GPIO132, + IMX_F2_GPIO132, + IMX_F3_GPIO132, + IMX_F0_GPIO133, + IMX_F1_GPIO133, + IMX_F2_GPIO133, + IMX_F3_GPIO133, + IMX_F0_GPIO134, + IMX_F1_GPIO134, + IMX_F2_GPIO134, + IMX_F3_GPIO134, + IMX_F0_GPIO135, + IMX_F1_GPIO135, + IMX_F2_GPIO135, + IMX_F3_GPIO135, + IMX_F0_GPIO136, + IMX_F1_GPIO136, + IMX_F2_GPIO136, + IMX_F3_GPIO136, + IMX_F0_GPIO137, + IMX_F1_GPIO137, + IMX_F2_GPIO137, + IMX_F3_GPIO137, + IMX_F0_GPIO138, + IMX_F1_GPIO138, + IMX_F2_GPIO138, + IMX_F3_GPIO138, + IMX_F0_GPIO139, + IMX_F1_GPIO139, + IMX_F2_GPIO139, + IMX_F3_GPIO139, + IMX_F0_GPIO140, + IMX_F1_GPIO140, + IMX_F2_GPIO140, + IMX_F3_GPIO140, + IMX_F0_GPIO141, + IMX_F1_GPIO141, + IMX_F2_GPIO141, + IMX_F3_GPIO141, + IMX_F0_GPIO142, + IMX_F1_GPIO142, + IMX_F2_GPIO142, + IMX_F3_GPIO142, + IMX_F0_GPIO143, + IMX_F1_GPIO143, + IMX_F2_GPIO143, + IMX_F3_GPIO143, + IMX_F0_GPIO144, + IMX_F1_GPIO144, + IMX_F2_GPIO144, + IMX_F3_GPIO144, +}; + +#define AMD_PINCTRL_FUNC_GRP(_number, _func) \ + [IMX_F##_func##_GPIO##_number] = \ + PINCTRL_PINGROUP("IMX_F"#_func "_GPIO"#_number, AMD_PINS(_number), 1) + +static const struct pingroup kerncz_groups[] = { + AMD_PINCTRL_FUNC_GRP(0, 0), + AMD_PINCTRL_FUNC_GRP(0, 1), + AMD_PINCTRL_FUNC_GRP(0, 2), + AMD_PINCTRL_FUNC_GRP(0, 3), + AMD_PINCTRL_FUNC_GRP(1, 0), + AMD_PINCTRL_FUNC_GRP(1, 1), + AMD_PINCTRL_FUNC_GRP(1, 2), + AMD_PINCTRL_FUNC_GRP(1, 3), + AMD_PINCTRL_FUNC_GRP(2, 0), + AMD_PINCTRL_FUNC_GRP(2, 1), + AMD_PINCTRL_FUNC_GRP(2, 2), + AMD_PINCTRL_FUNC_GRP(2, 3), + AMD_PINCTRL_FUNC_GRP(3, 0), + AMD_PINCTRL_FUNC_GRP(3, 1), + AMD_PINCTRL_FUNC_GRP(3, 2), + AMD_PINCTRL_FUNC_GRP(3, 3), + AMD_PINCTRL_FUNC_GRP(4, 0), + AMD_PINCTRL_FUNC_GRP(4, 1), + AMD_PINCTRL_FUNC_GRP(4, 2), + AMD_PINCTRL_FUNC_GRP(4, 3), + AMD_PINCTRL_FUNC_GRP(5, 0), + AMD_PINCTRL_FUNC_GRP(5, 1), + AMD_PINCTRL_FUNC_GRP(5, 2), + AMD_PINCTRL_FUNC_GRP(5, 3), + AMD_PINCTRL_FUNC_GRP(6, 0), + AMD_PINCTRL_FUNC_GRP(6, 1), + AMD_PINCTRL_FUNC_GRP(6, 2), + AMD_PINCTRL_FUNC_GRP(6, 3), + AMD_PINCTRL_FUNC_GRP(7, 0), + AMD_PINCTRL_FUNC_GRP(7, 1), + AMD_PINCTRL_FUNC_GRP(7, 2), + AMD_PINCTRL_FUNC_GRP(7, 3), + AMD_PINCTRL_FUNC_GRP(8, 0), + AMD_PINCTRL_FUNC_GRP(8, 1), + AMD_PINCTRL_FUNC_GRP(8, 2), + AMD_PINCTRL_FUNC_GRP(8, 3), + AMD_PINCTRL_FUNC_GRP(9, 0), + AMD_PINCTRL_FUNC_GRP(9, 1), + AMD_PINCTRL_FUNC_GRP(9, 2), + AMD_PINCTRL_FUNC_GRP(9, 3), + AMD_PINCTRL_FUNC_GRP(10, 0), + AMD_PINCTRL_FUNC_GRP(10, 1), + AMD_PINCTRL_FUNC_GRP(10, 2), + AMD_PINCTRL_FUNC_GRP(10, 3), + AMD_PINCTRL_FUNC_GRP(11, 0), + AMD_PINCTRL_FUNC_GRP(11, 1), + AMD_PINCTRL_FUNC_GRP(11, 2), + AMD_PINCTRL_FUNC_GRP(11, 3), + AMD_PINCTRL_FUNC_GRP(12, 0), + AMD_PINCTRL_FUNC_GRP(12, 1), + AMD_PINCTRL_FUNC_GRP(12, 2), + AMD_PINCTRL_FUNC_GRP(12, 3), + AMD_PINCTRL_FUNC_GRP(13, 0), + AMD_PINCTRL_FUNC_GRP(13, 1), + AMD_PINCTRL_FUNC_GRP(13, 2), + AMD_PINCTRL_FUNC_GRP(13, 3), + AMD_PINCTRL_FUNC_GRP(14, 0), + AMD_PINCTRL_FUNC_GRP(14, 1), + AMD_PINCTRL_FUNC_GRP(14, 2), + AMD_PINCTRL_FUNC_GRP(14, 3), + AMD_PINCTRL_FUNC_GRP(15, 0), + AMD_PINCTRL_FUNC_GRP(15, 1), + AMD_PINCTRL_FUNC_GRP(15, 2), + AMD_PINCTRL_FUNC_GRP(15, 3), + AMD_PINCTRL_FUNC_GRP(16, 0), + AMD_PINCTRL_FUNC_GRP(16, 1), + AMD_PINCTRL_FUNC_GRP(16, 2), + AMD_PINCTRL_FUNC_GRP(16, 3), + AMD_PINCTRL_FUNC_GRP(17, 0), + AMD_PINCTRL_FUNC_GRP(17, 1), + AMD_PINCTRL_FUNC_GRP(17, 2), + AMD_PINCTRL_FUNC_GRP(17, 3), + AMD_PINCTRL_FUNC_GRP(18, 0), + AMD_PINCTRL_FUNC_GRP(18, 1), + AMD_PINCTRL_FUNC_GRP(18, 2), + AMD_PINCTRL_FUNC_GRP(18, 3), + AMD_PINCTRL_FUNC_GRP(19, 0), + AMD_PINCTRL_FUNC_GRP(19, 1), + AMD_PINCTRL_FUNC_GRP(19, 2), + AMD_PINCTRL_FUNC_GRP(19, 3), + AMD_PINCTRL_FUNC_GRP(20, 0), + AMD_PINCTRL_FUNC_GRP(20, 1), + AMD_PINCTRL_FUNC_GRP(20, 2), + AMD_PINCTRL_FUNC_GRP(20, 3), + AMD_PINCTRL_FUNC_GRP(21, 0), + AMD_PINCTRL_FUNC_GRP(21, 1), + AMD_PINCTRL_FUNC_GRP(21, 2), + AMD_PINCTRL_FUNC_GRP(21, 3), + AMD_PINCTRL_FUNC_GRP(22, 0), + AMD_PINCTRL_FUNC_GRP(22, 1), + AMD_PINCTRL_FUNC_GRP(22, 2), + AMD_PINCTRL_FUNC_GRP(22, 3), + AMD_PINCTRL_FUNC_GRP(23, 0), + AMD_PINCTRL_FUNC_GRP(23, 1), + AMD_PINCTRL_FUNC_GRP(23, 2), + AMD_PINCTRL_FUNC_GRP(23, 3), + AMD_PINCTRL_FUNC_GRP(24, 0), + AMD_PINCTRL_FUNC_GRP(24, 1), + AMD_PINCTRL_FUNC_GRP(24, 2), + AMD_PINCTRL_FUNC_GRP(24, 3), + AMD_PINCTRL_FUNC_GRP(25, 0), + AMD_PINCTRL_FUNC_GRP(25, 1), + AMD_PINCTRL_FUNC_GRP(25, 2), + AMD_PINCTRL_FUNC_GRP(25, 3), + AMD_PINCTRL_FUNC_GRP(26, 0), + AMD_PINCTRL_FUNC_GRP(26, 1), + AMD_PINCTRL_FUNC_GRP(26, 2), + AMD_PINCTRL_FUNC_GRP(26, 3), + AMD_PINCTRL_FUNC_GRP(27, 0), + AMD_PINCTRL_FUNC_GRP(27, 1), + AMD_PINCTRL_FUNC_GRP(27, 2), + AMD_PINCTRL_FUNC_GRP(27, 3), + AMD_PINCTRL_FUNC_GRP(28, 0), + AMD_PINCTRL_FUNC_GRP(28, 1), + AMD_PINCTRL_FUNC_GRP(28, 2), + AMD_PINCTRL_FUNC_GRP(28, 3), + AMD_PINCTRL_FUNC_GRP(29, 0), + AMD_PINCTRL_FUNC_GRP(29, 1), + AMD_PINCTRL_FUNC_GRP(29, 2), + AMD_PINCTRL_FUNC_GRP(29, 3), + AMD_PINCTRL_FUNC_GRP(30, 0), + AMD_PINCTRL_FUNC_GRP(30, 1), + AMD_PINCTRL_FUNC_GRP(30, 2), + AMD_PINCTRL_FUNC_GRP(30, 3), + AMD_PINCTRL_FUNC_GRP(31, 0), + AMD_PINCTRL_FUNC_GRP(31, 1), + AMD_PINCTRL_FUNC_GRP(31, 2), + AMD_PINCTRL_FUNC_GRP(31, 3), + AMD_PINCTRL_FUNC_GRP(32, 0), + AMD_PINCTRL_FUNC_GRP(32, 1), + AMD_PINCTRL_FUNC_GRP(32, 2), + AMD_PINCTRL_FUNC_GRP(32, 3), + AMD_PINCTRL_FUNC_GRP(33, 0), + AMD_PINCTRL_FUNC_GRP(33, 1), + AMD_PINCTRL_FUNC_GRP(33, 2), + AMD_PINCTRL_FUNC_GRP(33, 3), + AMD_PINCTRL_FUNC_GRP(34, 0), + AMD_PINCTRL_FUNC_GRP(34, 1), + AMD_PINCTRL_FUNC_GRP(34, 2), + AMD_PINCTRL_FUNC_GRP(34, 3), + AMD_PINCTRL_FUNC_GRP(35, 0), + AMD_PINCTRL_FUNC_GRP(35, 1), + AMD_PINCTRL_FUNC_GRP(35, 2), + AMD_PINCTRL_FUNC_GRP(35, 3), + AMD_PINCTRL_FUNC_GRP(36, 0), + AMD_PINCTRL_FUNC_GRP(36, 1), + AMD_PINCTRL_FUNC_GRP(36, 2), + AMD_PINCTRL_FUNC_GRP(36, 3), + AMD_PINCTRL_FUNC_GRP(37, 0), + AMD_PINCTRL_FUNC_GRP(37, 1), + AMD_PINCTRL_FUNC_GRP(37, 2), + AMD_PINCTRL_FUNC_GRP(37, 3), + AMD_PINCTRL_FUNC_GRP(38, 0), + AMD_PINCTRL_FUNC_GRP(38, 1), + AMD_PINCTRL_FUNC_GRP(38, 2), + AMD_PINCTRL_FUNC_GRP(38, 3), + AMD_PINCTRL_FUNC_GRP(39, 0), + AMD_PINCTRL_FUNC_GRP(39, 1), + AMD_PINCTRL_FUNC_GRP(39, 2), + AMD_PINCTRL_FUNC_GRP(39, 3), + AMD_PINCTRL_FUNC_GRP(40, 0), + AMD_PINCTRL_FUNC_GRP(40, 1), + AMD_PINCTRL_FUNC_GRP(40, 2), + AMD_PINCTRL_FUNC_GRP(40, 3), + AMD_PINCTRL_FUNC_GRP(41, 0), + AMD_PINCTRL_FUNC_GRP(41, 1), + AMD_PINCTRL_FUNC_GRP(41, 2), + AMD_PINCTRL_FUNC_GRP(41, 3), + AMD_PINCTRL_FUNC_GRP(42, 0), + AMD_PINCTRL_FUNC_GRP(42, 1), + AMD_PINCTRL_FUNC_GRP(42, 2), + AMD_PINCTRL_FUNC_GRP(42, 3), + AMD_PINCTRL_FUNC_GRP(43, 0), + AMD_PINCTRL_FUNC_GRP(43, 1), + AMD_PINCTRL_FUNC_GRP(43, 2), + AMD_PINCTRL_FUNC_GRP(43, 3), + AMD_PINCTRL_FUNC_GRP(44, 0), + AMD_PINCTRL_FUNC_GRP(44, 1), + AMD_PINCTRL_FUNC_GRP(44, 2), + AMD_PINCTRL_FUNC_GRP(44, 3), + AMD_PINCTRL_FUNC_GRP(45, 0), + AMD_PINCTRL_FUNC_GRP(45, 1), + AMD_PINCTRL_FUNC_GRP(45, 2), + AMD_PINCTRL_FUNC_GRP(45, 3), + AMD_PINCTRL_FUNC_GRP(46, 0), + AMD_PINCTRL_FUNC_GRP(46, 1), + AMD_PINCTRL_FUNC_GRP(46, 2), + AMD_PINCTRL_FUNC_GRP(46, 3), + AMD_PINCTRL_FUNC_GRP(47, 0), + AMD_PINCTRL_FUNC_GRP(47, 1), + AMD_PINCTRL_FUNC_GRP(47, 2), + AMD_PINCTRL_FUNC_GRP(47, 3), + AMD_PINCTRL_FUNC_GRP(48, 0), + AMD_PINCTRL_FUNC_GRP(48, 1), + AMD_PINCTRL_FUNC_GRP(48, 2), + AMD_PINCTRL_FUNC_GRP(48, 3), + AMD_PINCTRL_FUNC_GRP(49, 0), + AMD_PINCTRL_FUNC_GRP(49, 1), + AMD_PINCTRL_FUNC_GRP(49, 2), + AMD_PINCTRL_FUNC_GRP(49, 3), + AMD_PINCTRL_FUNC_GRP(50, 0), + AMD_PINCTRL_FUNC_GRP(50, 1), + AMD_PINCTRL_FUNC_GRP(50, 2), + AMD_PINCTRL_FUNC_GRP(50, 3), + AMD_PINCTRL_FUNC_GRP(51, 0), + AMD_PINCTRL_FUNC_GRP(51, 1), + AMD_PINCTRL_FUNC_GRP(51, 2), + AMD_PINCTRL_FUNC_GRP(51, 3), + AMD_PINCTRL_FUNC_GRP(52, 0), + AMD_PINCTRL_FUNC_GRP(52, 1), + AMD_PINCTRL_FUNC_GRP(52, 2), + AMD_PINCTRL_FUNC_GRP(52, 3), + AMD_PINCTRL_FUNC_GRP(53, 0), + AMD_PINCTRL_FUNC_GRP(53, 1), + AMD_PINCTRL_FUNC_GRP(53, 2), + AMD_PINCTRL_FUNC_GRP(53, 3), + AMD_PINCTRL_FUNC_GRP(54, 0), + AMD_PINCTRL_FUNC_GRP(54, 1), + AMD_PINCTRL_FUNC_GRP(54, 2), + AMD_PINCTRL_FUNC_GRP(54, 3), + AMD_PINCTRL_FUNC_GRP(55, 0), + AMD_PINCTRL_FUNC_GRP(55, 1), + AMD_PINCTRL_FUNC_GRP(55, 2), + AMD_PINCTRL_FUNC_GRP(55, 3), + AMD_PINCTRL_FUNC_GRP(56, 0), + AMD_PINCTRL_FUNC_GRP(56, 1), + AMD_PINCTRL_FUNC_GRP(56, 2), + AMD_PINCTRL_FUNC_GRP(56, 3), + AMD_PINCTRL_FUNC_GRP(57, 0), + AMD_PINCTRL_FUNC_GRP(57, 1), + AMD_PINCTRL_FUNC_GRP(57, 2), + AMD_PINCTRL_FUNC_GRP(57, 3), + AMD_PINCTRL_FUNC_GRP(58, 0), + AMD_PINCTRL_FUNC_GRP(58, 1), + AMD_PINCTRL_FUNC_GRP(58, 2), + AMD_PINCTRL_FUNC_GRP(58, 3), + AMD_PINCTRL_FUNC_GRP(59, 0), + AMD_PINCTRL_FUNC_GRP(59, 1), + AMD_PINCTRL_FUNC_GRP(59, 2), + AMD_PINCTRL_FUNC_GRP(59, 3), + AMD_PINCTRL_FUNC_GRP(60, 0), + AMD_PINCTRL_FUNC_GRP(60, 1), + AMD_PINCTRL_FUNC_GRP(60, 2), + AMD_PINCTRL_FUNC_GRP(60, 3), + AMD_PINCTRL_FUNC_GRP(61, 0), + AMD_PINCTRL_FUNC_GRP(61, 1), + AMD_PINCTRL_FUNC_GRP(61, 2), + AMD_PINCTRL_FUNC_GRP(61, 3), + AMD_PINCTRL_FUNC_GRP(62, 0), + AMD_PINCTRL_FUNC_GRP(62, 1), + AMD_PINCTRL_FUNC_GRP(62, 2), + AMD_PINCTRL_FUNC_GRP(62, 3), + AMD_PINCTRL_FUNC_GRP(64, 0), + AMD_PINCTRL_FUNC_GRP(64, 1), + AMD_PINCTRL_FUNC_GRP(64, 2), + AMD_PINCTRL_FUNC_GRP(64, 3), + AMD_PINCTRL_FUNC_GRP(65, 0), + AMD_PINCTRL_FUNC_GRP(65, 1), + AMD_PINCTRL_FUNC_GRP(65, 2), + AMD_PINCTRL_FUNC_GRP(65, 3), + AMD_PINCTRL_FUNC_GRP(66, 0), + AMD_PINCTRL_FUNC_GRP(66, 1), + AMD_PINCTRL_FUNC_GRP(66, 2), + AMD_PINCTRL_FUNC_GRP(66, 3), + AMD_PINCTRL_FUNC_GRP(67, 0), + AMD_PINCTRL_FUNC_GRP(67, 1), + AMD_PINCTRL_FUNC_GRP(67, 2), + AMD_PINCTRL_FUNC_GRP(67, 3), + AMD_PINCTRL_FUNC_GRP(68, 0), + AMD_PINCTRL_FUNC_GRP(68, 1), + AMD_PINCTRL_FUNC_GRP(68, 2), + AMD_PINCTRL_FUNC_GRP(68, 3), + AMD_PINCTRL_FUNC_GRP(69, 0), + AMD_PINCTRL_FUNC_GRP(69, 1), + AMD_PINCTRL_FUNC_GRP(69, 2), + AMD_PINCTRL_FUNC_GRP(69, 3), + AMD_PINCTRL_FUNC_GRP(70, 0), + AMD_PINCTRL_FUNC_GRP(70, 1), + AMD_PINCTRL_FUNC_GRP(70, 2), + AMD_PINCTRL_FUNC_GRP(70, 3), + AMD_PINCTRL_FUNC_GRP(71, 0), + AMD_PINCTRL_FUNC_GRP(71, 1), + AMD_PINCTRL_FUNC_GRP(71, 2), + AMD_PINCTRL_FUNC_GRP(71, 3), + AMD_PINCTRL_FUNC_GRP(72, 0), + AMD_PINCTRL_FUNC_GRP(72, 1), + AMD_PINCTRL_FUNC_GRP(72, 2), + AMD_PINCTRL_FUNC_GRP(72, 3), + AMD_PINCTRL_FUNC_GRP(73, 0), + AMD_PINCTRL_FUNC_GRP(73, 1), + AMD_PINCTRL_FUNC_GRP(73, 2), + AMD_PINCTRL_FUNC_GRP(73, 3), + AMD_PINCTRL_FUNC_GRP(74, 0), + AMD_PINCTRL_FUNC_GRP(74, 1), + AMD_PINCTRL_FUNC_GRP(74, 2), + AMD_PINCTRL_FUNC_GRP(74, 3), + AMD_PINCTRL_FUNC_GRP(75, 0), + AMD_PINCTRL_FUNC_GRP(75, 1), + AMD_PINCTRL_FUNC_GRP(75, 2), + AMD_PINCTRL_FUNC_GRP(75, 3), + AMD_PINCTRL_FUNC_GRP(76, 0), + AMD_PINCTRL_FUNC_GRP(76, 1), + AMD_PINCTRL_FUNC_GRP(76, 2), + AMD_PINCTRL_FUNC_GRP(76, 3), + AMD_PINCTRL_FUNC_GRP(77, 0), + AMD_PINCTRL_FUNC_GRP(77, 1), + AMD_PINCTRL_FUNC_GRP(77, 2), + AMD_PINCTRL_FUNC_GRP(77, 3), + AMD_PINCTRL_FUNC_GRP(78, 0), + AMD_PINCTRL_FUNC_GRP(78, 1), + AMD_PINCTRL_FUNC_GRP(78, 2), + AMD_PINCTRL_FUNC_GRP(78, 3), + AMD_PINCTRL_FUNC_GRP(79, 0), + AMD_PINCTRL_FUNC_GRP(79, 1), + AMD_PINCTRL_FUNC_GRP(79, 2), + AMD_PINCTRL_FUNC_GRP(79, 3), + AMD_PINCTRL_FUNC_GRP(80, 0), + AMD_PINCTRL_FUNC_GRP(80, 1), + AMD_PINCTRL_FUNC_GRP(80, 2), + AMD_PINCTRL_FUNC_GRP(80, 3), + AMD_PINCTRL_FUNC_GRP(81, 0), + AMD_PINCTRL_FUNC_GRP(81, 1), + AMD_PINCTRL_FUNC_GRP(81, 2), + AMD_PINCTRL_FUNC_GRP(81, 3), + AMD_PINCTRL_FUNC_GRP(82, 0), + AMD_PINCTRL_FUNC_GRP(82, 1), + AMD_PINCTRL_FUNC_GRP(82, 2), + AMD_PINCTRL_FUNC_GRP(82, 3), + AMD_PINCTRL_FUNC_GRP(83, 0), + AMD_PINCTRL_FUNC_GRP(83, 1), + AMD_PINCTRL_FUNC_GRP(83, 2), + AMD_PINCTRL_FUNC_GRP(83, 3), + AMD_PINCTRL_FUNC_GRP(84, 0), + AMD_PINCTRL_FUNC_GRP(84, 1), + AMD_PINCTRL_FUNC_GRP(84, 2), + AMD_PINCTRL_FUNC_GRP(84, 3), + AMD_PINCTRL_FUNC_GRP(85, 0), + AMD_PINCTRL_FUNC_GRP(85, 1), + AMD_PINCTRL_FUNC_GRP(85, 2), + AMD_PINCTRL_FUNC_GRP(85, 3), + AMD_PINCTRL_FUNC_GRP(86, 0), + AMD_PINCTRL_FUNC_GRP(86, 1), + AMD_PINCTRL_FUNC_GRP(86, 2), + AMD_PINCTRL_FUNC_GRP(86, 3), + AMD_PINCTRL_FUNC_GRP(87, 0), + AMD_PINCTRL_FUNC_GRP(87, 1), + AMD_PINCTRL_FUNC_GRP(87, 2), + AMD_PINCTRL_FUNC_GRP(87, 3), + AMD_PINCTRL_FUNC_GRP(88, 0), + AMD_PINCTRL_FUNC_GRP(88, 1), + AMD_PINCTRL_FUNC_GRP(88, 2), + AMD_PINCTRL_FUNC_GRP(88, 3), + AMD_PINCTRL_FUNC_GRP(89, 0), + AMD_PINCTRL_FUNC_GRP(89, 1), + AMD_PINCTRL_FUNC_GRP(89, 2), + AMD_PINCTRL_FUNC_GRP(89, 3), + AMD_PINCTRL_FUNC_GRP(90, 0), + AMD_PINCTRL_FUNC_GRP(90, 1), + AMD_PINCTRL_FUNC_GRP(90, 2), + AMD_PINCTRL_FUNC_GRP(90, 3), + AMD_PINCTRL_FUNC_GRP(91, 0), + AMD_PINCTRL_FUNC_GRP(91, 1), + AMD_PINCTRL_FUNC_GRP(91, 2), + AMD_PINCTRL_FUNC_GRP(91, 3), + AMD_PINCTRL_FUNC_GRP(92, 0), + AMD_PINCTRL_FUNC_GRP(92, 1), + AMD_PINCTRL_FUNC_GRP(92, 2), + AMD_PINCTRL_FUNC_GRP(92, 3), + AMD_PINCTRL_FUNC_GRP(93, 0), + AMD_PINCTRL_FUNC_GRP(93, 1), + AMD_PINCTRL_FUNC_GRP(93, 2), + AMD_PINCTRL_FUNC_GRP(93, 3), + AMD_PINCTRL_FUNC_GRP(94, 0), + AMD_PINCTRL_FUNC_GRP(94, 1), + AMD_PINCTRL_FUNC_GRP(94, 2), + AMD_PINCTRL_FUNC_GRP(94, 3), + AMD_PINCTRL_FUNC_GRP(95, 0), + AMD_PINCTRL_FUNC_GRP(95, 1), + AMD_PINCTRL_FUNC_GRP(95, 2), + AMD_PINCTRL_FUNC_GRP(95, 3), + AMD_PINCTRL_FUNC_GRP(96, 0), + AMD_PINCTRL_FUNC_GRP(96, 1), + AMD_PINCTRL_FUNC_GRP(96, 2), + AMD_PINCTRL_FUNC_GRP(96, 3), + AMD_PINCTRL_FUNC_GRP(97, 0), + AMD_PINCTRL_FUNC_GRP(97, 1), + AMD_PINCTRL_FUNC_GRP(97, 2), + AMD_PINCTRL_FUNC_GRP(97, 3), + AMD_PINCTRL_FUNC_GRP(98, 0), + AMD_PINCTRL_FUNC_GRP(98, 1), + AMD_PINCTRL_FUNC_GRP(98, 2), + AMD_PINCTRL_FUNC_GRP(98, 3), + AMD_PINCTRL_FUNC_GRP(99, 0), + AMD_PINCTRL_FUNC_GRP(99, 1), + AMD_PINCTRL_FUNC_GRP(99, 2), + AMD_PINCTRL_FUNC_GRP(99, 3), + AMD_PINCTRL_FUNC_GRP(100, 0), + AMD_PINCTRL_FUNC_GRP(100, 1), + AMD_PINCTRL_FUNC_GRP(100, 2), + AMD_PINCTRL_FUNC_GRP(100, 3), + AMD_PINCTRL_FUNC_GRP(101, 0), + AMD_PINCTRL_FUNC_GRP(101, 1), + AMD_PINCTRL_FUNC_GRP(101, 2), + AMD_PINCTRL_FUNC_GRP(101, 3), + AMD_PINCTRL_FUNC_GRP(102, 0), + AMD_PINCTRL_FUNC_GRP(102, 1), + AMD_PINCTRL_FUNC_GRP(102, 2), + AMD_PINCTRL_FUNC_GRP(102, 3), + AMD_PINCTRL_FUNC_GRP(103, 0), + AMD_PINCTRL_FUNC_GRP(103, 1), + AMD_PINCTRL_FUNC_GRP(103, 2), + AMD_PINCTRL_FUNC_GRP(103, 3), + AMD_PINCTRL_FUNC_GRP(104, 0), + AMD_PINCTRL_FUNC_GRP(104, 1), + AMD_PINCTRL_FUNC_GRP(104, 2), + AMD_PINCTRL_FUNC_GRP(104, 3), + AMD_PINCTRL_FUNC_GRP(105, 0), + AMD_PINCTRL_FUNC_GRP(105, 1), + AMD_PINCTRL_FUNC_GRP(105, 2), + AMD_PINCTRL_FUNC_GRP(105, 3), + AMD_PINCTRL_FUNC_GRP(106, 0), + AMD_PINCTRL_FUNC_GRP(106, 1), + AMD_PINCTRL_FUNC_GRP(106, 2), + AMD_PINCTRL_FUNC_GRP(106, 3), + AMD_PINCTRL_FUNC_GRP(107, 0), + AMD_PINCTRL_FUNC_GRP(107, 1), + AMD_PINCTRL_FUNC_GRP(107, 2), + AMD_PINCTRL_FUNC_GRP(107, 3), + AMD_PINCTRL_FUNC_GRP(108, 0), + AMD_PINCTRL_FUNC_GRP(108, 1), + AMD_PINCTRL_FUNC_GRP(108, 2), + AMD_PINCTRL_FUNC_GRP(108, 3), + AMD_PINCTRL_FUNC_GRP(109, 0), + AMD_PINCTRL_FUNC_GRP(109, 1), + AMD_PINCTRL_FUNC_GRP(109, 2), + AMD_PINCTRL_FUNC_GRP(109, 3), + AMD_PINCTRL_FUNC_GRP(110, 0), + AMD_PINCTRL_FUNC_GRP(110, 1), + AMD_PINCTRL_FUNC_GRP(110, 2), + AMD_PINCTRL_FUNC_GRP(110, 3), + AMD_PINCTRL_FUNC_GRP(111, 0), + AMD_PINCTRL_FUNC_GRP(111, 1), + AMD_PINCTRL_FUNC_GRP(111, 2), + AMD_PINCTRL_FUNC_GRP(111, 3), + AMD_PINCTRL_FUNC_GRP(112, 0), + AMD_PINCTRL_FUNC_GRP(112, 1), + AMD_PINCTRL_FUNC_GRP(112, 2), + AMD_PINCTRL_FUNC_GRP(112, 3), + AMD_PINCTRL_FUNC_GRP(113, 0), + AMD_PINCTRL_FUNC_GRP(113, 1), + AMD_PINCTRL_FUNC_GRP(113, 2), + AMD_PINCTRL_FUNC_GRP(113, 3), + AMD_PINCTRL_FUNC_GRP(114, 0), + AMD_PINCTRL_FUNC_GRP(114, 1), + AMD_PINCTRL_FUNC_GRP(114, 2), + AMD_PINCTRL_FUNC_GRP(114, 3), + AMD_PINCTRL_FUNC_GRP(115, 0), + AMD_PINCTRL_FUNC_GRP(115, 1), + AMD_PINCTRL_FUNC_GRP(115, 2), + AMD_PINCTRL_FUNC_GRP(115, 3), + AMD_PINCTRL_FUNC_GRP(116, 0), + AMD_PINCTRL_FUNC_GRP(116, 1), + AMD_PINCTRL_FUNC_GRP(116, 2), + AMD_PINCTRL_FUNC_GRP(116, 3), + AMD_PINCTRL_FUNC_GRP(117, 0), + AMD_PINCTRL_FUNC_GRP(117, 1), + AMD_PINCTRL_FUNC_GRP(117, 2), + AMD_PINCTRL_FUNC_GRP(117, 3), + AMD_PINCTRL_FUNC_GRP(118, 0), + AMD_PINCTRL_FUNC_GRP(118, 1), + AMD_PINCTRL_FUNC_GRP(118, 2), + AMD_PINCTRL_FUNC_GRP(118, 3), + AMD_PINCTRL_FUNC_GRP(119, 0), + AMD_PINCTRL_FUNC_GRP(119, 1), + AMD_PINCTRL_FUNC_GRP(119, 2), + AMD_PINCTRL_FUNC_GRP(119, 3), + AMD_PINCTRL_FUNC_GRP(120, 0), + AMD_PINCTRL_FUNC_GRP(120, 1), + AMD_PINCTRL_FUNC_GRP(120, 2), + AMD_PINCTRL_FUNC_GRP(120, 3), + AMD_PINCTRL_FUNC_GRP(121, 0), + AMD_PINCTRL_FUNC_GRP(121, 1), + AMD_PINCTRL_FUNC_GRP(121, 2), + AMD_PINCTRL_FUNC_GRP(121, 3), + AMD_PINCTRL_FUNC_GRP(122, 0), + AMD_PINCTRL_FUNC_GRP(122, 1), + AMD_PINCTRL_FUNC_GRP(122, 2), + AMD_PINCTRL_FUNC_GRP(122, 3), + AMD_PINCTRL_FUNC_GRP(123, 0), + AMD_PINCTRL_FUNC_GRP(123, 1), + AMD_PINCTRL_FUNC_GRP(123, 2), + AMD_PINCTRL_FUNC_GRP(123, 3), + AMD_PINCTRL_FUNC_GRP(124, 0), + AMD_PINCTRL_FUNC_GRP(124, 1), + AMD_PINCTRL_FUNC_GRP(124, 2), + AMD_PINCTRL_FUNC_GRP(124, 3), + AMD_PINCTRL_FUNC_GRP(125, 0), + AMD_PINCTRL_FUNC_GRP(125, 1), + AMD_PINCTRL_FUNC_GRP(125, 2), + AMD_PINCTRL_FUNC_GRP(125, 3), + AMD_PINCTRL_FUNC_GRP(126, 0), + AMD_PINCTRL_FUNC_GRP(126, 1), + AMD_PINCTRL_FUNC_GRP(126, 2), + AMD_PINCTRL_FUNC_GRP(126, 3), + AMD_PINCTRL_FUNC_GRP(127, 0), + AMD_PINCTRL_FUNC_GRP(127, 1), + AMD_PINCTRL_FUNC_GRP(127, 2), + AMD_PINCTRL_FUNC_GRP(127, 3), + AMD_PINCTRL_FUNC_GRP(128, 0), + AMD_PINCTRL_FUNC_GRP(128, 1), + AMD_PINCTRL_FUNC_GRP(128, 2), + AMD_PINCTRL_FUNC_GRP(128, 3), + AMD_PINCTRL_FUNC_GRP(129, 0), + AMD_PINCTRL_FUNC_GRP(129, 1), + AMD_PINCTRL_FUNC_GRP(129, 2), + AMD_PINCTRL_FUNC_GRP(129, 3), + AMD_PINCTRL_FUNC_GRP(130, 0), + AMD_PINCTRL_FUNC_GRP(130, 1), + AMD_PINCTRL_FUNC_GRP(130, 2), + AMD_PINCTRL_FUNC_GRP(130, 3), + AMD_PINCTRL_FUNC_GRP(131, 0), + AMD_PINCTRL_FUNC_GRP(131, 1), + AMD_PINCTRL_FUNC_GRP(131, 2), + AMD_PINCTRL_FUNC_GRP(131, 3), + AMD_PINCTRL_FUNC_GRP(132, 0), + AMD_PINCTRL_FUNC_GRP(132, 1), + AMD_PINCTRL_FUNC_GRP(132, 2), + AMD_PINCTRL_FUNC_GRP(132, 3), + AMD_PINCTRL_FUNC_GRP(133, 0), + AMD_PINCTRL_FUNC_GRP(133, 1), + AMD_PINCTRL_FUNC_GRP(133, 2), + AMD_PINCTRL_FUNC_GRP(133, 3), + AMD_PINCTRL_FUNC_GRP(134, 0), + AMD_PINCTRL_FUNC_GRP(134, 1), + AMD_PINCTRL_FUNC_GRP(134, 2), + AMD_PINCTRL_FUNC_GRP(134, 3), + AMD_PINCTRL_FUNC_GRP(135, 0), + AMD_PINCTRL_FUNC_GRP(135, 1), + AMD_PINCTRL_FUNC_GRP(135, 2), + AMD_PINCTRL_FUNC_GRP(135, 3), + AMD_PINCTRL_FUNC_GRP(136, 0), + AMD_PINCTRL_FUNC_GRP(136, 1), + AMD_PINCTRL_FUNC_GRP(136, 2), + AMD_PINCTRL_FUNC_GRP(136, 3), + AMD_PINCTRL_FUNC_GRP(137, 0), + AMD_PINCTRL_FUNC_GRP(137, 1), + AMD_PINCTRL_FUNC_GRP(137, 2), + AMD_PINCTRL_FUNC_GRP(137, 3), + AMD_PINCTRL_FUNC_GRP(138, 0), + AMD_PINCTRL_FUNC_GRP(138, 1), + AMD_PINCTRL_FUNC_GRP(138, 2), + AMD_PINCTRL_FUNC_GRP(138, 3), + AMD_PINCTRL_FUNC_GRP(139, 0), + AMD_PINCTRL_FUNC_GRP(139, 1), + AMD_PINCTRL_FUNC_GRP(139, 2), + AMD_PINCTRL_FUNC_GRP(139, 3), + AMD_PINCTRL_FUNC_GRP(140, 0), + AMD_PINCTRL_FUNC_GRP(140, 1), + AMD_PINCTRL_FUNC_GRP(140, 2), + AMD_PINCTRL_FUNC_GRP(140, 3), + AMD_PINCTRL_FUNC_GRP(141, 0), + AMD_PINCTRL_FUNC_GRP(141, 1), + AMD_PINCTRL_FUNC_GRP(141, 2), + AMD_PINCTRL_FUNC_GRP(141, 3), + AMD_PINCTRL_FUNC_GRP(142, 0), + AMD_PINCTRL_FUNC_GRP(142, 1), + AMD_PINCTRL_FUNC_GRP(142, 2), + AMD_PINCTRL_FUNC_GRP(142, 3), + AMD_PINCTRL_FUNC_GRP(143, 0), + AMD_PINCTRL_FUNC_GRP(143, 1), + AMD_PINCTRL_FUNC_GRP(143, 2), + AMD_PINCTRL_FUNC_GRP(143, 3), + AMD_PINCTRL_FUNC_GRP(144, 0), + AMD_PINCTRL_FUNC_GRP(144, 1), + AMD_PINCTRL_FUNC_GRP(144, 2), + AMD_PINCTRL_FUNC_GRP(144, 3), + + PINCTRL_PINGROUP("i2c0", AMD_PINS(145, 146), 2), + PINCTRL_PINGROUP("i2c1", AMD_PINS(147, 148), 2), + PINCTRL_PINGROUP("i2c2", AMD_PINS(113, 114), 2), + PINCTRL_PINGROUP("i2c3", AMD_PINS(19, 20), 2), + PINCTRL_PINGROUP("uart0", AMD_PINS(135, 136, 137, 138, 139), 5), + PINCTRL_PINGROUP("uart1", AMD_PINS(140, 141, 142, 143, 144), 5), +}; -static const unsigned uart0_pins[] = {135, 136, 137, 138, 139}; -static const unsigned uart1_pins[] = {140, 141, 142, 143, 144}; +#define AMD_PMUX_FUNC(_number) { \ + .name = "iomux_gpio_"#_number, \ + .groups = { \ + "IMX_F0_GPIO"#_number, "IMX_F1_GPIO"#_number, \ + "IMX_F2_GPIO"#_number, "IMX_F3_GPIO"#_number, \ + }, \ + .index = _number, \ + .ngroups = NSELECTS, \ +} -static const struct amd_pingroup kerncz_groups[] = { - { - .name = "i2c0", - .pins = i2c0_pins, - .npins = 2, - }, - { - .name = "i2c1", - .pins = i2c1_pins, - .npins = 2, - }, - { - .name = "i2c2", - .pins = i2c2_pins, - .npins = 2, - }, - { - .name = "i2c3", - .pins = i2c3_pins, - .npins = 2, - }, - { - .name = "uart0", - .pins = uart0_pins, - .npins = 5, - }, - { - .name = "uart1", - .pins = uart1_pins, - .npins = 5, - }, +static const struct amd_function pmx_functions[] = { + AMD_PMUX_FUNC(0), + AMD_PMUX_FUNC(1), + AMD_PMUX_FUNC(2), + AMD_PMUX_FUNC(3), + AMD_PMUX_FUNC(4), + AMD_PMUX_FUNC(5), + AMD_PMUX_FUNC(6), + AMD_PMUX_FUNC(7), + AMD_PMUX_FUNC(8), + AMD_PMUX_FUNC(9), + AMD_PMUX_FUNC(10), + AMD_PMUX_FUNC(11), + AMD_PMUX_FUNC(12), + AMD_PMUX_FUNC(13), + AMD_PMUX_FUNC(14), + AMD_PMUX_FUNC(15), + AMD_PMUX_FUNC(16), + AMD_PMUX_FUNC(17), + AMD_PMUX_FUNC(18), + AMD_PMUX_FUNC(19), + AMD_PMUX_FUNC(20), + AMD_PMUX_FUNC(21), + AMD_PMUX_FUNC(22), + AMD_PMUX_FUNC(23), + AMD_PMUX_FUNC(24), + AMD_PMUX_FUNC(25), + AMD_PMUX_FUNC(26), + AMD_PMUX_FUNC(27), + AMD_PMUX_FUNC(28), + AMD_PMUX_FUNC(29), + AMD_PMUX_FUNC(30), + AMD_PMUX_FUNC(31), + AMD_PMUX_FUNC(32), + AMD_PMUX_FUNC(33), + AMD_PMUX_FUNC(34), + AMD_PMUX_FUNC(35), + AMD_PMUX_FUNC(36), + AMD_PMUX_FUNC(37), + AMD_PMUX_FUNC(38), + AMD_PMUX_FUNC(39), + AMD_PMUX_FUNC(40), + AMD_PMUX_FUNC(41), + AMD_PMUX_FUNC(42), + AMD_PMUX_FUNC(43), + AMD_PMUX_FUNC(44), + AMD_PMUX_FUNC(45), + AMD_PMUX_FUNC(46), + AMD_PMUX_FUNC(47), + AMD_PMUX_FUNC(48), + AMD_PMUX_FUNC(49), + AMD_PMUX_FUNC(50), + AMD_PMUX_FUNC(51), + AMD_PMUX_FUNC(52), + AMD_PMUX_FUNC(53), + AMD_PMUX_FUNC(54), + AMD_PMUX_FUNC(55), + AMD_PMUX_FUNC(56), + AMD_PMUX_FUNC(57), + AMD_PMUX_FUNC(58), + AMD_PMUX_FUNC(59), + AMD_PMUX_FUNC(60), + AMD_PMUX_FUNC(61), + AMD_PMUX_FUNC(62), + AMD_PMUX_FUNC(64), + AMD_PMUX_FUNC(65), + AMD_PMUX_FUNC(66), + AMD_PMUX_FUNC(67), + AMD_PMUX_FUNC(68), + AMD_PMUX_FUNC(69), + AMD_PMUX_FUNC(70), + AMD_PMUX_FUNC(71), + AMD_PMUX_FUNC(72), + AMD_PMUX_FUNC(73), + AMD_PMUX_FUNC(74), + AMD_PMUX_FUNC(75), + AMD_PMUX_FUNC(76), + AMD_PMUX_FUNC(77), + AMD_PMUX_FUNC(78), + AMD_PMUX_FUNC(79), + AMD_PMUX_FUNC(80), + AMD_PMUX_FUNC(81), + AMD_PMUX_FUNC(82), + AMD_PMUX_FUNC(83), + AMD_PMUX_FUNC(84), + AMD_PMUX_FUNC(85), + AMD_PMUX_FUNC(86), + AMD_PMUX_FUNC(87), + AMD_PMUX_FUNC(88), + AMD_PMUX_FUNC(89), + AMD_PMUX_FUNC(90), + AMD_PMUX_FUNC(91), + AMD_PMUX_FUNC(92), + AMD_PMUX_FUNC(93), + AMD_PMUX_FUNC(94), + AMD_PMUX_FUNC(95), + AMD_PMUX_FUNC(96), + AMD_PMUX_FUNC(97), + AMD_PMUX_FUNC(98), + AMD_PMUX_FUNC(99), + AMD_PMUX_FUNC(100), + AMD_PMUX_FUNC(101), + AMD_PMUX_FUNC(102), + AMD_PMUX_FUNC(103), + AMD_PMUX_FUNC(104), + AMD_PMUX_FUNC(105), + AMD_PMUX_FUNC(106), + AMD_PMUX_FUNC(107), + AMD_PMUX_FUNC(108), + AMD_PMUX_FUNC(109), + AMD_PMUX_FUNC(110), + AMD_PMUX_FUNC(111), + AMD_PMUX_FUNC(112), + AMD_PMUX_FUNC(113), + AMD_PMUX_FUNC(114), + AMD_PMUX_FUNC(115), + AMD_PMUX_FUNC(116), + AMD_PMUX_FUNC(117), + AMD_PMUX_FUNC(118), + AMD_PMUX_FUNC(119), + AMD_PMUX_FUNC(120), + AMD_PMUX_FUNC(121), + AMD_PMUX_FUNC(122), + AMD_PMUX_FUNC(123), + AMD_PMUX_FUNC(124), + AMD_PMUX_FUNC(125), + AMD_PMUX_FUNC(126), + AMD_PMUX_FUNC(127), + AMD_PMUX_FUNC(128), + AMD_PMUX_FUNC(129), + AMD_PMUX_FUNC(130), + AMD_PMUX_FUNC(131), + AMD_PMUX_FUNC(132), + AMD_PMUX_FUNC(133), + AMD_PMUX_FUNC(134), + AMD_PMUX_FUNC(135), + AMD_PMUX_FUNC(136), + AMD_PMUX_FUNC(137), + AMD_PMUX_FUNC(138), + AMD_PMUX_FUNC(139), + AMD_PMUX_FUNC(140), + AMD_PMUX_FUNC(141), + AMD_PMUX_FUNC(142), + AMD_PMUX_FUNC(143), + AMD_PMUX_FUNC(144), }; #endif diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index 517f2a6330ad..82b921fd630d 100644 --- a/drivers/pinctrl/pinctrl-at91-pio4.c +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -237,8 +237,6 @@ static void atmel_gpio_irq_unmask(struct irq_data *d) BIT(pin->line)); } -#ifdef CONFIG_PM_SLEEP - static int atmel_gpio_irq_set_wake(struct irq_data *d, unsigned int on) { struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d); @@ -255,9 +253,6 @@ static int atmel_gpio_irq_set_wake(struct irq_data *d, unsigned int on) return 0; } -#else -#define atmel_gpio_irq_set_wake NULL -#endif /* CONFIG_PM_SLEEP */ static struct irq_chip atmel_gpio_irq_chip = { .name = "GPIO", @@ -265,7 +260,7 @@ static struct irq_chip atmel_gpio_irq_chip = { .irq_mask = atmel_gpio_irq_mask, .irq_unmask = atmel_gpio_irq_unmask, .irq_set_type = atmel_gpio_irq_set_type, - .irq_set_wake = atmel_gpio_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(atmel_gpio_irq_set_wake), }; static int atmel_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index d91a010e65f5..5634fa063ebf 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1615,8 +1615,6 @@ static void gpio_irq_ack(struct irq_data *d) /* the interrupt is already cleared before by reading ISR */ } -#ifdef CONFIG_PM - static u32 wakeups[MAX_GPIO_BANKS]; static u32 backups[MAX_GPIO_BANKS]; @@ -1683,10 +1681,6 @@ void at91_pinctrl_gpio_resume(void) } } -#else -#define gpio_irq_set_wake NULL -#endif /* CONFIG_PM */ - static void gpio_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); @@ -1741,14 +1735,14 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev, gpio_irqchip->irq_disable = gpio_irq_mask; gpio_irqchip->irq_mask = gpio_irq_mask; gpio_irqchip->irq_unmask = gpio_irq_unmask; - gpio_irqchip->irq_set_wake = gpio_irq_set_wake; + gpio_irqchip->irq_set_wake = pm_ptr(gpio_irq_set_wake); gpio_irqchip->irq_set_type = at91_gpio->ops->irq_type; /* Disable irqs of this PIO controller */ writel_relaxed(~0, at91_gpio->regbase + PIO_IDR); /* - * Let the generic code handle this edge IRQ, the the chained + * Let the generic code handle this edge IRQ, the chained * handler will perform the actual work of handling the parent * interrupt. */ diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 207cbae3a7bf..7ab20ac15391 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -73,7 +73,7 @@ static const struct pinctrl_pin_desc axp209_pins[] = { PINCTRL_PIN(2, "GPIO2"), }; -static const struct pinctrl_pin_desc axp813_pins[] = { +static const struct pinctrl_pin_desc axp22x_pins[] = { PINCTRL_PIN(0, "GPIO0"), PINCTRL_PIN(1, "GPIO1"), }; @@ -87,9 +87,16 @@ static const struct axp20x_pctrl_desc axp20x_data = { .adc_mux = AXP20X_MUX_ADC, }; +static const struct axp20x_pctrl_desc axp22x_data = { + .pins = axp22x_pins, + .npins = ARRAY_SIZE(axp22x_pins), + .ldo_mask = BIT(0) | BIT(1), + .gpio_status_offset = 0, +}; + static const struct axp20x_pctrl_desc axp813_data = { - .pins = axp813_pins, - .npins = ARRAY_SIZE(axp813_pins), + .pins = axp22x_pins, + .npins = ARRAY_SIZE(axp22x_pins), .ldo_mask = BIT(0) | BIT(1), .adc_mask = BIT(0), .gpio_status_offset = 0, @@ -388,6 +395,7 @@ static int axp20x_build_funcs_groups(struct platform_device *pdev) static const struct of_device_id axp20x_pctl_match[] = { { .compatible = "x-powers,axp209-gpio", .data = &axp20x_data, }, + { .compatible = "x-powers,axp221-gpio", .data = &axp22x_data, }, { .compatible = "x-powers,axp813-gpio", .data = &axp813_data, }, { } }; diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 1ca11616db74..3a9ee9c8af11 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -21,6 +21,7 @@ #include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/seq_file.h> #include <linux/slab.h> #include "core.h" @@ -135,7 +136,6 @@ struct ingenic_pinctrl { struct ingenic_gpio_chip { struct ingenic_pinctrl *jzpc; struct gpio_chip gc; - struct irq_chip irq_chip; unsigned int irq, reg_base; }; @@ -3393,7 +3393,7 @@ static void ingenic_gpio_irq_mask(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; + irq_hw_number_t irq = irqd_to_hwirq(irqd); if (is_soc_or_above(jzgc->jzpc, ID_JZ4740)) ingenic_gpio_set_bit(jzgc, GPIO_MSK, irq, true); @@ -3405,7 +3405,7 @@ static void ingenic_gpio_irq_unmask(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; + irq_hw_number_t irq = irqd_to_hwirq(irqd); if (is_soc_or_above(jzgc->jzpc, ID_JZ4740)) ingenic_gpio_set_bit(jzgc, GPIO_MSK, irq, false); @@ -3417,7 +3417,9 @@ static void ingenic_gpio_irq_enable(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; + irq_hw_number_t irq = irqd_to_hwirq(irqd); + + gpiochip_enable_irq(gc, irq); if (is_soc_or_above(jzgc->jzpc, ID_JZ4770)) ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); @@ -3433,7 +3435,7 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; + irq_hw_number_t irq = irqd_to_hwirq(irqd); ingenic_gpio_irq_mask(irqd); @@ -3443,13 +3445,15 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd) ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); else ingenic_gpio_set_bit(jzgc, JZ4730_GPIO_GPIER, irq, false); + + gpiochip_disable_irq(gc, irq); } static void ingenic_gpio_irq_ack(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; + irq_hw_number_t irq = irqd_to_hwirq(irqd); bool high; if ((irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) && @@ -3477,6 +3481,7 @@ static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + irq_hw_number_t irq = irqd_to_hwirq(irqd); switch (type) { case IRQ_TYPE_EDGE_BOTH: @@ -3498,12 +3503,12 @@ static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) * best we can do is to set up a single-edge interrupt and then * switch to the opposing edge when ACKing the interrupt. */ - bool high = ingenic_gpio_get_value(jzgc, irqd->hwirq); + bool high = ingenic_gpio_get_value(jzgc, irq); type = high ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH; } - irq_set_type(jzgc, irqd->hwirq, type); + irq_set_type(jzgc, irq, type); return 0; } @@ -3668,22 +3673,45 @@ static const struct pinctrl_ops ingenic_pctlops = { static int ingenic_gpio_irq_request(struct irq_data *data) { struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); + irq_hw_number_t irq = irqd_to_hwirq(data); int ret; - ret = ingenic_gpio_direction_input(gpio_chip, data->hwirq); + ret = ingenic_gpio_direction_input(gpio_chip, irq); if (ret) return ret; - return gpiochip_reqres_irq(gpio_chip, data->hwirq); + return gpiochip_reqres_irq(gpio_chip, irq); } static void ingenic_gpio_irq_release(struct irq_data *data) { struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); + irq_hw_number_t irq = irqd_to_hwirq(data); + + return gpiochip_relres_irq(gpio_chip, irq); +} + +static void ingenic_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) +{ + struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); - return gpiochip_relres_irq(gpio_chip, data->hwirq); + seq_printf(p, "%s", gpio_chip->label); } +static const struct irq_chip ingenic_gpio_irqchip = { + .irq_enable = ingenic_gpio_irq_enable, + .irq_disable = ingenic_gpio_irq_disable, + .irq_unmask = ingenic_gpio_irq_unmask, + .irq_mask = ingenic_gpio_irq_mask, + .irq_ack = ingenic_gpio_irq_ack, + .irq_set_type = ingenic_gpio_irq_set_type, + .irq_set_wake = ingenic_gpio_irq_set_wake, + .irq_request_resources = ingenic_gpio_irq_request, + .irq_release_resources = ingenic_gpio_irq_release, + .irq_print_chip = ingenic_gpio_irq_print_chip, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, +}; + static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc, int pin, int func) { @@ -4172,20 +4200,8 @@ static int __init ingenic_gpio_probe(struct ingenic_pinctrl *jzpc, if (!jzgc->irq) return -EINVAL; - jzgc->irq_chip.name = jzgc->gc.label; - jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable; - jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable; - jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask; - jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask; - jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack; - jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type; - jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake; - jzgc->irq_chip.irq_request_resources = ingenic_gpio_irq_request; - jzgc->irq_chip.irq_release_resources = ingenic_gpio_irq_release; - jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; - girq = &jzgc->gc.irq; - girq->chip = &jzgc->irq_chip; + gpio_irq_chip_set_chip(girq, &ingenic_gpio_irqchip); girq->parent_handler = ingenic_gpio_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c index 771dd1f4fbe0..c5fd154990c8 100644 --- a/drivers/pinctrl/pinctrl-ocelot.c +++ b/drivers/pinctrl/pinctrl-ocelot.c @@ -1944,6 +1944,7 @@ static const struct of_device_id ocelot_pinctrl_of_match[] = { { .compatible = "microchip,lan966x-pinctrl", .data = &lan966x_desc }, {}, }; +MODULE_DEVICE_TABLE(of, ocelot_pinctrl_of_match); static struct regmap *ocelot_pinctrl_create_pincfg(struct platform_device *pdev, const struct ocelot_pinctrl *info) @@ -2050,4 +2051,5 @@ static struct platform_driver ocelot_pinctrl_driver = { }, .probe = ocelot_pinctrl_probe, }; -builtin_platform_driver(ocelot_pinctrl_driver); +module_platform_driver(ocelot_pinctrl_driver); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/pinctrl/pinctrl-starfive.c b/drivers/pinctrl/pinctrl-starfive.c index 2a86c1035cc8..3eb40e230d98 100644 --- a/drivers/pinctrl/pinctrl-starfive.c +++ b/drivers/pinctrl/pinctrl-starfive.c @@ -207,6 +207,7 @@ struct starfive_pinctrl { void __iomem *base; void __iomem *padctl; struct pinctrl_dev *pctl; + struct mutex mutex; /* serialize adding groups and functions */ }; static inline unsigned int starfive_pin_to_gpio(const struct starfive_pinctrl *sfp, @@ -522,6 +523,7 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev, nmaps = 0; ngroups = 0; + mutex_lock(&sfp->mutex); for_each_child_of_node(np, child) { int npins; int i; @@ -615,12 +617,14 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev, *maps = map; *num_maps = nmaps; + mutex_unlock(&sfp->mutex); return 0; put_child: of_node_put(child); free_map: pinctrl_utils_free_map(pctldev, map, nmaps); + mutex_unlock(&sfp->mutex); return ret; } @@ -1267,6 +1271,7 @@ static int starfive_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sfp); sfp->gc.parent = dev; raw_spin_lock_init(&sfp->lock); + mutex_init(&sfp->mutex); ret = devm_pinctrl_register_and_init(dev, &starfive_desc, sfp, &sfp->pctl); if (ret) diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c index e14012209992..7d2fbf8a02cd 100644 --- a/drivers/pinctrl/pinctrl-zynqmp.c +++ b/drivers/pinctrl/pinctrl-zynqmp.c @@ -163,6 +163,8 @@ static const char *zynqmp_pmux_get_function_name(struct pinctrl_dev *pctldev, * @num_groups: Number of function groups. * * Get function's group count and group names. + * + * Return: 0 */ static int zynqmp_pmux_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, @@ -410,6 +412,10 @@ static int zynqmp_pinconf_cfg_set(struct pinctrl_dev *pctldev, break; case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + param = PM_PINCTRL_CONFIG_TRI_STATE; + arg = PM_PINCTRL_TRI_STATE_ENABLE; + ret = zynqmp_pm_pinctrl_set_config(pin, param, arg); + break; case PIN_CONFIG_MODE_LOW_POWER: /* * These cases are mentioned in dts but configurable @@ -418,6 +424,11 @@ static int zynqmp_pinconf_cfg_set(struct pinctrl_dev *pctldev, */ ret = 0; break; + case PIN_CONFIG_OUTPUT_ENABLE: + param = PM_PINCTRL_CONFIG_TRI_STATE; + arg = PM_PINCTRL_TRI_STATE_DISABLE; + ret = zynqmp_pm_pinctrl_set_config(pin, param, arg); + break; default: dev_warn(pctldev->dev, "unsupported configuration parameter '%u'\n", diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 3daeb9772391..f415c13caae0 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -113,6 +113,14 @@ config PINCTRL_MSM8X74 This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 8974 platform. +config PINCTRL_MSM8909 + tristate "Qualcomm 8909 pin controller driver" + depends on OF + depends on PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm TLMM block found on the Qualcomm MSM8909 platform. + config PINCTRL_MSM8916 tristate "Qualcomm 8916 pin controller driver" depends on OF @@ -320,6 +328,15 @@ config PINCTRL_SM6350 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SM6350 platform. +config PINCTRL_SM6375 + tristate "Qualcomm Technologies Inc SM6375 pin controller driver" + depends on GPIOLIB && OF + depends on PINCTRL_MSM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc SM6375 platform. + config PINCTRL_SDX65 tristate "Qualcomm Technologies Inc SDX65 pin controller driver" depends on GPIOLIB && OF @@ -367,7 +384,7 @@ config PINCTRL_SM8350 config PINCTRL_SM8450 tristate "Qualcomm Technologies Inc SM8450 pin controller driver" depends on GPIOLIB && OF - select PINCTRL_MSM + depends on PINCTRL_MSM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 4f0ee7597f81..fbd64853a24d 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_PINCTRL_MSM8226) += pinctrl-msm8226.o obj-$(CONFIG_PINCTRL_MSM8660) += pinctrl-msm8660.o obj-$(CONFIG_PINCTRL_MSM8960) += pinctrl-msm8960.o obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o +obj-$(CONFIG_PINCTRL_MSM8909) += pinctrl-msm8909.o obj-$(CONFIG_PINCTRL_MSM8916) += pinctrl-msm8916.o obj-$(CONFIG_PINCTRL_MSM8953) += pinctrl-msm8953.o obj-$(CONFIG_PINCTRL_MSM8976) += pinctrl-msm8976.o @@ -37,6 +38,7 @@ obj-$(CONFIG_PINCTRL_SDX55) += pinctrl-sdx55.o obj-$(CONFIG_PINCTRL_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_SM6125) += pinctrl-sm6125.o obj-$(CONFIG_PINCTRL_SM6350) += pinctrl-sm6350.o +obj-$(CONFIG_PINCTRL_SM6375) += pinctrl-sm6375.o obj-$(CONFIG_PINCTRL_SDX65) += pinctrl-sdx65.o obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o obj-$(CONFIG_PINCTRL_SM8250) += pinctrl-sm8250.o diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 74810ec4df44..e97ce45b6d53 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -401,7 +401,7 @@ int lpi_pinctrl_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pctrl->slew_base), "Slew resource not provided\n"); - if (data->is_clk_optional) + if (of_property_read_bool(dev->of_node, "qcom,adsp-bypass-mode")) ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks); else ret = devm_clk_bulk_get(dev, MAX_LPI_NUM_CLKS, pctrl->clks); diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h index 759d5d8da562..afbac2a6c82c 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h @@ -77,7 +77,6 @@ struct lpi_pinctrl_variant_data { int ngroups; const struct lpi_function *functions; int nfunctions; - bool is_clk_optional; }; int lpi_pinctrl_probe(struct platform_device *pdev); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8909.c b/drivers/pinctrl/qcom/pinctrl-msm8909.c new file mode 100644 index 000000000000..6dd15b910632 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-msm8909.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2022, Kernkonzept GmbH. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-msm.h" + +#define FUNCTION(fname) \ + [msm_mux_##fname] = { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .name = "gpio" #id, \ + .pins = gpio##id##_pins, \ + .npins = ARRAY_SIZE(gpio##id##_pins), \ + .funcs = (int[]){ \ + msm_mux_gpio, \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 4, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } +static const struct pinctrl_pin_desc msm8909_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "SDC1_CLK"), + PINCTRL_PIN(114, "SDC1_CMD"), + PINCTRL_PIN(115, "SDC1_DATA"), + PINCTRL_PIN(116, "SDC2_CLK"), + PINCTRL_PIN(117, "SDC2_CMD"), + PINCTRL_PIN(118, "SDC2_DATA"), + PINCTRL_PIN(119, "QDSD_CLK"), + PINCTRL_PIN(120, "QDSD_CMD"), + PINCTRL_PIN(121, "QDSD_DATA0"), + PINCTRL_PIN(122, "QDSD_DATA1"), + PINCTRL_PIN(123, "QDSD_DATA2"), + PINCTRL_PIN(124, "QDSD_DATA3"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); + +static const unsigned int sdc1_clk_pins[] = { 113 }; +static const unsigned int sdc1_cmd_pins[] = { 114 }; +static const unsigned int sdc1_data_pins[] = { 115 }; +static const unsigned int sdc2_clk_pins[] = { 116 }; +static const unsigned int sdc2_cmd_pins[] = { 117 }; +static const unsigned int sdc2_data_pins[] = { 118 }; +static const unsigned int qdsd_clk_pins[] = { 119 }; +static const unsigned int qdsd_cmd_pins[] = { 120 }; +static const unsigned int qdsd_data0_pins[] = { 121 }; +static const unsigned int qdsd_data1_pins[] = { 122 }; +static const unsigned int qdsd_data2_pins[] = { 123 }; +static const unsigned int qdsd_data3_pins[] = { 124 }; + +enum msm8909_functions { + msm_mux_gpio, + msm_mux_adsp_ext, + msm_mux_atest_bbrx0, + msm_mux_atest_bbrx1, + msm_mux_atest_char, + msm_mux_atest_char0, + msm_mux_atest_char1, + msm_mux_atest_char2, + msm_mux_atest_char3, + msm_mux_atest_combodac, + msm_mux_atest_gpsadc0, + msm_mux_atest_gpsadc1, + msm_mux_atest_wlan0, + msm_mux_atest_wlan1, + msm_mux_bimc_dte0, + msm_mux_bimc_dte1, + msm_mux_blsp_i2c1, + msm_mux_blsp_i2c2, + msm_mux_blsp_i2c3, + msm_mux_blsp_i2c4, + msm_mux_blsp_i2c5, + msm_mux_blsp_i2c6, + msm_mux_blsp_spi1, + msm_mux_blsp_spi1_cs1, + msm_mux_blsp_spi1_cs2, + msm_mux_blsp_spi1_cs3, + msm_mux_blsp_spi2, + msm_mux_blsp_spi2_cs1, + msm_mux_blsp_spi2_cs2, + msm_mux_blsp_spi2_cs3, + msm_mux_blsp_spi3, + msm_mux_blsp_spi3_cs1, + msm_mux_blsp_spi3_cs2, + msm_mux_blsp_spi3_cs3, + msm_mux_blsp_spi4, + msm_mux_blsp_spi5, + msm_mux_blsp_spi6, + msm_mux_blsp_uart1, + msm_mux_blsp_uart2, + msm_mux_blsp_uim1, + msm_mux_blsp_uim2, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cdc_pdm0, + msm_mux_dbg_out, + msm_mux_dmic0_clk, + msm_mux_dmic0_data, + msm_mux_ebi0_wrcdc, + msm_mux_ebi2_a, + msm_mux_ebi2_lcd, + msm_mux_ext_lpass, + msm_mux_gcc_gp1_clk_a, + msm_mux_gcc_gp1_clk_b, + msm_mux_gcc_gp2_clk_a, + msm_mux_gcc_gp2_clk_b, + msm_mux_gcc_gp3_clk_a, + msm_mux_gcc_gp3_clk_b, + msm_mux_gcc_plltest, + msm_mux_gsm0_tx, + msm_mux_ldo_en, + msm_mux_ldo_update, + msm_mux_m_voc, + msm_mux_mdp_vsync, + msm_mux_modem_tsync, + msm_mux_nav_pps, + msm_mux_nav_tsync, + msm_mux_pa_indicator, + msm_mux_pbs0, + msm_mux_pbs1, + msm_mux_pbs2, + msm_mux_pri_mi2s_data0_a, + msm_mux_pri_mi2s_data0_b, + msm_mux_pri_mi2s_data1_a, + msm_mux_pri_mi2s_data1_b, + msm_mux_pri_mi2s_mclk_a, + msm_mux_pri_mi2s_mclk_b, + msm_mux_pri_mi2s_sck_a, + msm_mux_pri_mi2s_sck_b, + msm_mux_pri_mi2s_ws_a, + msm_mux_pri_mi2s_ws_b, + msm_mux_prng_rosc, + msm_mux_pwr_crypto_enabled_a, + msm_mux_pwr_crypto_enabled_b, + msm_mux_pwr_modem_enabled_a, + msm_mux_pwr_modem_enabled_b, + msm_mux_pwr_nav_enabled_a, + msm_mux_pwr_nav_enabled_b, + msm_mux_qdss_cti_trig_in_a0, + msm_mux_qdss_cti_trig_in_a1, + msm_mux_qdss_cti_trig_in_b0, + msm_mux_qdss_cti_trig_in_b1, + msm_mux_qdss_cti_trig_out_a0, + msm_mux_qdss_cti_trig_out_a1, + msm_mux_qdss_cti_trig_out_b0, + msm_mux_qdss_cti_trig_out_b1, + msm_mux_qdss_traceclk_a, + msm_mux_qdss_tracectl_a, + msm_mux_qdss_tracedata_a, + msm_mux_qdss_tracedata_b, + msm_mux_sd_write, + msm_mux_sec_mi2s, + msm_mux_smb_int, + msm_mux_ssbi0, + msm_mux_ssbi1, + msm_mux_uim1_clk, + msm_mux_uim1_data, + msm_mux_uim1_present, + msm_mux_uim1_reset, + msm_mux_uim2_clk, + msm_mux_uim2_data, + msm_mux_uim2_present, + msm_mux_uim2_reset, + msm_mux_uim3_clk, + msm_mux_uim3_data, + msm_mux_uim3_present, + msm_mux_uim3_reset, + msm_mux_uim_batt, + msm_mux_wcss_bt, + msm_mux_wcss_fm, + msm_mux_wcss_wlan, + msm_mux__, +}; + +static const char * const adsp_ext_groups[] = { "gpio38" }; +static const char * const atest_bbrx0_groups[] = { "gpio37" }; +static const char * const atest_bbrx1_groups[] = { "gpio36" }; +static const char * const atest_char0_groups[] = { "gpio62" }; +static const char * const atest_char1_groups[] = { "gpio61" }; +static const char * const atest_char2_groups[] = { "gpio60" }; +static const char * const atest_char3_groups[] = { "gpio59" }; +static const char * const atest_char_groups[] = { "gpio63" }; +static const char * const atest_combodac_groups[] = { + "gpio32", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio47", "gpio48", "gpio66", "gpio81", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio94", "gpio95", "gpio110" +}; +static const char * const atest_gpsadc0_groups[] = { "gpio65" }; +static const char * const atest_gpsadc1_groups[] = { "gpio79" }; +static const char * const atest_wlan0_groups[] = { "gpio96" }; +static const char * const atest_wlan1_groups[] = { "gpio97" }; +static const char * const bimc_dte0_groups[] = { "gpio6", "gpio59" }; +static const char * const bimc_dte1_groups[] = { "gpio7", "gpio60" }; +static const char * const blsp_i2c1_groups[] = { "gpio6", "gpio7" }; +static const char * const blsp_i2c2_groups[] = { "gpio111", "gpio112" }; +static const char * const blsp_i2c3_groups[] = { "gpio29", "gpio30" }; +static const char * const blsp_i2c4_groups[] = { "gpio14", "gpio15" }; +static const char * const blsp_i2c5_groups[] = { "gpio18", "gpio19" }; +static const char * const blsp_i2c6_groups[] = { "gpio10", "gpio11" }; +static const char * const blsp_spi1_cs1_groups[] = { "gpio97" }; +static const char * const blsp_spi1_cs2_groups[] = { "gpio37" }; +static const char * const blsp_spi1_cs3_groups[] = { "gpio65" }; +static const char * const blsp_spi1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7" +}; +static const char * const blsp_spi2_cs1_groups[] = { "gpio98" }; +static const char * const blsp_spi2_cs2_groups[] = { "gpio17" }; +static const char * const blsp_spi2_cs3_groups[] = { "gpio5" }; +static const char * const blsp_spi2_groups[] = { + "gpio20", "gpio21", "gpio111", "gpio112" +}; +static const char * const blsp_spi3_cs1_groups[] = { "gpio95" }; +static const char * const blsp_spi3_cs2_groups[] = { "gpio65" }; +static const char * const blsp_spi3_cs3_groups[] = { "gpio4" }; +static const char * const blsp_spi3_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3" +}; +static const char * const blsp_spi4_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15" +}; +static const char * const blsp_spi5_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19" +}; +static const char * const blsp_spi6_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11" +}; +static const char * const blsp_uart1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7" +}; +static const char * const blsp_uart2_groups[] = { + "gpio20", "gpio21", "gpio111", "gpio112" +}; +static const char * const blsp_uim1_groups[] = { "gpio4", "gpio5" }; +static const char * const blsp_uim2_groups[] = { "gpio20", "gpio21" }; +static const char * const cam_mclk_groups[] = { "gpio26", "gpio27" }; +static const char * const cci_async_groups[] = { "gpio33" }; +static const char * const cci_timer0_groups[] = { "gpio31" }; +static const char * const cci_timer1_groups[] = { "gpio32" }; +static const char * const cci_timer2_groups[] = { "gpio38" }; +static const char * const cdc_pdm0_groups[] = { + "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", "gpio64" +}; +static const char * const dbg_out_groups[] = { "gpio10" }; +static const char * const dmic0_clk_groups[] = { "gpio4" }; +static const char * const dmic0_data_groups[] = { "gpio5" }; +static const char * const ebi0_wrcdc_groups[] = { "gpio64" }; +static const char * const ebi2_a_groups[] = { "gpio99" }; +static const char * const ebi2_lcd_groups[] = { + "gpio24", "gpio24", "gpio25", "gpio95" +}; +static const char * const ext_lpass_groups[] = { "gpio45" }; +static const char * const gcc_gp1_clk_a_groups[] = { "gpio49" }; +static const char * const gcc_gp1_clk_b_groups[] = { "gpio14" }; +static const char * const gcc_gp2_clk_a_groups[] = { "gpio50" }; +static const char * const gcc_gp2_clk_b_groups[] = { "gpio12" }; +static const char * const gcc_gp3_clk_a_groups[] = { "gpio51" }; +static const char * const gcc_gp3_clk_b_groups[] = { "gpio13" }; +static const char * const gcc_plltest_groups[] = { "gpio66", "gpio67" }; +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112" +}; +static const char * const gsm0_tx_groups[] = { "gpio85" }; +static const char * const ldo_en_groups[] = { "gpio99" }; +static const char * const ldo_update_groups[] = { "gpio98" }; +static const char * const m_voc_groups[] = { "gpio8", "gpio95" }; +static const char * const mdp_vsync_groups[] = { "gpio24", "gpio25" }; +static const char * const modem_tsync_groups[] = { "gpio83" }; +static const char * const nav_pps_groups[] = { "gpio83" }; +static const char * const nav_tsync_groups[] = { "gpio83" }; +static const char * const pa_indicator_groups[] = { "gpio82" }; +static const char * const pbs0_groups[] = { "gpio90" }; +static const char * const pbs1_groups[] = { "gpio91" }; +static const char * const pbs2_groups[] = { "gpio92" }; +static const char * const pri_mi2s_data0_a_groups[] = { "gpio62" }; +static const char * const pri_mi2s_data0_b_groups[] = { "gpio95" }; +static const char * const pri_mi2s_data1_a_groups[] = { "gpio63" }; +static const char * const pri_mi2s_data1_b_groups[] = { "gpio96" }; +static const char * const pri_mi2s_mclk_a_groups[] = { "gpio59" }; +static const char * const pri_mi2s_mclk_b_groups[] = { "gpio98" }; +static const char * const pri_mi2s_sck_a_groups[] = { "gpio60" }; +static const char * const pri_mi2s_sck_b_groups[] = { "gpio94" }; +static const char * const pri_mi2s_ws_a_groups[] = { "gpio61" }; +static const char * const pri_mi2s_ws_b_groups[] = { "gpio110" }; +static const char * const prng_rosc_groups[] = { "gpio43" }; +static const char * const pwr_crypto_enabled_a_groups[] = { "gpio35" }; +static const char * const pwr_crypto_enabled_b_groups[] = { "gpio96" }; +static const char * const pwr_modem_enabled_a_groups[] = { "gpio28" }; +static const char * const pwr_modem_enabled_b_groups[] = { "gpio94" }; +static const char * const pwr_nav_enabled_a_groups[] = { "gpio34" }; +static const char * const pwr_nav_enabled_b_groups[] = { "gpio95" }; +static const char * const qdss_cti_trig_in_a0_groups[] = { "gpio20" }; +static const char * const qdss_cti_trig_in_a1_groups[] = { "gpio49" }; +static const char * const qdss_cti_trig_in_b0_groups[] = { "gpio21" }; +static const char * const qdss_cti_trig_in_b1_groups[] = { "gpio50" }; +static const char * const qdss_cti_trig_out_a0_groups[] = { "gpio23" }; +static const char * const qdss_cti_trig_out_a1_groups[] = { "gpio52" }; +static const char * const qdss_cti_trig_out_b0_groups[] = { "gpio22" }; +static const char * const qdss_cti_trig_out_b1_groups[] = { "gpio51" }; +static const char * const qdss_traceclk_a_groups[] = { "gpio46" }; +static const char * const qdss_tracectl_a_groups[] = { "gpio45" }; +static const char * const qdss_tracedata_a_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio47", "gpio48", "gpio58", "gpio65", "gpio94", "gpio96", + "gpio97" +}; +static const char * const qdss_tracedata_b_groups[] = { + "gpio14", "gpio16", "gpio17", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio33", "gpio34", "gpio35", "gpio36", "gpio37", "gpio93" +}; +static const char * const sd_write_groups[] = { "gpio99" }; +static const char * const sec_mi2s_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio98" +}; +static const char * const smb_int_groups[] = { "gpio58" }; +static const char * const ssbi0_groups[] = { "gpio88" }; +static const char * const ssbi1_groups[] = { "gpio89" }; +static const char * const uim1_clk_groups[] = { "gpio54" }; +static const char * const uim1_data_groups[] = { "gpio53" }; +static const char * const uim1_present_groups[] = { "gpio56" }; +static const char * const uim1_reset_groups[] = { "gpio55" }; +static const char * const uim2_clk_groups[] = { "gpio50" }; +static const char * const uim2_data_groups[] = { "gpio49" }; +static const char * const uim2_present_groups[] = { "gpio52" }; +static const char * const uim2_reset_groups[] = { "gpio51" }; +static const char * const uim3_clk_groups[] = { "gpio23" }; +static const char * const uim3_data_groups[] = { "gpio20" }; +static const char * const uim3_present_groups[] = { "gpio21" }; +static const char * const uim3_reset_groups[] = { "gpio22" }; +static const char * const uim_batt_groups[] = { "gpio57" }; +static const char * const wcss_bt_groups[] = { "gpio39", "gpio47", "gpio48" }; +static const char * const wcss_fm_groups[] = { "gpio45", "gpio46" }; +static const char * const wcss_wlan_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", "gpio44" +}; + +static const struct msm_function msm8909_functions[] = { + FUNCTION(adsp_ext), + FUNCTION(atest_bbrx0), + FUNCTION(atest_bbrx1), + FUNCTION(atest_char), + FUNCTION(atest_char0), + FUNCTION(atest_char1), + FUNCTION(atest_char2), + FUNCTION(atest_char3), + FUNCTION(atest_combodac), + FUNCTION(atest_gpsadc0), + FUNCTION(atest_gpsadc1), + FUNCTION(atest_wlan0), + FUNCTION(atest_wlan1), + FUNCTION(bimc_dte0), + FUNCTION(bimc_dte1), + FUNCTION(blsp_i2c1), + FUNCTION(blsp_i2c2), + FUNCTION(blsp_i2c3), + FUNCTION(blsp_i2c4), + FUNCTION(blsp_i2c5), + FUNCTION(blsp_i2c6), + FUNCTION(blsp_spi1), + FUNCTION(blsp_spi1_cs1), + FUNCTION(blsp_spi1_cs2), + FUNCTION(blsp_spi1_cs3), + FUNCTION(blsp_spi2), + FUNCTION(blsp_spi2_cs1), + FUNCTION(blsp_spi2_cs2), + FUNCTION(blsp_spi2_cs3), + FUNCTION(blsp_spi3), + FUNCTION(blsp_spi3_cs1), + FUNCTION(blsp_spi3_cs2), + FUNCTION(blsp_spi3_cs3), + FUNCTION(blsp_spi4), + FUNCTION(blsp_spi5), + FUNCTION(blsp_spi6), + FUNCTION(blsp_uart1), + FUNCTION(blsp_uart2), + FUNCTION(blsp_uim1), + FUNCTION(blsp_uim2), + FUNCTION(cam_mclk), + FUNCTION(cci_async), + FUNCTION(cci_timer0), + FUNCTION(cci_timer1), + FUNCTION(cci_timer2), + FUNCTION(cdc_pdm0), + FUNCTION(dbg_out), + FUNCTION(dmic0_clk), + FUNCTION(dmic0_data), + FUNCTION(ebi0_wrcdc), + FUNCTION(ebi2_a), + FUNCTION(ebi2_lcd), + FUNCTION(ext_lpass), + FUNCTION(gcc_gp1_clk_a), + FUNCTION(gcc_gp1_clk_b), + FUNCTION(gcc_gp2_clk_a), + FUNCTION(gcc_gp2_clk_b), + FUNCTION(gcc_gp3_clk_a), + FUNCTION(gcc_gp3_clk_b), + FUNCTION(gcc_plltest), + FUNCTION(gpio), + FUNCTION(gsm0_tx), + FUNCTION(ldo_en), + FUNCTION(ldo_update), + FUNCTION(m_voc), + FUNCTION(mdp_vsync), + FUNCTION(modem_tsync), + FUNCTION(nav_pps), + FUNCTION(nav_tsync), + FUNCTION(pa_indicator), + FUNCTION(pbs0), + FUNCTION(pbs1), + FUNCTION(pbs2), + FUNCTION(pri_mi2s_data0_a), + FUNCTION(pri_mi2s_data0_b), + FUNCTION(pri_mi2s_data1_a), + FUNCTION(pri_mi2s_data1_b), + FUNCTION(pri_mi2s_mclk_a), + FUNCTION(pri_mi2s_mclk_b), + FUNCTION(pri_mi2s_sck_a), + FUNCTION(pri_mi2s_sck_b), + FUNCTION(pri_mi2s_ws_a), + FUNCTION(pri_mi2s_ws_b), + FUNCTION(prng_rosc), + FUNCTION(pwr_crypto_enabled_a), + FUNCTION(pwr_crypto_enabled_b), + FUNCTION(pwr_modem_enabled_a), + FUNCTION(pwr_modem_enabled_b), + FUNCTION(pwr_nav_enabled_a), + FUNCTION(pwr_nav_enabled_b), + FUNCTION(qdss_cti_trig_in_a0), + FUNCTION(qdss_cti_trig_in_a1), + FUNCTION(qdss_cti_trig_in_b0), + FUNCTION(qdss_cti_trig_in_b1), + FUNCTION(qdss_cti_trig_out_a0), + FUNCTION(qdss_cti_trig_out_a1), + FUNCTION(qdss_cti_trig_out_b0), + FUNCTION(qdss_cti_trig_out_b1), + FUNCTION(qdss_traceclk_a), + FUNCTION(qdss_tracectl_a), + FUNCTION(qdss_tracedata_a), + FUNCTION(qdss_tracedata_b), + FUNCTION(sd_write), + FUNCTION(sec_mi2s), + FUNCTION(smb_int), + FUNCTION(ssbi0), + FUNCTION(ssbi1), + FUNCTION(uim1_clk), + FUNCTION(uim1_data), + FUNCTION(uim1_present), + FUNCTION(uim1_reset), + FUNCTION(uim2_clk), + FUNCTION(uim2_data), + FUNCTION(uim2_present), + FUNCTION(uim2_reset), + FUNCTION(uim3_clk), + FUNCTION(uim3_data), + FUNCTION(uim3_present), + FUNCTION(uim3_reset), + FUNCTION(uim_batt), + FUNCTION(wcss_bt), + FUNCTION(wcss_fm), + FUNCTION(wcss_wlan), +}; + +static const struct msm_pingroup msm8909_groups[] = { + PINGROUP(0, blsp_spi3, sec_mi2s, _, _, _, _, _, _, _), + PINGROUP(1, blsp_spi3, sec_mi2s, _, _, _, _, _, _, _), + PINGROUP(2, blsp_spi3, sec_mi2s, _, _, _, _, _, _, _), + PINGROUP(3, blsp_spi3, sec_mi2s, _, _, _, _, _, _, _), + PINGROUP(4, blsp_spi1, blsp_uart1, blsp_uim1, blsp_spi3_cs3, dmic0_clk, _, _, _, _), + PINGROUP(5, blsp_spi1, blsp_uart1, blsp_uim1, blsp_spi2_cs3, dmic0_data, _, _, _, _), + PINGROUP(6, blsp_spi1, blsp_uart1, blsp_i2c1, _, _, _, _, _, bimc_dte0), + PINGROUP(7, blsp_spi1, blsp_uart1, blsp_i2c1, _, _, _, _, _, bimc_dte1), + PINGROUP(8, blsp_spi6, m_voc, _, _, _, _, _, qdss_tracedata_a, _), + PINGROUP(9, blsp_spi6, _, _, _, _, _, qdss_tracedata_a, _, _), + PINGROUP(10, blsp_spi6, blsp_i2c6, dbg_out, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(11, blsp_spi6, blsp_i2c6, _, _, _, _, _, _, _), + PINGROUP(12, blsp_spi4, gcc_gp2_clk_b, _, _, _, _, _, _, _), + PINGROUP(13, blsp_spi4, gcc_gp3_clk_b, _, _, _, _, _, _, _), + PINGROUP(14, blsp_spi4, blsp_i2c4, gcc_gp1_clk_b, _, _, _, _, _, qdss_tracedata_b), + PINGROUP(15, blsp_spi4, blsp_i2c4, _, _, _, _, _, _, _), + PINGROUP(16, blsp_spi5, _, _, _, _, _, qdss_tracedata_b, _, _), + PINGROUP(17, blsp_spi5, blsp_spi2_cs2, _, _, _, _, _, qdss_tracedata_b, _), + PINGROUP(18, blsp_spi5, blsp_i2c5, _, _, _, _, _, _, _), + PINGROUP(19, blsp_spi5, blsp_i2c5, _, _, _, _, _, _, _), + PINGROUP(20, uim3_data, blsp_spi2, blsp_uart2, blsp_uim2, _, qdss_cti_trig_in_a0, _, _, _), + PINGROUP(21, uim3_present, blsp_spi2, blsp_uart2, blsp_uim2, _, qdss_cti_trig_in_b0, _, _, _), + PINGROUP(22, uim3_reset, _, qdss_cti_trig_out_b0, _, _, _, _, _, _), + PINGROUP(23, uim3_clk, qdss_cti_trig_out_a0, _, _, _, _, _, _, _), + PINGROUP(24, mdp_vsync, ebi2_lcd, ebi2_lcd, _, _, _, _, _, _), + PINGROUP(25, mdp_vsync, ebi2_lcd, _, _, _, _, _, _, _), + PINGROUP(26, cam_mclk, _, _, _, _, _, _, _, _), + PINGROUP(27, cam_mclk, _, _, _, _, _, _, _, _), + PINGROUP(28, _, pwr_modem_enabled_a, _, _, _, _, _, _, _), + PINGROUP(29, blsp_i2c3, _, _, _, _, _, qdss_tracedata_b, _, _), + PINGROUP(30, blsp_i2c3, _, _, _, _, _, qdss_tracedata_b, _, _), + PINGROUP(31, cci_timer0, _, _, _, _, _, _, qdss_tracedata_b, _), + PINGROUP(32, cci_timer1, _, qdss_tracedata_b, _, atest_combodac, _, _, _, _), + PINGROUP(33, cci_async, qdss_tracedata_b, _, _, _, _, _, _, _), + PINGROUP(34, pwr_nav_enabled_a, qdss_tracedata_b, _, _, _, _, _, _, _), + PINGROUP(35, pwr_crypto_enabled_a, qdss_tracedata_b, _, _, _, _, _, _, _), + PINGROUP(36, qdss_tracedata_b, _, atest_bbrx1, _, _, _, _, _, _), + PINGROUP(37, blsp_spi1_cs2, qdss_tracedata_b, _, atest_bbrx0, _, _, _, _, _), + PINGROUP(38, cci_timer2, adsp_ext, _, atest_combodac, _, _, _, _, _), + PINGROUP(39, wcss_bt, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(40, wcss_wlan, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(41, wcss_wlan, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(42, wcss_wlan, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(43, wcss_wlan, prng_rosc, qdss_tracedata_a, _, atest_combodac, _, _, _, _), + PINGROUP(44, wcss_wlan, _, atest_combodac, _, _, _, _, _, _), + PINGROUP(45, wcss_fm, ext_lpass, qdss_tracectl_a, _, atest_combodac, _, _, _, _), + PINGROUP(46, wcss_fm, qdss_traceclk_a, _, _, _, _, _, _, _), + PINGROUP(47, wcss_bt, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(48, wcss_bt, qdss_tracedata_a, _, atest_combodac, _, _, _, _, _), + PINGROUP(49, uim2_data, gcc_gp1_clk_a, qdss_cti_trig_in_a1, _, _, _, _, _, _), + PINGROUP(50, uim2_clk, gcc_gp2_clk_a, qdss_cti_trig_in_b1, _, _, _, _, _, _), + PINGROUP(51, uim2_reset, gcc_gp3_clk_a, qdss_cti_trig_out_b1, _, _, _, _, _, _), + PINGROUP(52, uim2_present, qdss_cti_trig_out_a1, _, _, _, _, _, _, _), + PINGROUP(53, uim1_data, _, _, _, _, _, _, _, _), + PINGROUP(54, uim1_clk, _, _, _, _, _, _, _, _), + PINGROUP(55, uim1_reset, _, _, _, _, _, _, _, _), + PINGROUP(56, uim1_present, _, _, _, _, _, _, _, _), + PINGROUP(57, uim_batt, _, _, _, _, _, _, _, _), + PINGROUP(58, qdss_tracedata_a, smb_int, _, _, _, _, _, _, _), + PINGROUP(59, cdc_pdm0, pri_mi2s_mclk_a, atest_char3, _, _, _, _, _, bimc_dte0), + PINGROUP(60, cdc_pdm0, pri_mi2s_sck_a, atest_char2, _, _, _, _, _, bimc_dte1), + PINGROUP(61, cdc_pdm0, pri_mi2s_ws_a, atest_char1, _, _, _, _, _, _), + PINGROUP(62, cdc_pdm0, pri_mi2s_data0_a, atest_char0, _, _, _, _, _, _), + PINGROUP(63, cdc_pdm0, pri_mi2s_data1_a, atest_char, _, _, _, _, _, _), + PINGROUP(64, cdc_pdm0, _, _, _, _, _, ebi0_wrcdc, _, _), + PINGROUP(65, blsp_spi3_cs2, blsp_spi1_cs3, qdss_tracedata_a, _, atest_gpsadc0, _, _, _, _), + PINGROUP(66, _, gcc_plltest, _, atest_combodac, _, _, _, _, _), + PINGROUP(67, _, gcc_plltest, _, _, _, _, _, _, _), + PINGROUP(68, _, _, _, _, _, _, _, _, _), + PINGROUP(69, _, _, _, _, _, _, _, _, _), + PINGROUP(70, _, _, _, _, _, _, _, _, _), + PINGROUP(71, _, _, _, _, _, _, _, _, _), + PINGROUP(72, _, _, _, _, _, _, _, _, _), + PINGROUP(73, _, _, _, _, _, _, _, _, _), + PINGROUP(74, _, _, _, _, _, _, _, _, _), + PINGROUP(75, _, _, _, _, _, _, _, _, _), + PINGROUP(76, _, _, _, _, _, _, _, _, _), + PINGROUP(77, _, _, _, _, _, _, _, _, _), + PINGROUP(78, _, _, _, _, _, _, _, _, _), + PINGROUP(79, _, _, atest_gpsadc1, _, _, _, _, _, _), + PINGROUP(80, _, _, _, _, _, _, _, _, _), + PINGROUP(81, _, _, _, atest_combodac, _, _, _, _, _), + PINGROUP(82, _, pa_indicator, _, _, _, _, _, _, _), + PINGROUP(83, _, modem_tsync, nav_tsync, nav_pps, _, atest_combodac, _, _, _), + PINGROUP(84, _, _, atest_combodac, _, _, _, _, _, _), + PINGROUP(85, gsm0_tx, _, _, atest_combodac, _, _, _, _, _), + PINGROUP(86, _, _, atest_combodac, _, _, _, _, _, _), + PINGROUP(87, _, _, _, _, _, _, _, _, _), + PINGROUP(88, _, ssbi0, _, _, _, _, _, _, _), + PINGROUP(89, _, ssbi1, _, _, _, _, _, _, _), + PINGROUP(90, pbs0, _, _, _, _, _, _, _, _), + PINGROUP(91, pbs1, _, _, _, _, _, _, _, _), + PINGROUP(92, pbs2, _, _, _, _, _, _, _, _), + PINGROUP(93, qdss_tracedata_b, _, _, _, _, _, _, _, _), + PINGROUP(94, pri_mi2s_sck_b, pwr_modem_enabled_b, qdss_tracedata_a, _, atest_combodac, _, _, _, _), + PINGROUP(95, blsp_spi3_cs1, pri_mi2s_data0_b, ebi2_lcd, m_voc, pwr_nav_enabled_b, _, atest_combodac, _, _), + PINGROUP(96, pri_mi2s_data1_b, _, pwr_crypto_enabled_b, qdss_tracedata_a, _, atest_wlan0, _, _, _), + PINGROUP(97, blsp_spi1_cs1, qdss_tracedata_a, _, atest_wlan1, _, _, _, _, _), + PINGROUP(98, sec_mi2s, pri_mi2s_mclk_b, blsp_spi2_cs1, ldo_update, _, _, _, _, _), + PINGROUP(99, ebi2_a, sd_write, ldo_en, _, _, _, _, _, _), + PINGROUP(100, _, _, _, _, _, _, _, _, _), + PINGROUP(101, _, _, _, _, _, _, _, _, _), + PINGROUP(102, _, _, _, _, _, _, _, _, _), + PINGROUP(103, _, _, _, _, _, _, _, _, _), + PINGROUP(104, _, _, _, _, _, _, _, _, _), + PINGROUP(105, _, _, _, _, _, _, _, _, _), + PINGROUP(106, _, _, _, _, _, _, _, _, _), + PINGROUP(107, _, _, _, _, _, _, _, _, _), + PINGROUP(108, _, _, _, _, _, _, _, _, _), + PINGROUP(109, _, _, _, _, _, _, _, _, _), + PINGROUP(110, pri_mi2s_ws_b, _, atest_combodac, _, _, _, _, _, _), + PINGROUP(111, blsp_spi2, blsp_uart2, blsp_i2c2, _, _, _, _, _, _), + PINGROUP(112, blsp_spi2, blsp_uart2, blsp_i2c2, _, _, _, _, _, _), + SDC_QDSD_PINGROUP(sdc1_clk, 0x10a000, 13, 6), + SDC_QDSD_PINGROUP(sdc1_cmd, 0x10a000, 11, 3), + SDC_QDSD_PINGROUP(sdc1_data, 0x10a000, 9, 0), + SDC_QDSD_PINGROUP(sdc2_clk, 0x109000, 14, 6), + SDC_QDSD_PINGROUP(sdc2_cmd, 0x109000, 11, 3), + SDC_QDSD_PINGROUP(sdc2_data, 0x109000, 9, 0), + SDC_QDSD_PINGROUP(qdsd_clk, 0x19c000, 3, 0), + SDC_QDSD_PINGROUP(qdsd_cmd, 0x19c000, 8, 5), + SDC_QDSD_PINGROUP(qdsd_data0, 0x19c000, 13, 10), + SDC_QDSD_PINGROUP(qdsd_data1, 0x19c000, 18, 15), + SDC_QDSD_PINGROUP(qdsd_data2, 0x19c000, 23, 20), + SDC_QDSD_PINGROUP(qdsd_data3, 0x19c000, 28, 25), +}; + +static const struct msm_gpio_wakeirq_map msm8909_mpm_map[] = { + { 65, 3 }, { 5, 4 }, { 11, 5 }, { 12, 6 }, { 64, 7 }, { 58, 8 }, + { 50, 9 }, { 13, 10 }, { 49, 11 }, { 20, 12 }, { 21, 13 }, { 25, 14 }, + { 46, 15 }, { 45, 16 }, { 28, 17 }, { 44, 18 }, { 31, 19 }, { 43, 20 }, + { 42, 21 }, { 34, 22 }, { 35, 23 }, { 36, 24 }, { 37, 25 }, { 38, 26 }, + { 39, 27 }, { 40, 28 }, { 41, 29 }, { 90, 30 }, { 91, 32 }, { 92, 33 }, + { 94, 34 }, { 95, 35 }, { 96, 36 }, { 97, 37 }, { 98, 38 }, + { 110, 39 }, { 111, 40 }, { 112, 41 }, { 105, 42 }, { 107, 43 }, + { 47, 50 }, { 48, 51 }, +}; + +static const struct msm_pinctrl_soc_data msm8909_pinctrl = { + .pins = msm8909_pins, + .npins = ARRAY_SIZE(msm8909_pins), + .functions = msm8909_functions, + .nfunctions = ARRAY_SIZE(msm8909_functions), + .groups = msm8909_groups, + .ngroups = ARRAY_SIZE(msm8909_groups), + .ngpios = 113, + .wakeirq_map = msm8909_mpm_map, + .nwakeirq_map = ARRAY_SIZE(msm8909_mpm_map), +}; + +static int msm8909_pinctrl_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &msm8909_pinctrl); +} + +static const struct of_device_id msm8909_pinctrl_of_match[] = { + { .compatible = "qcom,msm8909-tlmm", }, + { }, +}; +MODULE_DEVICE_TABLE(of, msm8909_pinctrl_of_match); + +static struct platform_driver msm8909_pinctrl_driver = { + .driver = { + .name = "msm8909-pinctrl", + .of_match_table = msm8909_pinctrl_of_match, + }, + .probe = msm8909_pinctrl_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init msm8909_pinctrl_init(void) +{ + return platform_driver_register(&msm8909_pinctrl_driver); +} +arch_initcall(msm8909_pinctrl_init); + +static void __exit msm8909_pinctrl_exit(void) +{ + platform_driver_unregister(&msm8909_pinctrl_driver); +} +module_exit(msm8909_pinctrl_exit); + +MODULE_DESCRIPTION("Qualcomm MSM8909 TLMM pinctrl driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8916.c b/drivers/pinctrl/qcom/pinctrl-msm8916.c index 396db12ae904..bf68913ba821 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8916.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8916.c @@ -844,8 +844,8 @@ static const struct msm_pingroup msm8916_groups[] = { PINGROUP(28, pwr_modem_enabled_a, NA, NA, NA, NA, NA, qdss_tracedata_b, NA, atest_combodac), PINGROUP(29, cci_i2c, NA, NA, NA, NA, NA, qdss_tracedata_b, NA, atest_combodac), PINGROUP(30, cci_i2c, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_b), - PINGROUP(31, cci_timer0, NA, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(32, cci_timer1, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(31, cci_timer0, flash_strobe, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(32, cci_timer1, flash_strobe, NA, NA, NA, NA, NA, NA, NA), PINGROUP(33, cci_async, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_b), PINGROUP(34, pwr_nav_enabled_a, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_b), PINGROUP(35, pwr_crypto_enabled_a, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_b), diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c index 2add9a4520c2..d615b6c55b89 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c @@ -141,7 +141,6 @@ static const struct lpi_pinctrl_variant_data sc7280_lpi_data = { .ngroups = ARRAY_SIZE(sc7280_groups), .functions = sc7280_functions, .nfunctions = ARRAY_SIZE(sc7280_functions), - .is_clk_optional = true, }; static const struct of_device_id lpi_pinctrl_of_match[] = { diff --git a/drivers/pinctrl/qcom/pinctrl-sm6375.c b/drivers/pinctrl/qcom/pinctrl-sm6375.c new file mode 100644 index 000000000000..1138e683e6f4 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm6375.c @@ -0,0 +1,1544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Konrad Dybcio <konrad.dybcio@somainline.org> + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-msm.h" + +#define FUNCTION(fname) \ + [msm_mux_##fname] = { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +#define REG_BASE 0x100000 +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .name = "gpio" #id, \ + .pins = gpio##id##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = REG_SIZE * id + 0x4, \ + .intr_cfg_reg = REG_SIZE * id + 0x8, \ + .intr_status_reg = REG_SIZE * id + 0xc, \ + .intr_target_reg = REG_SIZE * id + 0x8, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc sm6375_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "UFS_RESET"), + PINCTRL_PIN(157, "SDC1_RCLK"), + PINCTRL_PIN(158, "SDC1_CLK"), + PINCTRL_PIN(159, "SDC1_CMD"), + PINCTRL_PIN(160, "SDC1_DATA"), + PINCTRL_PIN(161, "SDC2_CLK"), + PINCTRL_PIN(162, "SDC2_CMD"), + PINCTRL_PIN(163, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); + + +static const unsigned int sdc1_rclk_pins[] = { 157 }; +static const unsigned int sdc1_clk_pins[] = { 158 }; +static const unsigned int sdc1_cmd_pins[] = { 159 }; +static const unsigned int sdc1_data_pins[] = { 160 }; +static const unsigned int sdc2_clk_pins[] = { 161 }; +static const unsigned int sdc2_cmd_pins[] = { 162 }; +static const unsigned int sdc2_data_pins[] = { 163 }; +static const unsigned int ufs_reset_pins[] = { 156 }; + +enum sm6375_functions { + msm_mux_adsp_ext, + msm_mux_agera_pll, + msm_mux_atest_char, + msm_mux_atest_char0, + msm_mux_atest_char1, + msm_mux_atest_char2, + msm_mux_atest_char3, + msm_mux_atest_tsens, + msm_mux_atest_tsens2, + msm_mux_atest_usb1, + msm_mux_atest_usb10, + msm_mux_atest_usb11, + msm_mux_atest_usb12, + msm_mux_atest_usb13, + msm_mux_atest_usb2, + msm_mux_atest_usb20, + msm_mux_atest_usb21, + msm_mux_atest_usb22, + msm_mux_atest_usb23, + msm_mux_audio_ref, + msm_mux_btfm_slimbus, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cci_timer3, + msm_mux_cci_timer4, + msm_mux_cri_trng, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_dp_hot, + msm_mux_edp_lcd, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gp_pdm0, + msm_mux_gp_pdm1, + msm_mux_gp_pdm2, + msm_mux_gpio, + msm_mux_gps_tx, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_ldo_en, + msm_mux_ldo_update, + msm_mux_lpass_ext, + msm_mux_m_voc, + msm_mux_mclk, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0, + msm_mux_mdp_vsync1, + msm_mux_mdp_vsync2, + msm_mux_mdp_vsync3, + msm_mux_mi2s_0, + msm_mux_mi2s_1, + msm_mux_mi2s_2, + msm_mux_mss_lte, + msm_mux_nav_gpio, + msm_mux_nav_pps, + msm_mux_pa_indicator, + msm_mux_phase_flag0, + msm_mux_phase_flag1, + msm_mux_phase_flag10, + msm_mux_phase_flag11, + msm_mux_phase_flag12, + msm_mux_phase_flag13, + msm_mux_phase_flag14, + msm_mux_phase_flag15, + msm_mux_phase_flag16, + msm_mux_phase_flag17, + msm_mux_phase_flag18, + msm_mux_phase_flag19, + msm_mux_phase_flag2, + msm_mux_phase_flag20, + msm_mux_phase_flag21, + msm_mux_phase_flag22, + msm_mux_phase_flag23, + msm_mux_phase_flag24, + msm_mux_phase_flag25, + msm_mux_phase_flag26, + msm_mux_phase_flag27, + msm_mux_phase_flag28, + msm_mux_phase_flag29, + msm_mux_phase_flag3, + msm_mux_phase_flag30, + msm_mux_phase_flag31, + msm_mux_phase_flag4, + msm_mux_phase_flag5, + msm_mux_phase_flag6, + msm_mux_phase_flag7, + msm_mux_phase_flag8, + msm_mux_phase_flag9, + msm_mux_pll_bist, + msm_mux_pll_bypassnl, + msm_mux_pll_clk, + msm_mux_pll_reset, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qdss_gpio0, + msm_mux_qdss_gpio1, + msm_mux_qdss_gpio10, + msm_mux_qdss_gpio11, + msm_mux_qdss_gpio12, + msm_mux_qdss_gpio13, + msm_mux_qdss_gpio14, + msm_mux_qdss_gpio15, + msm_mux_qdss_gpio2, + msm_mux_qdss_gpio3, + msm_mux_qdss_gpio4, + msm_mux_qdss_gpio5, + msm_mux_qdss_gpio6, + msm_mux_qdss_gpio7, + msm_mux_qdss_gpio8, + msm_mux_qdss_gpio9, + msm_mux_qlink0_enable, + msm_mux_qlink0_request, + msm_mux_qlink0_wmss, + msm_mux_qlink1_enable, + msm_mux_qlink1_request, + msm_mux_qlink1_wmss, + msm_mux_qup00, + msm_mux_qup01, + msm_mux_qup02, + msm_mux_qup10, + msm_mux_qup11_f1, + msm_mux_qup11_f2, + msm_mux_qup12, + msm_mux_qup13_f1, + msm_mux_qup13_f2, + msm_mux_qup14, + msm_mux_sd_write, + msm_mux_sdc1_tb, + msm_mux_sdc2_tb, + msm_mux_sp_cmu, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_uim1_clk, + msm_mux_uim1_data, + msm_mux_uim1_present, + msm_mux_uim1_reset, + msm_mux_uim2_clk, + msm_mux_uim2_data, + msm_mux_uim2_present, + msm_mux_uim2_reset, + msm_mux_usb2phy_ac, + msm_mux_usb_phy, + msm_mux_vfr_1, + msm_mux_vsense_trigger, + msm_mux_wlan1_adc0, + msm_mux_wlan1_adc1, + msm_mux_wlan2_adc0, + msm_mux_wlan2_adc1, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", + "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", "gpio50", + "gpio51", "gpio52", "gpio53", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", "gpio66", + "gpio67", "gpio68", "gpio69", "gpio75", "gpio76", "gpio77", "gpio78", + "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", "gpio85", + "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", "gpio92", + "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", "gpio99", + "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", "gpio105", + "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", "gpio111", + "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", "gpio117", + "gpio118", "gpio119", "gpio120", "gpio124", "gpio125", "gpio126", + "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", "gpio132", + "gpio133", "gpio134", "gpio135", "gpio136", "gpio141", "gpio142", + "gpio143", "gpio150", "gpio151", "gpio152", "gpio153", "gpio154", + "gpio155", +}; +static const char * const agera_pll_groups[] = { + "gpio89", +}; +static const char * const cci_async_groups[] = { + "gpio35", "gpio36", "gpio48", "gpio52", "gpio53", +}; +static const char * const cci_i2c_groups[] = { + "gpio2", "gpio3", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", +}; +static const char * const gps_tx_groups[] = { + "gpio101", "gpio102", "gpio107", "gpio108", +}; +static const char * const gp_pdm0_groups[] = { + "gpio37", "gpio68", +}; +static const char * const gp_pdm1_groups[] = { + "gpio8", "gpio52", +}; +static const char * const gp_pdm2_groups[] = { + "gpio57", +}; +static const char * const jitter_bist_groups[] = { + "gpio90", +}; +static const char * const mclk_groups[] = { + "gpio93", +}; +static const char * const mdp_vsync_groups[] = { + "gpio6", "gpio23", "gpio24", "gpio27", "gpio28", +}; +static const char * const mss_lte_groups[] = { + "gpio65", "gpio66", +}; +static const char * const nav_pps_groups[] = { + "gpio101", "gpio101", "gpio102", "gpio102", +}; +static const char * const pll_bist_groups[] = { + "gpio27", +}; +static const char * const qlink0_wmss_groups[] = { + "gpio103", +}; +static const char * const qlink1_wmss_groups[] = { + "gpio106", +}; +static const char * const usb_phy_groups[] = { + "gpio124", +}; +static const char * const adsp_ext_groups[] = { + "gpio87", +}; +static const char * const atest_char_groups[] = { + "gpio95", +}; +static const char * const atest_char0_groups[] = { + "gpio96", +}; +static const char * const atest_char1_groups[] = { + "gpio97", +}; +static const char * const atest_char2_groups[] = { + "gpio98", +}; +static const char * const atest_char3_groups[] = { + "gpio99", +}; +static const char * const atest_tsens_groups[] = { + "gpio92", +}; +static const char * const atest_tsens2_groups[] = { + "gpio93", +}; +static const char * const atest_usb1_groups[] = { + "gpio83", +}; +static const char * const atest_usb10_groups[] = { + "gpio84", +}; +static const char * const atest_usb11_groups[] = { + "gpio85", +}; +static const char * const atest_usb12_groups[] = { + "gpio86", +}; +static const char * const atest_usb13_groups[] = { + "gpio87", +}; +static const char * const atest_usb2_groups[] = { + "gpio88", +}; +static const char * const atest_usb20_groups[] = { + "gpio89", +}; +static const char * const atest_usb21_groups[] = { + "gpio90", +}; +static const char * const atest_usb22_groups[] = { + "gpio91", +}; +static const char * const atest_usb23_groups[] = { + "gpio92", +}; +static const char * const audio_ref_groups[] = { + "gpio60", +}; +static const char * const btfm_slimbus_groups[] = { + "gpio67", "gpio68", "gpio86", "gpio87", +}; +static const char * const cam_mclk_groups[] = { + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", +}; +static const char * const cci_timer0_groups[] = { + "gpio34", +}; +static const char * const cci_timer1_groups[] = { + "gpio35", +}; +static const char * const cci_timer2_groups[] = { + "gpio36", +}; +static const char * const cci_timer3_groups[] = { + "gpio37", +}; +static const char * const cci_timer4_groups[] = { + "gpio38", +}; +static const char * const cri_trng_groups[] = { + "gpio0", "gpio1", "gpio2", +}; +static const char * const dbg_out_groups[] = { + "gpio3", +}; +static const char * const ddr_bist_groups[] = { + "gpio19", "gpio20", "gpio21", "gpio22", +}; +static const char * const ddr_pxi0_groups[] = { + "gpio86", "gpio90", +}; +static const char * const ddr_pxi1_groups[] = { + "gpio87", "gpio91", +}; +static const char * const ddr_pxi2_groups[] = { + "gpio88", "gpio92", +}; +static const char * const ddr_pxi3_groups[] = { + "gpio89", "gpio93", +}; +static const char * const dp_hot_groups[] = { + "gpio12", "gpio118", +}; +static const char * const edp_lcd_groups[] = { + "gpio23", +}; +static const char * const gcc_gp1_groups[] = { + "gpio48", "gpio58", +}; +static const char * const gcc_gp2_groups[] = { + "gpio21", +}; +static const char * const gcc_gp3_groups[] = { + "gpio22", +}; +static const char * const ibi_i3c_groups[] = { + "gpio0", "gpio1", +}; +static const char * const ldo_en_groups[] = { + "gpio95", +}; +static const char * const ldo_update_groups[] = { + "gpio96", +}; +static const char * const lpass_ext_groups[] = { + "gpio60", "gpio93", +}; +static const char * const m_voc_groups[] = { + "gpio12", +}; +static const char * const mdp_vsync0_groups[] = { + "gpio47", +}; +static const char * const mdp_vsync1_groups[] = { + "gpio48", +}; +static const char * const mdp_vsync2_groups[] = { + "gpio56", +}; +static const char * const mdp_vsync3_groups[] = { + "gpio57", +}; +static const char * const mi2s_0_groups[] = { + "gpio88", "gpio89", "gpio90", "gpio91", +}; +static const char * const mi2s_1_groups[] = { + "gpio67", "gpio68", "gpio86", "gpio87", +}; +static const char * const mi2s_2_groups[] = { + "gpio60", +}; +static const char * const nav_gpio_groups[] = { + "gpio101", "gpio102", +}; +static const char * const pa_indicator_groups[] = { + "gpio118", +}; +static const char * const phase_flag0_groups[] = { + "gpio12", +}; +static const char * const phase_flag1_groups[] = { + "gpio17", +}; +static const char * const phase_flag10_groups[] = { + "gpio41", +}; +static const char * const phase_flag11_groups[] = { + "gpio42", +}; +static const char * const phase_flag12_groups[] = { + "gpio43", +}; +static const char * const phase_flag13_groups[] = { + "gpio44", +}; +static const char * const phase_flag14_groups[] = { + "gpio45", +}; +static const char * const phase_flag15_groups[] = { + "gpio46", +}; +static const char * const phase_flag16_groups[] = { + "gpio47", +}; +static const char * const phase_flag17_groups[] = { + "gpio48", +}; +static const char * const phase_flag18_groups[] = { + "gpio49", +}; +static const char * const phase_flag19_groups[] = { + "gpio50", +}; +static const char * const phase_flag2_groups[] = { + "gpio18", +}; +static const char * const phase_flag20_groups[] = { + "gpio51", +}; +static const char * const phase_flag21_groups[] = { + "gpio52", +}; +static const char * const phase_flag22_groups[] = { + "gpio53", +}; +static const char * const phase_flag23_groups[] = { + "gpio56", +}; +static const char * const phase_flag24_groups[] = { + "gpio57", +}; +static const char * const phase_flag25_groups[] = { + "gpio60", +}; +static const char * const phase_flag26_groups[] = { + "gpio61", +}; +static const char * const phase_flag27_groups[] = { + "gpio62", +}; +static const char * const phase_flag28_groups[] = { + "gpio63", +}; +static const char * const phase_flag29_groups[] = { + "gpio64", +}; +static const char * const phase_flag3_groups[] = { + "gpio34", +}; +static const char * const phase_flag30_groups[] = { + "gpio67", +}; +static const char * const phase_flag31_groups[] = { + "gpio68", +}; +static const char * const phase_flag4_groups[] = { + "gpio35", +}; +static const char * const phase_flag5_groups[] = { + "gpio36", +}; +static const char * const phase_flag6_groups[] = { + "gpio37", +}; +static const char * const phase_flag7_groups[] = { + "gpio38", +}; +static const char * const phase_flag8_groups[] = { + "gpio39", +}; +static const char * const phase_flag9_groups[] = { + "gpio40", +}; +static const char * const pll_bypassnl_groups[] = { + "gpio13", +}; +static const char * const pll_clk_groups[] = { + "gpio98", +}; +static const char * const pll_reset_groups[] = { + "gpio14", +}; +static const char * const prng_rosc0_groups[] = { + "gpio97", +}; +static const char * const prng_rosc1_groups[] = { + "gpio98", +}; +static const char * const prng_rosc2_groups[] = { + "gpio99", +}; +static const char * const prng_rosc3_groups[] = { + "gpio100", +}; +static const char * const qdss_cti_groups[] = { + "gpio2", "gpio3", "gpio6", "gpio7", "gpio61", "gpio62", "gpio86", + "gpio87", +}; +static const char * const qdss_gpio_groups[] = { + "gpio8", "gpio9", "gpio63", "gpio64", +}; +static const char * const qdss_gpio0_groups[] = { + "gpio39", "gpio65", +}; +static const char * const qdss_gpio1_groups[] = { + "gpio40", "gpio66", +}; +static const char * const qdss_gpio10_groups[] = { + "gpio50", "gpio56", +}; +static const char * const qdss_gpio11_groups[] = { + "gpio51", "gpio57", +}; +static const char * const qdss_gpio12_groups[] = { + "gpio34", "gpio52", +}; +static const char * const qdss_gpio13_groups[] = { + "gpio35", "gpio53", +}; +static const char * const qdss_gpio14_groups[] = { + "gpio27", "gpio36", +}; +static const char * const qdss_gpio15_groups[] = { + "gpio28", "gpio37", +}; +static const char * const qdss_gpio2_groups[] = { + "gpio38", "gpio41", +}; +static const char * const qdss_gpio3_groups[] = { + "gpio42", "gpio47", +}; +static const char * const qdss_gpio4_groups[] = { + "gpio43", "gpio88", +}; +static const char * const qdss_gpio5_groups[] = { + "gpio44", "gpio89", +}; +static const char * const qdss_gpio6_groups[] = { + "gpio45", "gpio90", +}; +static const char * const qdss_gpio7_groups[] = { + "gpio46", "gpio91", +}; +static const char * const qdss_gpio8_groups[] = { + "gpio48", "gpio92", +}; +static const char * const qdss_gpio9_groups[] = { + "gpio49", "gpio93", +}; +static const char * const qlink0_enable_groups[] = { + "gpio105", +}; +static const char * const qlink0_request_groups[] = { + "gpio104", +}; +static const char * const qlink1_enable_groups[] = { + "gpio108", +}; +static const char * const qlink1_request_groups[] = { + "gpio107", +}; +static const char * const qup00_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; +static const char * const qup01_groups[] = { + "gpio61", "gpio62", "gpio63", "gpio64", +}; +static const char * const qup02_groups[] = { + "gpio45", "gpio46", "gpio48", "gpio56", "gpio57", +}; +static const char * const qup10_groups[] = { + "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", +}; +static const char * const qup11_f1_groups[] = { + "gpio27", "gpio28", +}; +static const char * const qup11_f2_groups[] = { + "gpio27", "gpio28", +}; + +static const char * const qup12_groups[] = { + "gpio19", "gpio19", "gpio20", "gpio20", +}; +static const char * const qup13_f1_groups[] = { + "gpio25", "gpio26", +}; +static const char * const qup13_f2_groups[] = { + "gpio25", "gpio26", +}; +static const char * const qup14_groups[] = { + "gpio4", "gpio4", "gpio5", "gpio5", +}; +static const char * const sd_write_groups[] = { + "gpio85", +}; +static const char * const sdc1_tb_groups[] = { + "gpio4", +}; +static const char * const sdc2_tb_groups[] = { + "gpio5", +}; +static const char * const sp_cmu_groups[] = { + "gpio3", +}; +static const char * const tgu_ch0_groups[] = { + "gpio61", +}; +static const char * const tgu_ch1_groups[] = { + "gpio62", +}; +static const char * const tgu_ch2_groups[] = { + "gpio63", +}; +static const char * const tgu_ch3_groups[] = { + "gpio64", +}; +static const char * const tsense_pwm1_groups[] = { + "gpio88", +}; +static const char * const tsense_pwm2_groups[] = { + "gpio88", +}; +static const char * const uim1_clk_groups[] = { + "gpio80", +}; +static const char * const uim1_data_groups[] = { + "gpio79", +}; +static const char * const uim1_present_groups[] = { + "gpio82", +}; +static const char * const uim1_reset_groups[] = { + "gpio81", +}; +static const char * const uim2_clk_groups[] = { + "gpio76", +}; +static const char * const uim2_data_groups[] = { + "gpio75", +}; +static const char * const uim2_present_groups[] = { + "gpio78", +}; +static const char * const uim2_reset_groups[] = { + "gpio77", +}; +static const char * const usb2phy_ac_groups[] = { + "gpio47", +}; +static const char * const vfr_1_groups[] = { + "gpio49", +}; +static const char * const vsense_trigger_groups[] = { + "gpio89", +}; +static const char * const wlan1_adc0_groups[] = { + "gpio90", +}; +static const char * const wlan1_adc1_groups[] = { + "gpio92", +}; +static const char * const wlan2_adc0_groups[] = { + "gpio91", +}; +static const char * const wlan2_adc1_groups[] = { + "gpio93", +}; + +static const struct msm_function sm6375_functions[] = { + FUNCTION(adsp_ext), + FUNCTION(agera_pll), + FUNCTION(atest_char), + FUNCTION(atest_char0), + FUNCTION(atest_char1), + FUNCTION(atest_char2), + FUNCTION(atest_char3), + FUNCTION(atest_tsens), + FUNCTION(atest_tsens2), + FUNCTION(atest_usb1), + FUNCTION(atest_usb10), + FUNCTION(atest_usb11), + FUNCTION(atest_usb12), + FUNCTION(atest_usb13), + FUNCTION(atest_usb2), + FUNCTION(atest_usb20), + FUNCTION(atest_usb21), + FUNCTION(atest_usb22), + FUNCTION(atest_usb23), + FUNCTION(audio_ref), + FUNCTION(btfm_slimbus), + FUNCTION(cam_mclk), + FUNCTION(cci_async), + FUNCTION(cci_i2c), + FUNCTION(cci_timer0), + FUNCTION(cci_timer1), + FUNCTION(cci_timer2), + FUNCTION(cci_timer3), + FUNCTION(cci_timer4), + FUNCTION(cri_trng), + FUNCTION(dbg_out), + FUNCTION(ddr_bist), + FUNCTION(ddr_pxi0), + FUNCTION(ddr_pxi1), + FUNCTION(ddr_pxi2), + FUNCTION(ddr_pxi3), + FUNCTION(dp_hot), + FUNCTION(edp_lcd), + FUNCTION(gcc_gp1), + FUNCTION(gcc_gp2), + FUNCTION(gcc_gp3), + FUNCTION(gp_pdm0), + FUNCTION(gp_pdm1), + FUNCTION(gp_pdm2), + FUNCTION(gpio), + FUNCTION(gps_tx), + FUNCTION(ibi_i3c), + FUNCTION(jitter_bist), + FUNCTION(ldo_en), + FUNCTION(ldo_update), + FUNCTION(lpass_ext), + FUNCTION(m_voc), + FUNCTION(mclk), + FUNCTION(mdp_vsync), + FUNCTION(mdp_vsync0), + FUNCTION(mdp_vsync1), + FUNCTION(mdp_vsync2), + FUNCTION(mdp_vsync3), + FUNCTION(mi2s_0), + FUNCTION(mi2s_1), + FUNCTION(mi2s_2), + FUNCTION(mss_lte), + FUNCTION(nav_gpio), + FUNCTION(nav_pps), + FUNCTION(pa_indicator), + FUNCTION(phase_flag0), + FUNCTION(phase_flag1), + FUNCTION(phase_flag10), + FUNCTION(phase_flag11), + FUNCTION(phase_flag12), + FUNCTION(phase_flag13), + FUNCTION(phase_flag14), + FUNCTION(phase_flag15), + FUNCTION(phase_flag16), + FUNCTION(phase_flag17), + FUNCTION(phase_flag18), + FUNCTION(phase_flag19), + FUNCTION(phase_flag2), + FUNCTION(phase_flag20), + FUNCTION(phase_flag21), + FUNCTION(phase_flag22), + FUNCTION(phase_flag23), + FUNCTION(phase_flag24), + FUNCTION(phase_flag25), + FUNCTION(phase_flag26), + FUNCTION(phase_flag27), + FUNCTION(phase_flag28), + FUNCTION(phase_flag29), + FUNCTION(phase_flag3), + FUNCTION(phase_flag30), + FUNCTION(phase_flag31), + FUNCTION(phase_flag4), + FUNCTION(phase_flag5), + FUNCTION(phase_flag6), + FUNCTION(phase_flag7), + FUNCTION(phase_flag8), + FUNCTION(phase_flag9), + FUNCTION(pll_bist), + FUNCTION(pll_bypassnl), + FUNCTION(pll_clk), + FUNCTION(pll_reset), + FUNCTION(prng_rosc0), + FUNCTION(prng_rosc1), + FUNCTION(prng_rosc2), + FUNCTION(prng_rosc3), + FUNCTION(qdss_cti), + FUNCTION(qdss_gpio), + FUNCTION(qdss_gpio0), + FUNCTION(qdss_gpio1), + FUNCTION(qdss_gpio10), + FUNCTION(qdss_gpio11), + FUNCTION(qdss_gpio12), + FUNCTION(qdss_gpio13), + FUNCTION(qdss_gpio14), + FUNCTION(qdss_gpio15), + FUNCTION(qdss_gpio2), + FUNCTION(qdss_gpio3), + FUNCTION(qdss_gpio4), + FUNCTION(qdss_gpio5), + FUNCTION(qdss_gpio6), + FUNCTION(qdss_gpio7), + FUNCTION(qdss_gpio8), + FUNCTION(qdss_gpio9), + FUNCTION(qlink0_enable), + FUNCTION(qlink0_request), + FUNCTION(qlink0_wmss), + FUNCTION(qlink1_enable), + FUNCTION(qlink1_request), + FUNCTION(qlink1_wmss), + FUNCTION(qup00), + FUNCTION(qup01), + FUNCTION(qup02), + FUNCTION(qup10), + FUNCTION(qup11_f1), + FUNCTION(qup11_f2), + FUNCTION(qup12), + FUNCTION(qup13_f1), + FUNCTION(qup13_f2), + FUNCTION(qup14), + FUNCTION(sd_write), + FUNCTION(sdc1_tb), + FUNCTION(sdc2_tb), + FUNCTION(sp_cmu), + FUNCTION(tgu_ch0), + FUNCTION(tgu_ch1), + FUNCTION(tgu_ch2), + FUNCTION(tgu_ch3), + FUNCTION(tsense_pwm1), + FUNCTION(tsense_pwm2), + FUNCTION(uim1_clk), + FUNCTION(uim1_data), + FUNCTION(uim1_present), + FUNCTION(uim1_reset), + FUNCTION(uim2_clk), + FUNCTION(uim2_data), + FUNCTION(uim2_present), + FUNCTION(uim2_reset), + FUNCTION(usb2phy_ac), + FUNCTION(usb_phy), + FUNCTION(vfr_1), + FUNCTION(vsense_trigger), + FUNCTION(wlan1_adc0), + FUNCTION(wlan1_adc1), + FUNCTION(wlan2_adc0), + FUNCTION(wlan2_adc1), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sm6375_groups[] = { + [0] = PINGROUP(0, ibi_i3c, qup00, cri_trng, _, _, _, _, _, _), + [1] = PINGROUP(1, ibi_i3c, qup00, cri_trng, _, _, _, _, _, _), + [2] = PINGROUP(2, qup00, cci_i2c, cri_trng, qdss_cti, _, _, _, _, _), + [3] = PINGROUP(3, qup00, cci_i2c, sp_cmu, dbg_out, qdss_cti, _, _, _, _), + [4] = PINGROUP(4, qup14, qup14, sdc1_tb, _, _, _, _, _, _), + [5] = PINGROUP(5, qup14, qup14, sdc2_tb, _, _, _, _, _, _), + [6] = PINGROUP(6, mdp_vsync, qdss_cti, _, _, _, _, _, _, _), + [7] = PINGROUP(7, qdss_cti, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, gp_pdm1, qdss_gpio, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qdss_gpio, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, _, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, _, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, m_voc, dp_hot, _, phase_flag0, _, _, _, _, _), + [13] = PINGROUP(13, qup10, pll_bypassnl, _, _, _, _, _, _, _), + [14] = PINGROUP(14, qup10, pll_reset, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup10, _, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup10, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, _, phase_flag1, qup10, _, _, _, _, _, _), + [18] = PINGROUP(18, _, phase_flag2, _, _, _, _, _, _, _), + [19] = PINGROUP(19, qup12, qup12, ddr_bist, _, _, _, _, _, _), + [20] = PINGROUP(20, qup12, qup12, ddr_bist, _, _, _, _, _, _), + [21] = PINGROUP(21, gcc_gp2, ddr_bist, _, _, _, _, _, _, _), + [22] = PINGROUP(22, gcc_gp3, ddr_bist, _, _, _, _, _, _, _), + [23] = PINGROUP(23, mdp_vsync, edp_lcd, _, _, _, _, _, _, _), + [24] = PINGROUP(24, mdp_vsync, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup13_f1, qup13_f2, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup13_f1, qup13_f2, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup11_f1, qup11_f2, mdp_vsync, pll_bist, _, qdss_gpio14, _, _, _), + [28] = PINGROUP(28, qup11_f1, qup11_f2, mdp_vsync, _, qdss_gpio15, _, _, _, _), + [29] = PINGROUP(29, cam_mclk, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, cam_mclk, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, cam_mclk, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, cam_mclk, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, cam_mclk, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, cci_timer0, _, phase_flag3, qdss_gpio12, _, _, _, _, _), + [35] = PINGROUP(35, cci_timer1, cci_async, _, phase_flag4, qdss_gpio13, _, _, _, _), + [36] = PINGROUP(36, cci_timer2, cci_async, _, phase_flag5, qdss_gpio14, _, _, _, _), + [37] = PINGROUP(37, cci_timer3, gp_pdm0, _, phase_flag6, qdss_gpio15, _, _, _, _), + [38] = PINGROUP(38, cci_timer4, _, phase_flag7, qdss_gpio2, _, _, _, _, _), + [39] = PINGROUP(39, cci_i2c, _, phase_flag8, qdss_gpio0, _, _, _, _, _), + [40] = PINGROUP(40, cci_i2c, _, phase_flag9, qdss_gpio1, _, _, _, _, _), + [41] = PINGROUP(41, cci_i2c, _, phase_flag10, qdss_gpio2, _, _, _, _, _), + [42] = PINGROUP(42, cci_i2c, _, phase_flag11, qdss_gpio3, _, _, _, _, _), + [43] = PINGROUP(43, cci_i2c, _, phase_flag12, qdss_gpio4, _, _, _, _, _), + [44] = PINGROUP(44, cci_i2c, _, phase_flag13, qdss_gpio5, _, _, _, _, _), + [45] = PINGROUP(45, qup02, _, phase_flag14, qdss_gpio6, _, _, _, _, _), + [46] = PINGROUP(46, qup02, _, phase_flag15, qdss_gpio7, _, _, _, _, _), + [47] = PINGROUP(47, mdp_vsync0, _, phase_flag16, qdss_gpio3, _, _, usb2phy_ac, _, _), + [48] = PINGROUP(48, cci_async, mdp_vsync1, gcc_gp1, _, phase_flag17, qdss_gpio8, qup02, + _, _), + [49] = PINGROUP(49, vfr_1, _, phase_flag18, qdss_gpio9, _, _, _, _, _), + [50] = PINGROUP(50, _, phase_flag19, qdss_gpio10, _, _, _, _, _, _), + [51] = PINGROUP(51, _, phase_flag20, qdss_gpio11, _, _, _, _, _, _), + [52] = PINGROUP(52, cci_async, gp_pdm1, _, phase_flag21, qdss_gpio12, _, _, _, _), + [53] = PINGROUP(53, cci_async, _, phase_flag22, qdss_gpio13, _, _, _, _, _), + [54] = PINGROUP(54, _, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, _, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, qup02, mdp_vsync2, _, phase_flag23, qdss_gpio10, _, _, _, _), + [57] = PINGROUP(57, qup02, mdp_vsync3, gp_pdm2, _, phase_flag24, qdss_gpio11, _, _, _), + [58] = PINGROUP(58, gcc_gp1, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, _, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, audio_ref, lpass_ext, mi2s_2, _, phase_flag25, _, _, _, _), + [61] = PINGROUP(61, qup01, tgu_ch0, _, phase_flag26, qdss_cti, _, _, _, _), + [62] = PINGROUP(62, qup01, tgu_ch1, _, phase_flag27, qdss_cti, _, _, _, _), + [63] = PINGROUP(63, qup01, tgu_ch2, _, phase_flag28, qdss_gpio, _, _, _, _), + [64] = PINGROUP(64, qup01, tgu_ch3, _, phase_flag29, qdss_gpio, _, _, _, _), + [65] = PINGROUP(65, mss_lte, _, qdss_gpio0, _, _, _, _, _, _), + [66] = PINGROUP(66, mss_lte, _, qdss_gpio1, _, _, _, _, _, _), + [67] = PINGROUP(67, btfm_slimbus, mi2s_1, _, phase_flag30, _, _, _, _, _), + [68] = PINGROUP(68, btfm_slimbus, mi2s_1, gp_pdm0, _, phase_flag31, _, _, _, _), + [69] = PINGROUP(69, _, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, _, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, _, _, _, _, _, _, _, _, _), + [72] = PINGROUP(72, _, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, _, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, _, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, uim2_data, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, uim2_clk, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, uim2_reset, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, uim2_present, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, uim1_data, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, uim1_clk, _, _, _, _, _, _, _, _), + [81] = PINGROUP(81, uim1_reset, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, uim1_present, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, atest_usb1, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, _, atest_usb10, _, _, _, _, _, _, _), + [85] = PINGROUP(85, sd_write, _, atest_usb11, _, _, _, _, _, _), + [86] = PINGROUP(86, btfm_slimbus, mi2s_1, _, qdss_cti, atest_usb12, ddr_pxi0, _, _, _), + [87] = PINGROUP(87, btfm_slimbus, mi2s_1, adsp_ext, _, qdss_cti, atest_usb13, ddr_pxi1, _, + _), + [88] = PINGROUP(88, mi2s_0, _, qdss_gpio4, _, atest_usb2, ddr_pxi2, tsense_pwm1, + tsense_pwm2, _), + [89] = PINGROUP(89, mi2s_0, agera_pll, _, qdss_gpio5, _, vsense_trigger, atest_usb20, + ddr_pxi3, _), + [90] = PINGROUP(90, mi2s_0, jitter_bist, _, qdss_gpio6, _, wlan1_adc0, atest_usb21, + ddr_pxi0, _), + [91] = PINGROUP(91, mi2s_0, _, qdss_gpio7, _, wlan2_adc0, atest_usb22, ddr_pxi1, _, _), + [92] = PINGROUP(92, _, qdss_gpio8, atest_tsens, wlan1_adc1, atest_usb23, ddr_pxi2, _, _, + _), + [93] = PINGROUP(93, mclk, lpass_ext, _, qdss_gpio9, atest_tsens2, wlan2_adc1, ddr_pxi3, + _, _), + [94] = PINGROUP(94, _, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, ldo_en, _, atest_char, _, _, _, _, _, _), + [96] = PINGROUP(96, ldo_update, _, atest_char0, _, _, _, _, _, _), + [97] = PINGROUP(97, prng_rosc0, _, atest_char1, _, _, _, _, _, _), + [98] = PINGROUP(98, _, atest_char2, _, _, prng_rosc1, pll_clk, _, _, _), + [99] = PINGROUP(99, _, atest_char3, _, _, prng_rosc2, _, _, _, _), + [100] = PINGROUP(100, _, _, prng_rosc3, _, _, _, _, _, _), + [101] = PINGROUP(101, nav_gpio, nav_pps, nav_pps, gps_tx, _, _, _, _, _), + [102] = PINGROUP(102, nav_gpio, nav_pps, nav_pps, gps_tx, _, _, _, _, _), + [103] = PINGROUP(103, qlink0_wmss, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, qlink0_request, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, qlink0_enable, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, qlink1_wmss, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, qlink1_request, gps_tx, _, _, _, _, _, _, _), + [108] = PINGROUP(108, qlink1_enable, gps_tx, _, _, _, _, _, _, _), + [109] = PINGROUP(109, _, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, _, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, _, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, _, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, _, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, _, _, pa_indicator, dp_hot, _, _, _, _, _), + [119] = PINGROUP(119, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, usb_phy, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, _, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, _, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, _, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, _, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, _, _, _, _, _, _, _, _, _), + [152] = PINGROUP(152, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, _, _, _, _, _, _, _, _, _), + [156] = UFS_RESET(ufs_reset, 0x1ae000), + [157] = SDC_PINGROUP(sdc1_rclk, 0x1a1000, 0, 0), + [158] = SDC_PINGROUP(sdc1_clk, 0x1a0000, 13, 6), + [159] = SDC_PINGROUP(sdc1_cmd, 0x1a0000, 11, 3), + [160] = SDC_PINGROUP(sdc1_data, 0x1a0000, 9, 0), + [161] = SDC_PINGROUP(sdc2_clk, 0x1a2000, 14, 6), + [162] = SDC_PINGROUP(sdc2_cmd, 0x1a2000, 11, 3), + [163] = SDC_PINGROUP(sdc2_data, 0x1a2000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map sm6375_mpm_map[] = { + { 0, 84 }, { 3, 6 }, { 4, 7 }, { 7, 8 }, { 8, 9 }, { 9, 10 }, { 11, 11 }, { 12, 13 }, + { 13, 14 }, { 16, 16 }, { 17, 17 }, { 18, 18 }, { 19, 19 }, { 21, 20 }, { 22, 21 }, + { 23, 23 }, { 24, 24 }, { 25, 25 }, { 27, 26 }, { 28, 27 }, { 37, 28 }, { 38, 29 }, + { 48, 30 }, { 50, 31 }, { 51, 32 }, { 52, 33 }, { 57, 34 }, { 59, 35 }, { 60, 37 }, + { 61, 38 }, { 62, 39 }, { 64, 40 }, { 66, 41 }, { 67, 42 }, { 68, 43 }, { 69, 44 }, + { 78, 45 }, { 82, 36 }, { 83, 47 }, { 84, 48 }, { 85, 49 }, { 87, 50 }, { 88, 51 }, + { 91, 52 }, { 94, 53 }, { 95, 54 }, { 96, 55 }, { 97, 56 }, { 98, 57 }, { 99, 58 }, + { 100, 59 }, { 104, 60 }, { 107, 61 }, { 118, 62 }, { 124, 63 }, { 125, 64 }, { 126, 65 }, + { 128, 66 }, { 129, 67 }, { 131, 69 }, { 133, 70 }, { 134, 71 }, { 136, 73 }, { 142, 74 }, + { 150, 75 }, { 153, 76 }, { 155, 77 }, +}; + +static const struct msm_pinctrl_soc_data sm6375_tlmm = { + .pins = sm6375_pins, + .npins = ARRAY_SIZE(sm6375_pins), + .functions = sm6375_functions, + .nfunctions = ARRAY_SIZE(sm6375_functions), + .groups = sm6375_groups, + .ngroups = ARRAY_SIZE(sm6375_groups), + .ngpios = 157, + .wakeirq_map = sm6375_mpm_map, + .nwakeirq_map = ARRAY_SIZE(sm6375_mpm_map), +}; + +static int sm6375_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sm6375_tlmm); +} + +static const struct of_device_id sm6375_tlmm_of_match[] = { + { .compatible = "qcom,sm6375-tlmm", }, + { }, +}; + +static struct platform_driver sm6375_tlmm_driver = { + .driver = { + .name = "sm6375-tlmm", + .of_match_table = sm6375_tlmm_of_match, + }, + .probe = sm6375_tlmm_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init sm6375_tlmm_init(void) +{ + return platform_driver_register(&sm6375_tlmm_driver); +} +arch_initcall(sm6375_tlmm_init); + +static void __exit sm6375_tlmm_exit(void) +{ + platform_driver_unregister(&sm6375_tlmm_driver); +} +module_exit(sm6375_tlmm_exit); + +MODULE_DESCRIPTION("QTI SM6375 TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, sm6375_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index af144e724bd9..3bd7f9fedcc3 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -1316,7 +1316,7 @@ static const struct msm_pingroup sm8250_groups[] = { static const struct msm_gpio_wakeirq_map sm8250_pdc_map[] = { { 0, 79 }, { 1, 84 }, { 2, 80 }, { 3, 82 }, { 4, 107 }, { 7, 43 }, { 11, 42 }, { 14, 44 }, { 15, 52 }, { 19, 67 }, { 23, 68 }, { 24, 105 }, - { 27, 92 }, { 28, 106 }, { 31, 69 }, { 35, 70 }, { 39, 37 }, + { 27, 92 }, { 28, 106 }, { 31, 69 }, { 35, 70 }, { 39, 73 }, { 40, 108 }, { 43, 71 }, { 45, 72 }, { 47, 83 }, { 51, 74 }, { 55, 77 }, { 59, 78 }, { 63, 75 }, { 64, 81 }, { 65, 87 }, { 66, 88 }, { 67, 89 }, { 68, 54 }, { 70, 85 }, { 77, 46 }, { 80, 90 }, { 81, 91 }, { 83, 97 }, diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 3be2a08ae3a6..ccaf40a9c0e6 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -1159,6 +1159,7 @@ static const struct of_device_id pmic_gpio_of_match[] = { /* pm8150l has 12 GPIOs with holes on 7 */ { .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 }, { .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pm8226-gpio", .data = (void *) 8 }, { .compatible = "qcom,pm8350-gpio", .data = (void *) 10 }, { .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 }, { .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 }, @@ -1175,6 +1176,8 @@ static const struct of_device_id pmic_gpio_of_match[] = { { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, { .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 }, { .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 }, + /* pmp8074 has 12 GPIOs with holes on 1 and 12 */ + { .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 }, { .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 }, { .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 }, /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig index 961007ce7b3a..0903a0a41831 100644 --- a/drivers/pinctrl/renesas/Kconfig +++ b/drivers/pinctrl/renesas/Kconfig @@ -38,7 +38,9 @@ config PINCTRL_RENESAS select PINCTRL_PFC_R8A77995 if ARCH_R8A77995 select PINCTRL_PFC_R8A779A0 if ARCH_R8A779A0 select PINCTRL_PFC_R8A779F0 if ARCH_R8A779F0 + select PINCTRL_PFC_R8A779G0 if ARCH_R8A779G0 select PINCTRL_RZG2L if ARCH_RZG2L + select PINCTRL_RZV2M if ARCH_R9A09G011 select PINCTRL_PFC_SH7203 if CPU_SUBTYPE_SH7203 select PINCTRL_PFC_SH7264 if CPU_SUBTYPE_SH7264 select PINCTRL_PFC_SH7269 if CPU_SUBTYPE_SH7269 @@ -153,6 +155,10 @@ config PINCTRL_PFC_R8A779A0 bool "pin control support for R-Car V3U" if COMPILE_TEST select PINCTRL_SH_PFC +config PINCTRL_PFC_R8A779G0 + bool "pin control support for R-Car V4H" if COMPILE_TEST + select PINCTRL_SH_PFC + config PINCTRL_PFC_R8A7740 bool "pin control support for R-Mobile A1" if COMPILE_TEST select PINCTRL_SH_PFC_GPIO @@ -237,6 +243,18 @@ config PINCTRL_RZN1 help This selects pinctrl driver for Renesas RZ/N1 devices. +config PINCTRL_RZV2M + bool "pin control support for RZ/V2M" + depends on OF + depends on ARCH_R9A09G011 || COMPILE_TEST + select GPIOLIB + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + help + This selects GPIO and pinctrl driver for Renesas RZ/V2M + platforms. + config PINCTRL_PFC_SH7203 bool "pin control support for SH7203" if COMPILE_TEST select PINCTRL_SH_FUNC_GPIO diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile index 5d936c154a6f..558b30ce0dec 100644 --- a/drivers/pinctrl/renesas/Makefile +++ b/drivers/pinctrl/renesas/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PINCTRL_PFC_R8A77990) += pfc-r8a77990.o obj-$(CONFIG_PINCTRL_PFC_R8A77995) += pfc-r8a77995.o obj-$(CONFIG_PINCTRL_PFC_R8A779A0) += pfc-r8a779a0.o obj-$(CONFIG_PINCTRL_PFC_R8A779F0) += pfc-r8a779f0.o +obj-$(CONFIG_PINCTRL_PFC_R8A779G0) += pfc-r8a779g0.o obj-$(CONFIG_PINCTRL_PFC_SH7203) += pfc-sh7203.o obj-$(CONFIG_PINCTRL_PFC_SH7264) += pfc-sh7264.o obj-$(CONFIG_PINCTRL_PFC_SH7269) += pfc-sh7269.o @@ -49,6 +50,7 @@ obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o obj-$(CONFIG_PINCTRL_RZA2) += pinctrl-rza2.o obj-$(CONFIG_PINCTRL_RZG2L) += pinctrl-rzg2l.o obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o +obj-$(CONFIG_PINCTRL_RZV2M) += pinctrl-rzv2m.o ifeq ($(CONFIG_COMPILE_TEST),y) CFLAGS_pfc-sh7203.o += -I$(srctree)/arch/sh/include/cpu-sh2a diff --git a/drivers/pinctrl/renesas/core.c b/drivers/pinctrl/renesas/core.c index 8c14b2021bf0..c91102d3f1d1 100644 --- a/drivers/pinctrl/renesas/core.c +++ b/drivers/pinctrl/renesas/core.c @@ -644,6 +644,12 @@ static const struct of_device_id sh_pfc_of_table[] = { .data = &r8a779f0_pinmux_info, }, #endif +#ifdef CONFIG_PINCTRL_PFC_R8A779G0 + { + .compatible = "renesas,pfc-r8a779g0", + .data = &r8a779g0_pinmux_info, + }, +#endif #ifdef CONFIG_PINCTRL_PFC_SH73A0 { .compatible = "renesas,pfc-sh73a0", diff --git a/drivers/pinctrl/renesas/pfc-r8a779f0.c b/drivers/pinctrl/renesas/pfc-r8a779f0.c index aaca4ee2af55..417c357f16b1 100644 --- a/drivers/pinctrl/renesas/pfc-r8a779f0.c +++ b/drivers/pinctrl/renesas/pfc-r8a779f0.c @@ -1902,7 +1902,6 @@ static const struct pinmux_drive_reg pinmux_drive_regs[] = { enum ioctrl_regs { POC0, POC1, - POC2, POC3, TD0SEL1, }; @@ -1910,7 +1909,6 @@ enum ioctrl_regs { static const struct pinmux_ioctrl_reg pinmux_ioctrl_regs[] = { [POC0] = { 0xe60500a0, }, [POC1] = { 0xe60508a0, }, - [POC2] = { 0xe60510a0, }, [POC3] = { 0xe60518a0, }, [TD0SEL1] = { 0xe6050920, }, { /* sentinel */ }, diff --git a/drivers/pinctrl/renesas/pfc-r8a779g0.c b/drivers/pinctrl/renesas/pfc-r8a779g0.c new file mode 100644 index 000000000000..5dd1c2c7708a --- /dev/null +++ b/drivers/pinctrl/renesas/pfc-r8a779g0.c @@ -0,0 +1,4262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R8A779A0 processor support - PFC hardware block. + * + * Copyright (C) 2021 Renesas Electronics Corp. + * + * This file is based on the drivers/pinctrl/renesas/pfc-r8a779a0.c + */ + +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/kernel.h> + +#include "sh_pfc.h" + +#define CFG_FLAGS (SH_PFC_PIN_CFG_DRIVE_STRENGTH | SH_PFC_PIN_CFG_PULL_UP_DOWN) + +#define CPU_ALL_GP(fn, sfx) \ + PORT_GP_CFG_19(0, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE_18_33), \ + PORT_GP_CFG_23(1, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE_18_33), \ + PORT_GP_CFG_1(1, 23, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(1, 24, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(1, 25, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(1, 26, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(1, 27, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(1, 28, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_20(2, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_13(3, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE_18_33), \ + PORT_GP_CFG_1(3, 13, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 14, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 15, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 16, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 17, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 18, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 19, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 20, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 21, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 22, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 23, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 24, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 25, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 26, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 27, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 28, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(3, 29, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_25(4, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_21(5, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_21(6, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_21(7, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_14(8, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE_18_33) + +/* GPSR0 */ +#define GPSR0_18 F_(MSIOF2_RXD, IP2SR0_11_8) +#define GPSR0_17 F_(MSIOF2_SCK, IP2SR0_7_4) +#define GPSR0_16 F_(MSIOF2_TXD, IP2SR0_3_0) +#define GPSR0_15 F_(MSIOF2_SYNC, IP1SR0_31_28) +#define GPSR0_14 F_(MSIOF2_SS1, IP1SR0_27_24) +#define GPSR0_13 F_(MSIOF2_SS2, IP1SR0_23_20) +#define GPSR0_12 F_(MSIOF5_RXD, IP1SR0_19_16) +#define GPSR0_11 F_(MSIOF5_SCK, IP1SR0_15_12) +#define GPSR0_10 F_(MSIOF5_TXD, IP1SR0_11_8) +#define GPSR0_9 F_(MSIOF5_SYNC, IP1SR0_7_4) +#define GPSR0_8 F_(MSIOF5_SS1, IP1SR0_3_0) +#define GPSR0_7 F_(MSIOF5_SS2, IP0SR0_31_28) +#define GPSR0_6 F_(IRQ0, IP0SR0_27_24) +#define GPSR0_5 F_(IRQ1, IP0SR0_23_20) +#define GPSR0_4 F_(IRQ2, IP0SR0_19_16) +#define GPSR0_3 F_(IRQ3, IP0SR0_15_12) +#define GPSR0_2 F_(GP0_02, IP0SR0_11_8) +#define GPSR0_1 F_(GP0_01, IP0SR0_7_4) +#define GPSR0_0 F_(GP0_00, IP0SR0_3_0) + +/* GPSR1 */ +#define GPSR1_28 F_(HTX3, IP3SR1_19_16) +#define GPSR1_27 F_(HCTS3_N, IP3SR1_15_12) +#define GPSR1_26 F_(HRTS3_N, IP3SR1_11_8) +#define GPSR1_25 F_(HSCK3, IP3SR1_7_4) +#define GPSR1_24 F_(HRX3, IP3SR1_3_0) +#define GPSR1_23 F_(GP1_23, IP2SR1_31_28) +#define GPSR1_22 F_(AUDIO_CLKIN, IP2SR1_27_24) +#define GPSR1_21 F_(AUDIO_CLKOUT, IP2SR1_23_20) +#define GPSR1_20 F_(SSI_SD, IP2SR1_19_16) +#define GPSR1_19 F_(SSI_WS, IP2SR1_15_12) +#define GPSR1_18 F_(SSI_SCK, IP2SR1_11_8) +#define GPSR1_17 F_(SCIF_CLK, IP2SR1_7_4) +#define GPSR1_16 F_(HRX0, IP2SR1_3_0) +#define GPSR1_15 F_(HSCK0, IP1SR1_31_28) +#define GPSR1_14 F_(HRTS0_N, IP1SR1_27_24) +#define GPSR1_13 F_(HCTS0_N, IP1SR1_23_20) +#define GPSR1_12 F_(HTX0, IP1SR1_19_16) +#define GPSR1_11 F_(MSIOF0_RXD, IP1SR1_15_12) +#define GPSR1_10 F_(MSIOF0_SCK, IP1SR1_11_8) +#define GPSR1_9 F_(MSIOF0_TXD, IP1SR1_7_4) +#define GPSR1_8 F_(MSIOF0_SYNC, IP1SR1_3_0) +#define GPSR1_7 F_(MSIOF0_SS1, IP0SR1_31_28) +#define GPSR1_6 F_(MSIOF0_SS2, IP0SR1_27_24) +#define GPSR1_5 F_(MSIOF1_RXD, IP0SR1_23_20) +#define GPSR1_4 F_(MSIOF1_TXD, IP0SR1_19_16) +#define GPSR1_3 F_(MSIOF1_SCK, IP0SR1_15_12) +#define GPSR1_2 F_(MSIOF1_SYNC, IP0SR1_11_8) +#define GPSR1_1 F_(MSIOF1_SS1, IP0SR1_7_4) +#define GPSR1_0 F_(MSIOF1_SS2, IP0SR1_3_0) + +/* GPSR2 */ +#define GPSR2_19 F_(CANFD7_RX, IP2SR2_15_12) +#define GPSR2_18 F_(CANFD7_TX, IP2SR2_11_8) +#define GPSR2_17 F_(CANFD4_RX, IP2SR2_7_4) +#define GPSR2_16 F_(CANFD4_TX, IP2SR2_3_0) +#define GPSR2_15 F_(CANFD3_RX, IP1SR2_31_28) +#define GPSR2_14 F_(CANFD3_TX, IP1SR2_27_24) +#define GPSR2_13 F_(CANFD2_RX, IP1SR2_23_20) +#define GPSR2_12 F_(CANFD2_TX, IP1SR2_19_16) +#define GPSR2_11 F_(CANFD0_RX, IP1SR2_15_12) +#define GPSR2_10 F_(CANFD0_TX, IP1SR2_11_8) +#define GPSR2_9 F_(CAN_CLK, IP1SR2_7_4) +#define GPSR2_8 F_(TPU0TO0, IP1SR2_3_0) +#define GPSR2_7 F_(TPU0TO1, IP0SR2_31_28) +#define GPSR2_6 F_(FXR_TXDB, IP0SR2_27_24) +#define GPSR2_5 F_(FXR_TXENB_N, IP0SR2_23_20) +#define GPSR2_4 F_(RXDB_EXTFXR, IP0SR2_19_16) +#define GPSR2_3 F_(CLK_EXTFXR, IP0SR2_15_12) +#define GPSR2_2 F_(RXDA_EXTFXR, IP0SR2_11_8) +#define GPSR2_1 F_(FXR_TXENA_N, IP0SR2_7_4) +#define GPSR2_0 F_(FXR_TXDA, IP0SR2_3_0) + +/* GPSR3 */ +#define GPSR3_29 F_(RPC_INT_N, IP3SR3_23_20) +#define GPSR3_28 F_(RPC_WP_N, IP3SR3_19_16) +#define GPSR3_27 F_(RPC_RESET_N, IP3SR3_15_12) +#define GPSR3_26 F_(QSPI1_IO3, IP3SR3_11_8) +#define GPSR3_25 F_(QSPI1_SSL, IP3SR3_7_4) +#define GPSR3_24 F_(QSPI1_IO2, IP3SR3_3_0) +#define GPSR3_23 F_(QSPI1_MISO_IO1, IP2SR3_31_28) +#define GPSR3_22 F_(QSPI1_SPCLK, IP2SR3_27_24) +#define GPSR3_21 F_(QSPI1_MOSI_IO0, IP2SR3_23_20) +#define GPSR3_20 F_(QSPI0_SPCLK, IP2SR3_19_16) +#define GPSR3_19 F_(QSPI0_MOSI_IO0, IP2SR3_15_12) +#define GPSR3_18 F_(QSPI0_MISO_IO1, IP2SR3_11_8) +#define GPSR3_17 F_(QSPI0_IO2, IP2SR3_7_4) +#define GPSR3_16 F_(QSPI0_IO3, IP2SR3_3_0) +#define GPSR3_15 F_(QSPI0_SSL, IP1SR3_31_28) +#define GPSR3_14 F_(IPC_CLKOUT, IP1SR3_27_24) +#define GPSR3_13 F_(IPC_CLKIN, IP1SR3_23_20) +#define GPSR3_12 F_(SD_WP, IP1SR3_19_16) +#define GPSR3_11 F_(SD_CD, IP1SR3_15_12) +#define GPSR3_10 F_(MMC_SD_CMD, IP1SR3_11_8) +#define GPSR3_9 F_(MMC_D6, IP1SR3_7_4) +#define GPSR3_8 F_(MMC_D7, IP1SR3_3_0) +#define GPSR3_7 F_(MMC_D4, IP0SR3_31_28) +#define GPSR3_6 F_(MMC_D5, IP0SR3_27_24) +#define GPSR3_5 F_(MMC_SD_D3, IP0SR3_23_20) +#define GPSR3_4 F_(MMC_DS, IP0SR3_19_16) +#define GPSR3_3 F_(MMC_SD_CLK, IP0SR3_15_12) +#define GPSR3_2 F_(MMC_SD_D2, IP0SR3_11_8) +#define GPSR3_1 F_(MMC_SD_D0, IP0SR3_7_4) +#define GPSR3_0 F_(MMC_SD_D1, IP0SR3_3_0) + +/* GPSR4 */ +#define GPSR4_24 FM(AVS1) +#define GPSR4_23 FM(AVS0) +#define GPSR4_22 FM(PCIE1_CLKREQ_N) +#define GPSR4_21 FM(PCIE0_CLKREQ_N) +#define GPSR4_20 FM(TSN0_TXCREFCLK) +#define GPSR4_19 FM(TSN0_TD2) +#define GPSR4_18 FM(TSN0_TD3) +#define GPSR4_17 FM(TSN0_RD2) +#define GPSR4_16 FM(TSN0_RD3) +#define GPSR4_15 FM(TSN0_TD0) +#define GPSR4_14 FM(TSN0_TD1) +#define GPSR4_13 FM(TSN0_RD1) +#define GPSR4_12 FM(TSN0_TXC) +#define GPSR4_11 FM(TSN0_RXC) +#define GPSR4_10 FM(TSN0_RD0) +#define GPSR4_9 FM(TSN0_TX_CTL) +#define GPSR4_8 FM(TSN0_AVTP_PPS0) +#define GPSR4_7 FM(TSN0_RX_CTL) +#define GPSR4_6 FM(TSN0_AVTP_CAPTURE) +#define GPSR4_5 FM(TSN0_AVTP_MATCH) +#define GPSR4_4 FM(TSN0_LINK) +#define GPSR4_3 FM(TSN0_PHY_INT) +#define GPSR4_2 FM(TSN0_AVTP_PPS1) +#define GPSR4_1 FM(TSN0_MDC) +#define GPSR4_0 FM(TSN0_MDIO) + +/* GPSR 5 */ +#define GPSR5_20 FM(AVB2_RX_CTL) +#define GPSR5_19 FM(AVB2_TX_CTL) +#define GPSR5_18 FM(AVB2_RXC) +#define GPSR5_17 FM(AVB2_RD0) +#define GPSR5_16 FM(AVB2_TXC) +#define GPSR5_15 FM(AVB2_TD0) +#define GPSR5_14 FM(AVB2_RD1) +#define GPSR5_13 FM(AVB2_RD2) +#define GPSR5_12 FM(AVB2_TD1) +#define GPSR5_11 FM(AVB2_TD2) +#define GPSR5_10 FM(AVB2_MDIO) +#define GPSR5_9 FM(AVB2_RD3) +#define GPSR5_8 FM(AVB2_TD3) +#define GPSR5_7 FM(AVB2_TXCREFCLK) +#define GPSR5_6 FM(AVB2_MDC) +#define GPSR5_5 FM(AVB2_MAGIC) +#define GPSR5_4 FM(AVB2_PHY_INT) +#define GPSR5_3 FM(AVB2_LINK) +#define GPSR5_2 FM(AVB2_AVTP_MATCH) +#define GPSR5_1 FM(AVB2_AVTP_CAPTURE) +#define GPSR5_0 FM(AVB2_AVTP_PPS) + +/* GPSR 6 */ +#define GPSR6_20 F_(AVB1_TXCREFCLK, IP2SR6_19_16) +#define GPSR6_19 F_(AVB1_RD3, IP2SR6_15_12) +#define GPSR6_18 F_(AVB1_TD3, IP2SR6_11_8) +#define GPSR6_17 F_(AVB1_RD2, IP2SR6_7_4) +#define GPSR6_16 F_(AVB1_TD2, IP2SR6_3_0) +#define GPSR6_15 F_(AVB1_RD0, IP1SR6_31_28) +#define GPSR6_14 F_(AVB1_RD1, IP1SR6_27_24) +#define GPSR6_13 F_(AVB1_TD0, IP1SR6_23_20) +#define GPSR6_12 F_(AVB1_TD1, IP1SR6_19_16) +#define GPSR6_11 F_(AVB1_AVTP_CAPTURE, IP1SR6_15_12) +#define GPSR6_10 F_(AVB1_AVTP_PPS, IP1SR6_11_8) +#define GPSR6_9 F_(AVB1_RX_CTL, IP1SR6_7_4) +#define GPSR6_8 F_(AVB1_RXC, IP1SR6_3_0) +#define GPSR6_7 F_(AVB1_TX_CTL, IP0SR6_31_28) +#define GPSR6_6 F_(AVB1_TXC, IP0SR6_27_24) +#define GPSR6_5 F_(AVB1_AVTP_MATCH, IP0SR6_23_20) +#define GPSR6_4 F_(AVB1_LINK, IP0SR6_19_16) +#define GPSR6_3 F_(AVB1_PHY_INT, IP0SR6_15_12) +#define GPSR6_2 F_(AVB1_MDC, IP0SR6_11_8) +#define GPSR6_1 F_(AVB1_MAGIC, IP0SR6_7_4) +#define GPSR6_0 F_(AVB1_MDIO, IP0SR6_3_0) + +/* GPSR7 */ +#define GPSR7_20 F_(AVB0_RX_CTL, IP2SR7_19_16) +#define GPSR7_19 F_(AVB0_RXC, IP2SR7_15_12) +#define GPSR7_18 F_(AVB0_RD0, IP2SR7_11_8) +#define GPSR7_17 F_(AVB0_RD1, IP2SR7_7_4) +#define GPSR7_16 F_(AVB0_TX_CTL, IP2SR7_3_0) +#define GPSR7_15 F_(AVB0_TXC, IP1SR7_31_28) +#define GPSR7_14 F_(AVB0_MDIO, IP1SR7_27_24) +#define GPSR7_13 F_(AVB0_MDC, IP1SR7_23_20) +#define GPSR7_12 F_(AVB0_RD2, IP1SR7_19_16) +#define GPSR7_11 F_(AVB0_TD0, IP1SR7_15_12) +#define GPSR7_10 F_(AVB0_MAGIC, IP1SR7_11_8) +#define GPSR7_9 F_(AVB0_TXCREFCLK, IP1SR7_7_4) +#define GPSR7_8 F_(AVB0_RD3, IP1SR7_3_0) +#define GPSR7_7 F_(AVB0_TD1, IP0SR7_31_28) +#define GPSR7_6 F_(AVB0_TD2, IP0SR7_27_24) +#define GPSR7_5 F_(AVB0_PHY_INT, IP0SR7_23_20) +#define GPSR7_4 F_(AVB0_LINK, IP0SR7_19_16) +#define GPSR7_3 F_(AVB0_TD3, IP0SR7_15_12) +#define GPSR7_2 F_(AVB0_AVTP_MATCH, IP0SR7_11_8) +#define GPSR7_1 F_(AVB0_AVTP_CAPTURE, IP0SR7_7_4) +#define GPSR7_0 F_(AVB0_AVTP_PPS, IP0SR7_3_0) + +/* GPSR8 */ +#define GPSR8_13 F_(GP8_13, IP1SR8_23_20) +#define GPSR8_12 F_(GP8_12, IP1SR8_19_16) +#define GPSR8_11 F_(SDA5, IP1SR8_15_12) +#define GPSR8_10 F_(SCL5, IP1SR8_11_8) +#define GPSR8_9 F_(SDA4, IP1SR8_7_4) +#define GPSR8_8 F_(SCL4, IP1SR8_3_0) +#define GPSR8_7 F_(SDA3, IP0SR8_31_28) +#define GPSR8_6 F_(SCL3, IP0SR8_27_24) +#define GPSR8_5 F_(SDA2, IP0SR8_23_20) +#define GPSR8_4 F_(SCL2, IP0SR8_19_16) +#define GPSR8_3 F_(SDA1, IP0SR8_15_12) +#define GPSR8_2 F_(SCL1, IP0SR8_11_8) +#define GPSR8_1 F_(SDA0, IP0SR8_7_4) +#define GPSR8_0 F_(SCL0, IP0SR8_3_0) + +/* SR0 */ +/* IP0SR0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR0_3_0 F_(0, 0) FM(ERROROUTC_B) FM(TCLK2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_7_4 F_(0, 0) FM(MSIOF3_SS1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_11_8 F_(0, 0) FM(MSIOF3_SS2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_15_12 FM(IRQ3) FM(MSIOF3_SCK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_19_16 FM(IRQ2) FM(MSIOF3_TXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_23_20 FM(IRQ1) FM(MSIOF3_RXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_27_24 FM(IRQ0) FM(MSIOF3_SYNC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR0_31_28 FM(MSIOF5_SS2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR0_3_0 FM(MSIOF5_SS1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_7_4 FM(MSIOF5_SYNC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_11_8 FM(MSIOF5_TXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_15_12 FM(MSIOF5_SCK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_19_16 FM(MSIOF5_RXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_23_20 FM(MSIOF2_SS2) FM(TCLK1) FM(IRQ2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_27_24 FM(MSIOF2_SS1) FM(HTX1) FM(TX1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR0_31_28 FM(MSIOF2_SYNC) FM(HRX1) FM(RX1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR0_3_0 FM(MSIOF2_TXD) FM(HCTS1_N) FM(CTS1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR0_7_4 FM(MSIOF2_SCK) FM(HRTS1_N) FM(RTS1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR0_11_8 FM(MSIOF2_RXD) FM(HSCK1) FM(SCK1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR1 */ +/* IP0SR1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR1_3_0 FM(MSIOF1_SS2) FM(HTX3_A) FM(TX3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_7_4 FM(MSIOF1_SS1) FM(HCTS3_N_A) FM(RX3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_11_8 FM(MSIOF1_SYNC) FM(HRTS3_N_A) FM(RTS3_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_15_12 FM(MSIOF1_SCK) FM(HSCK3_A) FM(CTS3_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_19_16 FM(MSIOF1_TXD) FM(HRX3_A) FM(SCK3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_23_20 FM(MSIOF1_RXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_27_24 FM(MSIOF0_SS2) FM(HTX1_X) FM(TX1_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR1_31_28 FM(MSIOF0_SS1) FM(HRX1_X) FM(RX1_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR1_3_0 FM(MSIOF0_SYNC) FM(HCTS1_N_X) FM(CTS1_N_X) FM(CANFD5_TX_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_7_4 FM(MSIOF0_TXD) FM(HRTS1_N_X) FM(RTS1_N_X) FM(CANFD5_RX_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_11_8 FM(MSIOF0_SCK) FM(HSCK1_X) FM(SCK1_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_15_12 FM(MSIOF0_RXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_19_16 FM(HTX0) FM(TX0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_23_20 FM(HCTS0_N) FM(CTS0_N) FM(PWM8_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_27_24 FM(HRTS0_N) FM(RTS0_N) FM(PWM9_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR1_31_28 FM(HSCK0) FM(SCK0) FM(PWM0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR1_3_0 FM(HRX0) FM(RX0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_7_4 FM(SCIF_CLK) FM(IRQ4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_11_8 FM(SSI_SCK) FM(TCLK3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_15_12 FM(SSI_WS) FM(TCLK4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_19_16 FM(SSI_SD) FM(IRQ0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_23_20 FM(AUDIO_CLKOUT) FM(IRQ1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_27_24 FM(AUDIO_CLKIN) FM(PWM3_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR1_31_28 F_(0, 0) FM(TCLK2) FM(MSIOF4_SS1) FM(IRQ3_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP3SR1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP3SR1_3_0 FM(HRX3) FM(SCK3_A) FM(MSIOF4_SS2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR1_7_4 FM(HSCK3) FM(CTS3_N_A) FM(MSIOF4_SCK) FM(TPU0TO0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR1_11_8 FM(HRTS3_N) FM(RTS3_N_A) FM(MSIOF4_TXD) FM(TPU0TO1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR1_15_12 FM(HCTS3_N) FM(RX3_A) FM(MSIOF4_RXD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR1_19_16 FM(HTX3) FM(TX3_A) FM(MSIOF4_SYNC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR2 */ +/* IP0SR2 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR2_3_0 FM(FXR_TXDA) FM(CANFD1_TX) FM(TPU0TO2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_7_4 FM(FXR_TXENA_N) FM(CANFD1_RX) FM(TPU0TO3_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_11_8 FM(RXDA_EXTFXR) FM(CANFD5_TX) FM(IRQ5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_15_12 FM(CLK_EXTFXR) FM(CANFD5_RX) FM(IRQ4_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_19_16 FM(RXDB_EXTFXR) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_23_20 FM(FXR_TXENB_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_27_24 FM(FXR_TXDB) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR2_31_28 FM(TPU0TO1) FM(CANFD6_TX) F_(0, 0) FM(TCLK2_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR2 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR2_3_0 FM(TPU0TO0) FM(CANFD6_RX) F_(0, 0) FM(TCLK1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_7_4 FM(CAN_CLK) FM(FXR_TXENA_N_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_11_8 FM(CANFD0_TX) FM(FXR_TXENB_N_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_15_12 FM(CANFD0_RX) FM(STPWT_EXTFXR) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_19_16 FM(CANFD2_TX) FM(TPU0TO2) F_(0, 0) FM(TCLK3_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_23_20 FM(CANFD2_RX) FM(TPU0TO3) FM(PWM1_B) FM(TCLK4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_27_24 FM(CANFD3_TX) F_(0, 0) FM(PWM2_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR2_31_28 FM(CANFD3_RX) F_(0, 0) FM(PWM3_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR2 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR2_3_0 FM(CANFD4_TX) F_(0, 0) FM(PWM4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR2_7_4 FM(CANFD4_RX) F_(0, 0) FM(PWM5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR2_11_8 FM(CANFD7_TX) F_(0, 0) FM(PWM6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR2_15_12 FM(CANFD7_RX) F_(0, 0) FM(PWM7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR3 */ +/* IP0SR3 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR3_3_0 FM(MMC_SD_D1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_7_4 FM(MMC_SD_D0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_11_8 FM(MMC_SD_D2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_15_12 FM(MMC_SD_CLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_19_16 FM(MMC_DS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_23_20 FM(MMC_SD_D3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_27_24 FM(MMC_D5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR3_31_28 FM(MMC_D4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR3 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR3_3_0 FM(MMC_D7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_7_4 FM(MMC_D6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_11_8 FM(MMC_SD_CMD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_15_12 FM(SD_CD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_19_16 FM(SD_WP) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_23_20 FM(IPC_CLKIN) FM(IPC_CLKEN_IN) FM(PWM1_A) FM(TCLK3_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_27_24 FM(IPC_CLKOUT) FM(IPC_CLKEN_OUT) FM(ERROROUTC_A) FM(TCLK4_X) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR3_31_28 FM(QSPI0_SSL) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR3 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR3_3_0 FM(QSPI0_IO3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_7_4 FM(QSPI0_IO2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_11_8 FM(QSPI0_MISO_IO1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_15_12 FM(QSPI0_MOSI_IO0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_19_16 FM(QSPI0_SPCLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_23_20 FM(QSPI1_MOSI_IO0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_27_24 FM(QSPI1_SPCLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR3_31_28 FM(QSPI1_MISO_IO1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP3SR3 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP3SR3_3_0 FM(QSPI1_IO2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR3_7_4 FM(QSPI1_SSL) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR3_11_8 FM(QSPI1_IO3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR3_15_12 FM(RPC_RESET_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR3_19_16 FM(RPC_WP_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP3SR3_23_20 FM(RPC_INT_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR6 */ +/* IP0SR6 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR6_3_0 FM(AVB1_MDIO) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_7_4 FM(AVB1_MAGIC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_11_8 FM(AVB1_MDC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_15_12 FM(AVB1_PHY_INT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_19_16 FM(AVB1_LINK) FM(AVB1_MII_TX_ER) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_23_20 FM(AVB1_AVTP_MATCH) FM(AVB1_MII_RX_ER) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_27_24 FM(AVB1_TXC) FM(AVB1_MII_TXC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR6_31_28 FM(AVB1_TX_CTL) FM(AVB1_MII_TX_EN) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR6 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR6_3_0 FM(AVB1_RXC) FM(AVB1_MII_RXC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_7_4 FM(AVB1_RX_CTL) FM(AVB1_MII_RX_DV) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_11_8 FM(AVB1_AVTP_PPS) FM(AVB1_MII_COL) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_15_12 FM(AVB1_AVTP_CAPTURE) FM(AVB1_MII_CRS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_19_16 FM(AVB1_TD1) FM(AVB1_MII_TD1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_23_20 FM(AVB1_TD0) FM(AVB1_MII_TD0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_27_24 FM(AVB1_RD1) FM(AVB1_MII_RD1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR6_31_28 FM(AVB1_RD0) FM(AVB1_MII_RD0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR6 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR6_3_0 FM(AVB1_TD2) FM(AVB1_MII_TD2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR6_7_4 FM(AVB1_RD2) FM(AVB1_MII_RD2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR6_11_8 FM(AVB1_TD3) FM(AVB1_MII_TD3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR6_15_12 FM(AVB1_RD3) FM(AVB1_MII_RD3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR6_19_16 FM(AVB1_TXCREFCLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR7 */ +/* IP0SR7 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR7_3_0 FM(AVB0_AVTP_PPS) FM(AVB0_MII_COL) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_7_4 FM(AVB0_AVTP_CAPTURE) FM(AVB0_MII_CRS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_11_8 FM(AVB0_AVTP_MATCH) FM(AVB0_MII_RX_ER) FM(CC5_OSCOUT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_15_12 FM(AVB0_TD3) FM(AVB0_MII_TD3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_19_16 FM(AVB0_LINK) FM(AVB0_MII_TX_ER) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_23_20 FM(AVB0_PHY_INT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_27_24 FM(AVB0_TD2) FM(AVB0_MII_TD2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR7_31_28 FM(AVB0_TD1) FM(AVB0_MII_TD1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR7 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR7_3_0 FM(AVB0_RD3) FM(AVB0_MII_RD3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_7_4 FM(AVB0_TXCREFCLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_11_8 FM(AVB0_MAGIC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_15_12 FM(AVB0_TD0) FM(AVB0_MII_TD0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_19_16 FM(AVB0_RD2) FM(AVB0_MII_RD2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_23_20 FM(AVB0_MDC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_27_24 FM(AVB0_MDIO) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR7_31_28 FM(AVB0_TXC) FM(AVB0_MII_TXC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP2SR7 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP2SR7_3_0 FM(AVB0_TX_CTL) FM(AVB0_MII_TX_EN) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR7_7_4 FM(AVB0_RD1) FM(AVB0_MII_RD1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR7_11_8 FM(AVB0_RD0) FM(AVB0_MII_RD0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR7_15_12 FM(AVB0_RXC) FM(AVB0_MII_RXC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP2SR7_19_16 FM(AVB0_RX_CTL) FM(AVB0_MII_RX_DV) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* SR8 */ +/* IP0SR8 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP0SR8_3_0 FM(SCL0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_7_4 FM(SDA0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_11_8 FM(SCL1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_15_12 FM(SDA1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_19_16 FM(SCL2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_23_20 FM(SDA2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_27_24 FM(SCL3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP0SR8_31_28 FM(SDA3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +/* IP1SR8 */ /* 0 */ /* 1 */ /* 2 */ /* 3 4 5 6 7 8 9 A B C D E F */ +#define IP1SR8_3_0 FM(SCL4) FM(HRX2) FM(SCK4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR8_7_4 FM(SDA4) FM(HTX2) FM(CTS4_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR8_11_8 FM(SCL5) FM(HRTS2_N) FM(RTS4_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR8_15_12 FM(SDA5) FM(SCIF_CLK2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR8_19_16 F_(0, 0) FM(HCTS2_N) FM(TX4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) +#define IP1SR8_23_20 F_(0, 0) FM(HSCK2) FM(RX4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) + +#define PINMUX_GPSR \ + GPSR3_29 \ + GPSR1_28 GPSR3_28 \ + GPSR1_27 GPSR3_27 \ + GPSR1_26 GPSR3_26 \ + GPSR1_25 GPSR3_25 \ + GPSR1_24 GPSR3_24 GPSR4_24 \ + GPSR1_23 GPSR3_23 GPSR4_23 \ + GPSR1_22 GPSR3_22 GPSR4_22 \ + GPSR1_21 GPSR3_21 GPSR4_21 \ + GPSR1_20 GPSR3_20 GPSR4_20 GPSR5_20 GPSR6_20 GPSR7_20 \ + GPSR1_19 GPSR2_19 GPSR3_19 GPSR4_19 GPSR5_19 GPSR6_19 GPSR7_19 \ +GPSR0_18 GPSR1_18 GPSR2_18 GPSR3_18 GPSR4_18 GPSR5_18 GPSR6_18 GPSR7_18 \ +GPSR0_17 GPSR1_17 GPSR2_17 GPSR3_17 GPSR4_17 GPSR5_17 GPSR6_17 GPSR7_17 \ +GPSR0_16 GPSR1_16 GPSR2_16 GPSR3_16 GPSR4_16 GPSR5_16 GPSR6_16 GPSR7_16 \ +GPSR0_15 GPSR1_15 GPSR2_15 GPSR3_15 GPSR4_15 GPSR5_15 GPSR6_15 GPSR7_15 \ +GPSR0_14 GPSR1_14 GPSR2_14 GPSR3_14 GPSR4_14 GPSR5_14 GPSR6_14 GPSR7_14 \ +GPSR0_13 GPSR1_13 GPSR2_13 GPSR3_13 GPSR4_13 GPSR5_13 GPSR6_13 GPSR7_13 GPSR8_13 \ +GPSR0_12 GPSR1_12 GPSR2_12 GPSR3_12 GPSR4_12 GPSR5_12 GPSR6_12 GPSR7_12 GPSR8_12 \ +GPSR0_11 GPSR1_11 GPSR2_11 GPSR3_11 GPSR4_11 GPSR5_11 GPSR6_11 GPSR7_11 GPSR8_11 \ +GPSR0_10 GPSR1_10 GPSR2_10 GPSR3_10 GPSR4_10 GPSR5_10 GPSR6_10 GPSR7_10 GPSR8_10 \ +GPSR0_9 GPSR1_9 GPSR2_9 GPSR3_9 GPSR4_9 GPSR5_9 GPSR6_9 GPSR7_9 GPSR8_9 \ +GPSR0_8 GPSR1_8 GPSR2_8 GPSR3_8 GPSR4_8 GPSR5_8 GPSR6_8 GPSR7_8 GPSR8_8 \ +GPSR0_7 GPSR1_7 GPSR2_7 GPSR3_7 GPSR4_7 GPSR5_7 GPSR6_7 GPSR7_7 GPSR8_7 \ +GPSR0_6 GPSR1_6 GPSR2_6 GPSR3_6 GPSR4_6 GPSR5_6 GPSR6_6 GPSR7_6 GPSR8_6 \ +GPSR0_5 GPSR1_5 GPSR2_5 GPSR3_5 GPSR4_5 GPSR5_5 GPSR6_5 GPSR7_5 GPSR8_5 \ +GPSR0_4 GPSR1_4 GPSR2_4 GPSR3_4 GPSR4_4 GPSR5_4 GPSR6_4 GPSR7_4 GPSR8_4 \ +GPSR0_3 GPSR1_3 GPSR2_3 GPSR3_3 GPSR4_3 GPSR5_3 GPSR6_3 GPSR7_3 GPSR8_3 \ +GPSR0_2 GPSR1_2 GPSR2_2 GPSR3_2 GPSR4_2 GPSR5_2 GPSR6_2 GPSR7_2 GPSR8_2 \ +GPSR0_1 GPSR1_1 GPSR2_1 GPSR3_1 GPSR4_1 GPSR5_1 GPSR6_1 GPSR7_1 GPSR8_1 \ +GPSR0_0 GPSR1_0 GPSR2_0 GPSR3_0 GPSR4_0 GPSR5_0 GPSR6_0 GPSR7_0 GPSR8_0 + +#define PINMUX_IPSR \ +\ +FM(IP0SR0_3_0) IP0SR0_3_0 FM(IP1SR0_3_0) IP1SR0_3_0 FM(IP2SR0_3_0) IP2SR0_3_0 \ +FM(IP0SR0_7_4) IP0SR0_7_4 FM(IP1SR0_7_4) IP1SR0_7_4 FM(IP2SR0_7_4) IP2SR0_7_4 \ +FM(IP0SR0_11_8) IP0SR0_11_8 FM(IP1SR0_11_8) IP1SR0_11_8 FM(IP2SR0_11_8) IP2SR0_11_8 \ +FM(IP0SR0_15_12) IP0SR0_15_12 FM(IP1SR0_15_12) IP1SR0_15_12 \ +FM(IP0SR0_19_16) IP0SR0_19_16 FM(IP1SR0_19_16) IP1SR0_19_16 \ +FM(IP0SR0_23_20) IP0SR0_23_20 FM(IP1SR0_23_20) IP1SR0_23_20 \ +FM(IP0SR0_27_24) IP0SR0_27_24 FM(IP1SR0_27_24) IP1SR0_27_24 \ +FM(IP0SR0_31_28) IP0SR0_31_28 FM(IP1SR0_31_28) IP1SR0_31_28 \ +\ +FM(IP0SR1_3_0) IP0SR1_3_0 FM(IP1SR1_3_0) IP1SR1_3_0 FM(IP2SR1_3_0) IP2SR1_3_0 FM(IP3SR1_3_0) IP3SR1_3_0 \ +FM(IP0SR1_7_4) IP0SR1_7_4 FM(IP1SR1_7_4) IP1SR1_7_4 FM(IP2SR1_7_4) IP2SR1_7_4 FM(IP3SR1_7_4) IP3SR1_7_4 \ +FM(IP0SR1_11_8) IP0SR1_11_8 FM(IP1SR1_11_8) IP1SR1_11_8 FM(IP2SR1_11_8) IP2SR1_11_8 FM(IP3SR1_11_8) IP3SR1_11_8 \ +FM(IP0SR1_15_12) IP0SR1_15_12 FM(IP1SR1_15_12) IP1SR1_15_12 FM(IP2SR1_15_12) IP2SR1_15_12 FM(IP3SR1_15_12) IP3SR1_15_12 \ +FM(IP0SR1_19_16) IP0SR1_19_16 FM(IP1SR1_19_16) IP1SR1_19_16 FM(IP2SR1_19_16) IP2SR1_19_16 FM(IP3SR1_19_16) IP3SR1_19_16 \ +FM(IP0SR1_23_20) IP0SR1_23_20 FM(IP1SR1_23_20) IP1SR1_23_20 FM(IP2SR1_23_20) IP2SR1_23_20 \ +FM(IP0SR1_27_24) IP0SR1_27_24 FM(IP1SR1_27_24) IP1SR1_27_24 FM(IP2SR1_27_24) IP2SR1_27_24 \ +FM(IP0SR1_31_28) IP0SR1_31_28 FM(IP1SR1_31_28) IP1SR1_31_28 FM(IP2SR1_31_28) IP2SR1_31_28 \ +\ +FM(IP0SR2_3_0) IP0SR2_3_0 FM(IP1SR2_3_0) IP1SR2_3_0 FM(IP2SR2_3_0) IP2SR2_3_0 \ +FM(IP0SR2_7_4) IP0SR2_7_4 FM(IP1SR2_7_4) IP1SR2_7_4 FM(IP2SR2_7_4) IP2SR2_7_4 \ +FM(IP0SR2_11_8) IP0SR2_11_8 FM(IP1SR2_11_8) IP1SR2_11_8 FM(IP2SR2_11_8) IP2SR2_11_8 \ +FM(IP0SR2_15_12) IP0SR2_15_12 FM(IP1SR2_15_12) IP1SR2_15_12 FM(IP2SR2_15_12) IP2SR2_15_12 \ +FM(IP0SR2_19_16) IP0SR2_19_16 FM(IP1SR2_19_16) IP1SR2_19_16 \ +FM(IP0SR2_23_20) IP0SR2_23_20 FM(IP1SR2_23_20) IP1SR2_23_20 \ +FM(IP0SR2_27_24) IP0SR2_27_24 FM(IP1SR2_27_24) IP1SR2_27_24 \ +FM(IP0SR2_31_28) IP0SR2_31_28 FM(IP1SR2_31_28) IP1SR2_31_28 \ +\ +FM(IP0SR3_3_0) IP0SR3_3_0 FM(IP1SR3_3_0) IP1SR3_3_0 FM(IP2SR3_3_0) IP2SR3_3_0 FM(IP3SR3_3_0) IP3SR3_3_0 \ +FM(IP0SR3_7_4) IP0SR3_7_4 FM(IP1SR3_7_4) IP1SR3_7_4 FM(IP2SR3_7_4) IP2SR3_7_4 FM(IP3SR3_7_4) IP3SR3_7_4 \ +FM(IP0SR3_11_8) IP0SR3_11_8 FM(IP1SR3_11_8) IP1SR3_11_8 FM(IP2SR3_11_8) IP2SR3_11_8 FM(IP3SR3_11_8) IP3SR3_11_8 \ +FM(IP0SR3_15_12) IP0SR3_15_12 FM(IP1SR3_15_12) IP1SR3_15_12 FM(IP2SR3_15_12) IP2SR3_15_12 FM(IP3SR3_15_12) IP3SR3_15_12 \ +FM(IP0SR3_19_16) IP0SR3_19_16 FM(IP1SR3_19_16) IP1SR3_19_16 FM(IP2SR3_19_16) IP2SR3_19_16 FM(IP3SR3_19_16) IP3SR3_19_16 \ +FM(IP0SR3_23_20) IP0SR3_23_20 FM(IP1SR3_23_20) IP1SR3_23_20 FM(IP2SR3_23_20) IP2SR3_23_20 FM(IP3SR3_23_20) IP3SR3_23_20 \ +FM(IP0SR3_27_24) IP0SR3_27_24 FM(IP1SR3_27_24) IP1SR3_27_24 FM(IP2SR3_27_24) IP2SR3_27_24 \ +FM(IP0SR3_31_28) IP0SR3_31_28 FM(IP1SR3_31_28) IP1SR3_31_28 FM(IP2SR3_31_28) IP2SR3_31_28 \ +\ +FM(IP0SR6_3_0) IP0SR6_3_0 FM(IP1SR6_3_0) IP1SR6_3_0 FM(IP2SR6_3_0) IP2SR6_3_0 \ +FM(IP0SR6_7_4) IP0SR6_7_4 FM(IP1SR6_7_4) IP1SR6_7_4 FM(IP2SR6_7_4) IP2SR6_7_4 \ +FM(IP0SR6_11_8) IP0SR6_11_8 FM(IP1SR6_11_8) IP1SR6_11_8 FM(IP2SR6_11_8) IP2SR6_11_8 \ +FM(IP0SR6_15_12) IP0SR6_15_12 FM(IP1SR6_15_12) IP1SR6_15_12 FM(IP2SR6_15_12) IP2SR6_15_12 \ +FM(IP0SR6_19_16) IP0SR6_19_16 FM(IP1SR6_19_16) IP1SR6_19_16 FM(IP2SR6_19_16) IP2SR6_19_16 \ +FM(IP0SR6_23_20) IP0SR6_23_20 FM(IP1SR6_23_20) IP1SR6_23_20 \ +FM(IP0SR6_27_24) IP0SR6_27_24 FM(IP1SR6_27_24) IP1SR6_27_24 \ +FM(IP0SR6_31_28) IP0SR6_31_28 FM(IP1SR6_31_28) IP1SR6_31_28 \ +\ +FM(IP0SR7_3_0) IP0SR7_3_0 FM(IP1SR7_3_0) IP1SR7_3_0 FM(IP2SR7_3_0) IP2SR7_3_0 \ +FM(IP0SR7_7_4) IP0SR7_7_4 FM(IP1SR7_7_4) IP1SR7_7_4 FM(IP2SR7_7_4) IP2SR7_7_4 \ +FM(IP0SR7_11_8) IP0SR7_11_8 FM(IP1SR7_11_8) IP1SR7_11_8 FM(IP2SR7_11_8) IP2SR7_11_8 \ +FM(IP0SR7_15_12) IP0SR7_15_12 FM(IP1SR7_15_12) IP1SR7_15_12 FM(IP2SR7_15_12) IP2SR7_15_12 \ +FM(IP0SR7_19_16) IP0SR7_19_16 FM(IP1SR7_19_16) IP1SR7_19_16 FM(IP2SR7_19_16) IP2SR7_19_16 \ +FM(IP0SR7_23_20) IP0SR7_23_20 FM(IP1SR7_23_20) IP1SR7_23_20 \ +FM(IP0SR7_27_24) IP0SR7_27_24 FM(IP1SR7_27_24) IP1SR7_27_24 \ +FM(IP0SR7_31_28) IP0SR7_31_28 FM(IP1SR7_31_28) IP1SR7_31_28 \ +\ +FM(IP0SR8_3_0) IP0SR8_3_0 FM(IP1SR8_3_0) IP1SR8_3_0 \ +FM(IP0SR8_7_4) IP0SR8_7_4 FM(IP1SR8_7_4) IP1SR8_7_4 \ +FM(IP0SR8_11_8) IP0SR8_11_8 FM(IP1SR8_11_8) IP1SR8_11_8 \ +FM(IP0SR8_15_12) IP0SR8_15_12 FM(IP1SR8_15_12) IP1SR8_15_12 \ +FM(IP0SR8_19_16) IP0SR8_19_16 FM(IP1SR8_19_16) IP1SR8_19_16 \ +FM(IP0SR8_23_20) IP0SR8_23_20 FM(IP1SR8_23_20) IP1SR8_23_20 \ +FM(IP0SR8_27_24) IP0SR8_27_24 \ +FM(IP0SR8_31_28) IP0SR8_31_28 + +/* MOD_SEL4 */ /* 0 */ /* 1 */ +#define MOD_SEL4_19 FM(SEL_TSN0_TD2_0) FM(SEL_TSN0_TD2_1) +#define MOD_SEL4_18 FM(SEL_TSN0_TD3_0) FM(SEL_TSN0_TD3_1) +#define MOD_SEL4_15 FM(SEL_TSN0_TD0_0) FM(SEL_TSN0_TD0_1) +#define MOD_SEL4_14 FM(SEL_TSN0_TD1_0) FM(SEL_TSN0_TD1_1) +#define MOD_SEL4_12 FM(SEL_TSN0_TXC_0) FM(SEL_TSN0_TXC_1) +#define MOD_SEL4_9 FM(SEL_TSN0_TX_CTL_0) FM(SEL_TSN0_TX_CTL_1) +#define MOD_SEL4_8 FM(SEL_TSN0_AVTP_PPS0_0) FM(SEL_TSN0_AVTP_PPS0_1) +#define MOD_SEL4_5 FM(SEL_TSN0_AVTP_MATCH_0) FM(SEL_TSN0_AVTP_MATCH_1) +#define MOD_SEL4_2 FM(SEL_TSN0_AVTP_PPS1_0) FM(SEL_TSN0_AVTP_PPS1_1) +#define MOD_SEL4_1 FM(SEL_TSN0_MDC_0) FM(SEL_TSN0_MDC_1) + +/* MOD_SEL5 */ /* 0 */ /* 1 */ +#define MOD_SEL5_19 FM(SEL_AVB2_TX_CTL_0) FM(SEL_AVB2_TX_CTL_1) +#define MOD_SEL5_16 FM(SEL_AVB2_TXC_0) FM(SEL_AVB2_TXC_1) +#define MOD_SEL5_15 FM(SEL_AVB2_TD0_0) FM(SEL_AVB2_TD0_1) +#define MOD_SEL5_12 FM(SEL_AVB2_TD1_0) FM(SEL_AVB2_TD1_1) +#define MOD_SEL5_11 FM(SEL_AVB2_TD2_0) FM(SEL_AVB2_TD2_1) +#define MOD_SEL5_8 FM(SEL_AVB2_TD3_0) FM(SEL_AVB2_TD3_1) +#define MOD_SEL5_6 FM(SEL_AVB2_MDC_0) FM(SEL_AVB2_MDC_1) +#define MOD_SEL5_5 FM(SEL_AVB2_MAGIC_0) FM(SEL_AVB2_MAGIC_1) +#define MOD_SEL5_2 FM(SEL_AVB2_AVTP_MATCH_0) FM(SEL_AVB2_AVTP_MATCH_1) +#define MOD_SEL5_0 FM(SEL_AVB2_AVTP_PPS_0) FM(SEL_AVB2_AVTP_PPS_1) + +/* MOD_SEL6 */ /* 0 */ /* 1 */ +#define MOD_SEL6_18 FM(SEL_AVB1_TD3_0) FM(SEL_AVB1_TD3_1) +#define MOD_SEL6_16 FM(SEL_AVB1_TD2_0) FM(SEL_AVB1_TD2_1) +#define MOD_SEL6_13 FM(SEL_AVB1_TD0_0) FM(SEL_AVB1_TD0_1) +#define MOD_SEL6_12 FM(SEL_AVB1_TD1_0) FM(SEL_AVB1_TD1_1) +#define MOD_SEL6_10 FM(SEL_AVB1_AVTP_PPS_0) FM(SEL_AVB1_AVTP_PPS_1) +#define MOD_SEL6_7 FM(SEL_AVB1_TX_CTL_0) FM(SEL_AVB1_TX_CTL_1) +#define MOD_SEL6_6 FM(SEL_AVB1_TXC_0) FM(SEL_AVB1_TXC_1) +#define MOD_SEL6_5 FM(SEL_AVB1_AVTP_MATCH_0) FM(SEL_AVB1_AVTP_MATCH_1) +#define MOD_SEL6_2 FM(SEL_AVB1_MDC_0) FM(SEL_AVB1_MDC_1) +#define MOD_SEL6_1 FM(SEL_AVB1_MAGIC_0) FM(SEL_AVB1_MAGIC_1) + +/* MOD_SEL7 */ /* 0 */ /* 1 */ +#define MOD_SEL7_16 FM(SEL_AVB0_TX_CTL_0) FM(SEL_AVB0_TX_CTL_1) +#define MOD_SEL7_15 FM(SEL_AVB0_TXC_0) FM(SEL_AVB0_TXC_1) +#define MOD_SEL7_13 FM(SEL_AVB0_MDC_0) FM(SEL_AVB0_MDC_1) +#define MOD_SEL7_11 FM(SEL_AVB0_TD0_0) FM(SEL_AVB0_TD0_1) +#define MOD_SEL7_10 FM(SEL_AVB0_MAGIC_0) FM(SEL_AVB0_MAGIC_1) +#define MOD_SEL7_7 FM(SEL_AVB0_TD1_0) FM(SEL_AVB0_TD1_1) +#define MOD_SEL7_6 FM(SEL_AVB0_TD2_0) FM(SEL_AVB0_TD2_1) +#define MOD_SEL7_3 FM(SEL_AVB0_TD3_0) FM(SEL_AVB0_TD3_1) +#define MOD_SEL7_2 FM(SEL_AVB0_AVTP_MATCH_0) FM(SEL_AVB0_AVTP_MATCH_1) +#define MOD_SEL7_0 FM(SEL_AVB0_AVTP_PPS_0) FM(SEL_AVB0_AVTP_PPS_1) + +/* MOD_SEL8 */ /* 0 */ /* 1 */ +#define MOD_SEL8_11 FM(SEL_SDA5_0) FM(SEL_SDA5_1) +#define MOD_SEL8_10 FM(SEL_SCL5_0) FM(SEL_SCL5_1) +#define MOD_SEL8_9 FM(SEL_SDA4_0) FM(SEL_SDA4_1) +#define MOD_SEL8_8 FM(SEL_SCL4_0) FM(SEL_SCL4_1) +#define MOD_SEL8_7 FM(SEL_SDA3_0) FM(SEL_SDA3_1) +#define MOD_SEL8_6 FM(SEL_SCL3_0) FM(SEL_SCL3_1) +#define MOD_SEL8_5 FM(SEL_SDA2_0) FM(SEL_SDA2_1) +#define MOD_SEL8_4 FM(SEL_SCL2_0) FM(SEL_SCL2_1) +#define MOD_SEL8_3 FM(SEL_SDA1_0) FM(SEL_SDA1_1) +#define MOD_SEL8_2 FM(SEL_SCL1_0) FM(SEL_SCL1_1) +#define MOD_SEL8_1 FM(SEL_SDA0_0) FM(SEL_SDA0_1) +#define MOD_SEL8_0 FM(SEL_SCL0_0) FM(SEL_SCL0_1) + +#define PINMUX_MOD_SELS \ +\ +MOD_SEL4_19 MOD_SEL5_19 \ +MOD_SEL4_18 MOD_SEL6_18 \ + \ + MOD_SEL5_16 MOD_SEL6_16 MOD_SEL7_16 \ +MOD_SEL4_15 MOD_SEL5_15 MOD_SEL7_15 \ +MOD_SEL4_14 \ + MOD_SEL6_13 MOD_SEL7_13 \ +MOD_SEL4_12 MOD_SEL5_12 MOD_SEL6_12 \ + MOD_SEL5_11 MOD_SEL7_11 MOD_SEL8_11 \ + MOD_SEL6_10 MOD_SEL7_10 MOD_SEL8_10 \ +MOD_SEL4_9 MOD_SEL8_9 \ +MOD_SEL4_8 MOD_SEL5_8 MOD_SEL8_8 \ + MOD_SEL6_7 MOD_SEL7_7 MOD_SEL8_7 \ + MOD_SEL5_6 MOD_SEL6_6 MOD_SEL7_6 MOD_SEL8_6 \ +MOD_SEL4_5 MOD_SEL5_5 MOD_SEL6_5 MOD_SEL8_5 \ + MOD_SEL8_4 \ + MOD_SEL7_3 MOD_SEL8_3 \ +MOD_SEL4_2 MOD_SEL5_2 MOD_SEL6_2 MOD_SEL7_2 MOD_SEL8_2 \ +MOD_SEL4_1 MOD_SEL6_1 MOD_SEL8_1 \ + MOD_SEL5_0 MOD_SEL7_0 MOD_SEL8_0 + +enum { + PINMUX_RESERVED = 0, + + PINMUX_DATA_BEGIN, + GP_ALL(DATA), + PINMUX_DATA_END, + +#define F_(x, y) +#define FM(x) FN_##x, + PINMUX_FUNCTION_BEGIN, + GP_ALL(FN), + PINMUX_GPSR + PINMUX_IPSR + PINMUX_MOD_SELS + PINMUX_FUNCTION_END, +#undef F_ +#undef FM + +#define F_(x, y) +#define FM(x) x##_MARK, + PINMUX_MARK_BEGIN, + PINMUX_GPSR + PINMUX_IPSR + PINMUX_MOD_SELS + PINMUX_MARK_END, +#undef F_ +#undef FM +}; + +static const u16 pinmux_data[] = { + PINMUX_DATA_GP_ALL(), + + PINMUX_SINGLE(AVS1), + PINMUX_SINGLE(AVS0), + PINMUX_SINGLE(PCIE1_CLKREQ_N), + PINMUX_SINGLE(PCIE0_CLKREQ_N), + + /* TSN0 without MODSEL4 */ + PINMUX_SINGLE(TSN0_TXCREFCLK), + PINMUX_SINGLE(TSN0_RD2), + PINMUX_SINGLE(TSN0_RD3), + PINMUX_SINGLE(TSN0_RD1), + PINMUX_SINGLE(TSN0_RXC), + PINMUX_SINGLE(TSN0_RD0), + PINMUX_SINGLE(TSN0_RX_CTL), + PINMUX_SINGLE(TSN0_AVTP_CAPTURE), + PINMUX_SINGLE(TSN0_LINK), + PINMUX_SINGLE(TSN0_PHY_INT), + PINMUX_SINGLE(TSN0_MDIO), + /* TSN0 with MODSEL4 */ + PINMUX_IPSR_NOGM(0, TSN0_TD2, SEL_TSN0_TD2_1), + PINMUX_IPSR_NOGM(0, TSN0_TD3, SEL_TSN0_TD3_1), + PINMUX_IPSR_NOGM(0, TSN0_TD0, SEL_TSN0_TD0_1), + PINMUX_IPSR_NOGM(0, TSN0_TD1, SEL_TSN0_TD1_1), + PINMUX_IPSR_NOGM(0, TSN0_TXC, SEL_TSN0_TXC_1), + PINMUX_IPSR_NOGM(0, TSN0_TX_CTL, SEL_TSN0_TX_CTL_1), + PINMUX_IPSR_NOGM(0, TSN0_AVTP_PPS0, SEL_TSN0_AVTP_PPS0_1), + PINMUX_IPSR_NOGM(0, TSN0_AVTP_MATCH, SEL_TSN0_AVTP_MATCH_1), + PINMUX_IPSR_NOGM(0, TSN0_AVTP_PPS1, SEL_TSN0_AVTP_PPS1_1), + PINMUX_IPSR_NOGM(0, TSN0_MDC, SEL_TSN0_MDC_1), + + /* TSN0 without MODSEL5 */ + PINMUX_SINGLE(AVB2_RX_CTL), + PINMUX_SINGLE(AVB2_RXC), + PINMUX_SINGLE(AVB2_RD0), + PINMUX_SINGLE(AVB2_RD1), + PINMUX_SINGLE(AVB2_RD2), + PINMUX_SINGLE(AVB2_MDIO), + PINMUX_SINGLE(AVB2_RD3), + PINMUX_SINGLE(AVB2_TXCREFCLK), + PINMUX_SINGLE(AVB2_PHY_INT), + PINMUX_SINGLE(AVB2_LINK), + PINMUX_SINGLE(AVB2_AVTP_CAPTURE), + /* TSN0 with MODSEL5 */ + PINMUX_IPSR_NOGM(0, AVB2_TX_CTL, SEL_AVB2_TX_CTL_1), + PINMUX_IPSR_NOGM(0, AVB2_TXC, SEL_AVB2_TXC_1), + PINMUX_IPSR_NOGM(0, AVB2_TD0, SEL_AVB2_TD0_1), + PINMUX_IPSR_NOGM(0, AVB2_TD1, SEL_AVB2_TD1_1), + PINMUX_IPSR_NOGM(0, AVB2_TD2, SEL_AVB2_TD2_1), + PINMUX_IPSR_NOGM(0, AVB2_TD3, SEL_AVB2_TD3_1), + PINMUX_IPSR_NOGM(0, AVB2_MDC, SEL_AVB2_MDC_1), + PINMUX_IPSR_NOGM(0, AVB2_MAGIC, SEL_AVB2_MAGIC_1), + PINMUX_IPSR_NOGM(0, AVB2_AVTP_MATCH, SEL_AVB2_AVTP_MATCH_1), + PINMUX_IPSR_NOGM(0, AVB2_AVTP_PPS, SEL_AVB2_AVTP_PPS_1), + + /* IP0SR0 */ + PINMUX_IPSR_GPSR(IP0SR0_3_0, ERROROUTC_B), + PINMUX_IPSR_GPSR(IP0SR0_3_0, TCLK2_A), + + PINMUX_IPSR_GPSR(IP0SR0_7_4, MSIOF3_SS1), + + PINMUX_IPSR_GPSR(IP0SR0_11_8, MSIOF3_SS2), + + PINMUX_IPSR_GPSR(IP0SR0_15_12, IRQ3), + PINMUX_IPSR_GPSR(IP0SR0_15_12, MSIOF3_SCK), + + PINMUX_IPSR_GPSR(IP0SR0_19_16, IRQ2), + PINMUX_IPSR_GPSR(IP0SR0_19_16, MSIOF3_TXD), + + PINMUX_IPSR_GPSR(IP0SR0_23_20, IRQ1), + PINMUX_IPSR_GPSR(IP0SR0_23_20, MSIOF3_RXD), + + PINMUX_IPSR_GPSR(IP0SR0_27_24, IRQ0), + PINMUX_IPSR_GPSR(IP0SR0_27_24, MSIOF3_SYNC), + + PINMUX_IPSR_GPSR(IP0SR0_31_28, MSIOF5_SS2), + + /* IP1SR0 */ + PINMUX_IPSR_GPSR(IP1SR0_3_0, MSIOF5_SS1), + + PINMUX_IPSR_GPSR(IP1SR0_7_4, MSIOF5_SYNC), + + PINMUX_IPSR_GPSR(IP1SR0_11_8, MSIOF5_TXD), + + PINMUX_IPSR_GPSR(IP1SR0_15_12, MSIOF5_SCK), + + PINMUX_IPSR_GPSR(IP1SR0_19_16, MSIOF5_RXD), + + PINMUX_IPSR_GPSR(IP1SR0_23_20, MSIOF2_SS2), + PINMUX_IPSR_GPSR(IP1SR0_23_20, TCLK1), + PINMUX_IPSR_GPSR(IP1SR0_23_20, IRQ2_A), + + PINMUX_IPSR_GPSR(IP1SR0_27_24, MSIOF2_SS1), + PINMUX_IPSR_GPSR(IP1SR0_27_24, HTX1), + PINMUX_IPSR_GPSR(IP1SR0_27_24, TX1), + + PINMUX_IPSR_GPSR(IP1SR0_31_28, MSIOF2_SYNC), + PINMUX_IPSR_GPSR(IP1SR0_31_28, HRX1), + PINMUX_IPSR_GPSR(IP1SR0_31_28, RX1), + + /* IP2SR0 */ + PINMUX_IPSR_GPSR(IP2SR0_3_0, MSIOF2_TXD), + PINMUX_IPSR_GPSR(IP2SR0_3_0, HCTS1_N), + PINMUX_IPSR_GPSR(IP2SR0_3_0, CTS1_N), + + PINMUX_IPSR_GPSR(IP2SR0_7_4, MSIOF2_SCK), + PINMUX_IPSR_GPSR(IP2SR0_7_4, HRTS1_N), + PINMUX_IPSR_GPSR(IP2SR0_7_4, RTS1_N), + + PINMUX_IPSR_GPSR(IP2SR0_11_8, MSIOF2_RXD), + PINMUX_IPSR_GPSR(IP2SR0_11_8, HSCK1), + PINMUX_IPSR_GPSR(IP2SR0_11_8, SCK1), + + /* IP0SR1 */ + PINMUX_IPSR_GPSR(IP0SR1_3_0, MSIOF1_SS2), + PINMUX_IPSR_GPSR(IP0SR1_3_0, HTX3_A), + PINMUX_IPSR_GPSR(IP0SR1_3_0, TX3), + + PINMUX_IPSR_GPSR(IP0SR1_7_4, MSIOF1_SS1), + PINMUX_IPSR_GPSR(IP0SR1_7_4, HCTS3_N_A), + PINMUX_IPSR_GPSR(IP0SR1_7_4, RX3), + + PINMUX_IPSR_GPSR(IP0SR1_11_8, MSIOF1_SYNC), + PINMUX_IPSR_GPSR(IP0SR1_11_8, HRTS3_N_A), + PINMUX_IPSR_GPSR(IP0SR1_11_8, RTS3_N), + + PINMUX_IPSR_GPSR(IP0SR1_15_12, MSIOF1_SCK), + PINMUX_IPSR_GPSR(IP0SR1_15_12, HSCK3_A), + PINMUX_IPSR_GPSR(IP0SR1_15_12, CTS3_N), + + PINMUX_IPSR_GPSR(IP0SR1_19_16, MSIOF1_TXD), + PINMUX_IPSR_GPSR(IP0SR1_19_16, HRX3_A), + PINMUX_IPSR_GPSR(IP0SR1_19_16, SCK3), + + PINMUX_IPSR_GPSR(IP0SR1_23_20, MSIOF1_RXD), + + PINMUX_IPSR_GPSR(IP0SR1_27_24, MSIOF0_SS2), + PINMUX_IPSR_GPSR(IP0SR1_27_24, HTX1_X), + PINMUX_IPSR_GPSR(IP0SR1_27_24, TX1_X), + + PINMUX_IPSR_GPSR(IP0SR1_31_28, MSIOF0_SS1), + PINMUX_IPSR_GPSR(IP0SR1_31_28, HRX1_X), + PINMUX_IPSR_GPSR(IP0SR1_31_28, RX1_X), + + /* IP1SR1 */ + PINMUX_IPSR_GPSR(IP1SR1_3_0, MSIOF0_SYNC), + PINMUX_IPSR_GPSR(IP1SR1_3_0, HCTS1_N_X), + PINMUX_IPSR_GPSR(IP1SR1_3_0, CTS1_N_X), + PINMUX_IPSR_GPSR(IP1SR1_3_0, CANFD5_TX_B), + + PINMUX_IPSR_GPSR(IP1SR1_7_4, MSIOF0_TXD), + PINMUX_IPSR_GPSR(IP1SR1_7_4, HRTS1_N_X), + PINMUX_IPSR_GPSR(IP1SR1_7_4, RTS1_N_X), + PINMUX_IPSR_GPSR(IP1SR1_7_4, CANFD5_RX_B), + + PINMUX_IPSR_GPSR(IP1SR1_11_8, MSIOF0_SCK), + PINMUX_IPSR_GPSR(IP1SR1_11_8, HSCK1_X), + PINMUX_IPSR_GPSR(IP1SR1_11_8, SCK1_X), + + PINMUX_IPSR_GPSR(IP1SR1_15_12, MSIOF0_RXD), + + PINMUX_IPSR_GPSR(IP1SR1_19_16, HTX0), + PINMUX_IPSR_GPSR(IP1SR1_19_16, TX0), + + PINMUX_IPSR_GPSR(IP1SR1_23_20, HCTS0_N), + PINMUX_IPSR_GPSR(IP1SR1_23_20, CTS0_N), + PINMUX_IPSR_GPSR(IP1SR1_23_20, PWM8_A), + + PINMUX_IPSR_GPSR(IP1SR1_27_24, HRTS0_N), + PINMUX_IPSR_GPSR(IP1SR1_27_24, RTS0_N), + PINMUX_IPSR_GPSR(IP1SR1_27_24, PWM9_A), + + PINMUX_IPSR_GPSR(IP1SR1_31_28, HSCK0), + PINMUX_IPSR_GPSR(IP1SR1_31_28, SCK0), + PINMUX_IPSR_GPSR(IP1SR1_31_28, PWM0_A), + + /* IP2SR1 */ + PINMUX_IPSR_GPSR(IP2SR1_3_0, HRX0), + PINMUX_IPSR_GPSR(IP2SR1_3_0, RX0), + + PINMUX_IPSR_GPSR(IP2SR1_7_4, SCIF_CLK), + PINMUX_IPSR_GPSR(IP2SR1_7_4, IRQ4_A), + + PINMUX_IPSR_GPSR(IP2SR1_11_8, SSI_SCK), + PINMUX_IPSR_GPSR(IP2SR1_11_8, TCLK3), + + PINMUX_IPSR_GPSR(IP2SR1_15_12, SSI_WS), + PINMUX_IPSR_GPSR(IP2SR1_15_12, TCLK4), + + PINMUX_IPSR_GPSR(IP2SR1_19_16, SSI_SD), + PINMUX_IPSR_GPSR(IP2SR1_19_16, IRQ0_A), + + PINMUX_IPSR_GPSR(IP2SR1_23_20, AUDIO_CLKOUT), + PINMUX_IPSR_GPSR(IP2SR1_23_20, IRQ1_A), + + PINMUX_IPSR_GPSR(IP2SR1_27_24, AUDIO_CLKIN), + PINMUX_IPSR_GPSR(IP2SR1_27_24, PWM3_A), + + PINMUX_IPSR_GPSR(IP2SR1_31_28, TCLK2), + PINMUX_IPSR_GPSR(IP2SR1_31_28, MSIOF4_SS1), + PINMUX_IPSR_GPSR(IP2SR1_31_28, IRQ3_B), + + /* IP3SR1 */ + PINMUX_IPSR_GPSR(IP3SR1_3_0, HRX3), + PINMUX_IPSR_GPSR(IP3SR1_3_0, SCK3_A), + PINMUX_IPSR_GPSR(IP3SR1_3_0, MSIOF4_SS2), + + PINMUX_IPSR_GPSR(IP3SR1_7_4, HSCK3), + PINMUX_IPSR_GPSR(IP3SR1_7_4, CTS3_N_A), + PINMUX_IPSR_GPSR(IP3SR1_7_4, MSIOF4_SCK), + PINMUX_IPSR_GPSR(IP3SR1_7_4, TPU0TO0_A), + + PINMUX_IPSR_GPSR(IP3SR1_11_8, HRTS3_N), + PINMUX_IPSR_GPSR(IP3SR1_11_8, RTS3_N_A), + PINMUX_IPSR_GPSR(IP3SR1_11_8, MSIOF4_TXD), + PINMUX_IPSR_GPSR(IP3SR1_11_8, TPU0TO1_A), + + PINMUX_IPSR_GPSR(IP3SR1_15_12, HCTS3_N), + PINMUX_IPSR_GPSR(IP3SR1_15_12, RX3_A), + PINMUX_IPSR_GPSR(IP3SR1_15_12, MSIOF4_RXD), + + PINMUX_IPSR_GPSR(IP3SR1_19_16, HTX3), + PINMUX_IPSR_GPSR(IP3SR1_19_16, TX3_A), + PINMUX_IPSR_GPSR(IP3SR1_19_16, MSIOF4_SYNC), + + /* IP0SR2 */ + PINMUX_IPSR_GPSR(IP0SR2_3_0, FXR_TXDA), + PINMUX_IPSR_GPSR(IP0SR2_3_0, CANFD1_TX), + PINMUX_IPSR_GPSR(IP0SR2_3_0, TPU0TO2_A), + + PINMUX_IPSR_GPSR(IP0SR2_7_4, FXR_TXENA_N), + PINMUX_IPSR_GPSR(IP0SR2_7_4, CANFD1_RX), + PINMUX_IPSR_GPSR(IP0SR2_7_4, TPU0TO3_A), + + PINMUX_IPSR_GPSR(IP0SR2_11_8, RXDA_EXTFXR), + PINMUX_IPSR_GPSR(IP0SR2_11_8, CANFD5_TX), + PINMUX_IPSR_GPSR(IP0SR2_11_8, IRQ5), + + PINMUX_IPSR_GPSR(IP0SR2_15_12, CLK_EXTFXR), + PINMUX_IPSR_GPSR(IP0SR2_15_12, CANFD5_RX), + PINMUX_IPSR_GPSR(IP0SR2_15_12, IRQ4_B), + + PINMUX_IPSR_GPSR(IP0SR2_19_16, RXDB_EXTFXR), + + PINMUX_IPSR_GPSR(IP0SR2_23_20, FXR_TXENB_N), + + PINMUX_IPSR_GPSR(IP0SR2_27_24, FXR_TXDB), + + PINMUX_IPSR_GPSR(IP0SR2_31_28, TPU0TO1), + PINMUX_IPSR_GPSR(IP0SR2_31_28, CANFD6_TX), + PINMUX_IPSR_GPSR(IP0SR2_31_28, TCLK2_B), + + /* IP1SR2 */ + PINMUX_IPSR_GPSR(IP1SR2_3_0, TPU0TO0), + PINMUX_IPSR_GPSR(IP1SR2_3_0, CANFD6_RX), + PINMUX_IPSR_GPSR(IP1SR2_3_0, TCLK1_A), + + PINMUX_IPSR_GPSR(IP1SR2_7_4, CAN_CLK), + PINMUX_IPSR_GPSR(IP1SR2_7_4, FXR_TXENA_N_X), + + PINMUX_IPSR_GPSR(IP1SR2_11_8, CANFD0_TX), + PINMUX_IPSR_GPSR(IP1SR2_11_8, FXR_TXENB_N_X), + + PINMUX_IPSR_GPSR(IP1SR2_15_12, CANFD0_RX), + PINMUX_IPSR_GPSR(IP1SR2_15_12, STPWT_EXTFXR), + + PINMUX_IPSR_GPSR(IP1SR2_19_16, CANFD2_TX), + PINMUX_IPSR_GPSR(IP1SR2_19_16, TPU0TO2), + PINMUX_IPSR_GPSR(IP1SR2_19_16, TCLK3_A), + + PINMUX_IPSR_GPSR(IP1SR2_23_20, CANFD2_RX), + PINMUX_IPSR_GPSR(IP1SR2_23_20, TPU0TO3), + PINMUX_IPSR_GPSR(IP1SR2_23_20, PWM1_B), + PINMUX_IPSR_GPSR(IP1SR2_23_20, TCLK4_A), + + PINMUX_IPSR_GPSR(IP1SR2_27_24, CANFD3_TX), + PINMUX_IPSR_GPSR(IP1SR2_27_24, PWM2_B), + + PINMUX_IPSR_GPSR(IP1SR2_31_28, CANFD3_RX), + PINMUX_IPSR_GPSR(IP1SR2_31_28, PWM3_B), + + /* IP2SR2 */ + PINMUX_IPSR_GPSR(IP2SR2_3_0, CANFD4_TX), + PINMUX_IPSR_GPSR(IP2SR2_3_0, PWM4), + + PINMUX_IPSR_GPSR(IP2SR2_7_4, CANFD4_RX), + PINMUX_IPSR_GPSR(IP2SR2_7_4, PWM5), + + PINMUX_IPSR_GPSR(IP2SR2_11_8, CANFD7_TX), + PINMUX_IPSR_GPSR(IP2SR2_11_8, PWM6), + + PINMUX_IPSR_GPSR(IP2SR2_15_12, CANFD7_RX), + PINMUX_IPSR_GPSR(IP2SR2_15_12, PWM7), + + /* IP0SR3 */ + PINMUX_IPSR_GPSR(IP0SR3_3_0, MMC_SD_D1), + PINMUX_IPSR_GPSR(IP0SR3_7_4, MMC_SD_D0), + PINMUX_IPSR_GPSR(IP0SR3_11_8, MMC_SD_D2), + PINMUX_IPSR_GPSR(IP0SR3_15_12, MMC_SD_CLK), + PINMUX_IPSR_GPSR(IP0SR3_19_16, MMC_DS), + PINMUX_IPSR_GPSR(IP0SR3_23_20, MMC_SD_D3), + PINMUX_IPSR_GPSR(IP0SR3_27_24, MMC_D5), + PINMUX_IPSR_GPSR(IP0SR3_31_28, MMC_D4), + + /* IP1SR3 */ + PINMUX_IPSR_GPSR(IP1SR3_3_0, MMC_D7), + + PINMUX_IPSR_GPSR(IP1SR3_7_4, MMC_D6), + + PINMUX_IPSR_GPSR(IP1SR3_11_8, MMC_SD_CMD), + + PINMUX_IPSR_GPSR(IP1SR3_15_12, SD_CD), + + PINMUX_IPSR_GPSR(IP1SR3_19_16, SD_WP), + + PINMUX_IPSR_GPSR(IP1SR3_23_20, IPC_CLKIN), + PINMUX_IPSR_GPSR(IP1SR3_23_20, IPC_CLKEN_IN), + PINMUX_IPSR_GPSR(IP1SR3_23_20, PWM1_A), + PINMUX_IPSR_GPSR(IP1SR3_23_20, TCLK3_X), + + PINMUX_IPSR_GPSR(IP1SR3_27_24, IPC_CLKOUT), + PINMUX_IPSR_GPSR(IP1SR3_27_24, IPC_CLKEN_OUT), + PINMUX_IPSR_GPSR(IP1SR3_27_24, ERROROUTC_A), + PINMUX_IPSR_GPSR(IP1SR3_27_24, TCLK4_X), + + PINMUX_IPSR_GPSR(IP1SR3_31_28, QSPI0_SSL), + + /* IP2SR3 */ + PINMUX_IPSR_GPSR(IP2SR3_3_0, QSPI0_IO3), + PINMUX_IPSR_GPSR(IP2SR3_7_4, QSPI0_IO2), + PINMUX_IPSR_GPSR(IP2SR3_11_8, QSPI0_MISO_IO1), + PINMUX_IPSR_GPSR(IP2SR3_15_12, QSPI0_MOSI_IO0), + PINMUX_IPSR_GPSR(IP2SR3_19_16, QSPI0_SPCLK), + PINMUX_IPSR_GPSR(IP2SR3_23_20, QSPI1_MOSI_IO0), + PINMUX_IPSR_GPSR(IP2SR3_27_24, QSPI1_SPCLK), + PINMUX_IPSR_GPSR(IP2SR3_31_28, QSPI1_MISO_IO1), + + /* IP3SR3 */ + PINMUX_IPSR_GPSR(IP3SR3_3_0, QSPI1_IO2), + PINMUX_IPSR_GPSR(IP3SR3_7_4, QSPI1_SSL), + PINMUX_IPSR_GPSR(IP3SR3_11_8, QSPI1_IO3), + PINMUX_IPSR_GPSR(IP3SR3_15_12, RPC_RESET_N), + PINMUX_IPSR_GPSR(IP3SR3_19_16, RPC_WP_N), + PINMUX_IPSR_GPSR(IP3SR3_23_20, RPC_INT_N), + + /* IP0SR6 */ + PINMUX_IPSR_GPSR(IP0SR6_3_0, AVB1_MDIO), + + PINMUX_IPSR_MSEL(IP0SR6_7_4, AVB1_MAGIC, SEL_AVB1_MAGIC_1), + + PINMUX_IPSR_MSEL(IP0SR6_11_8, AVB1_MDC, SEL_AVB1_MDC_1), + + PINMUX_IPSR_GPSR(IP0SR6_15_12, AVB1_PHY_INT), + + PINMUX_IPSR_GPSR(IP0SR6_19_16, AVB1_LINK), + PINMUX_IPSR_GPSR(IP0SR6_19_16, AVB1_MII_TX_ER), + + PINMUX_IPSR_MSEL(IP0SR6_23_20, AVB1_AVTP_MATCH, SEL_AVB1_AVTP_MATCH_1), + PINMUX_IPSR_MSEL(IP0SR6_23_20, AVB1_MII_RX_ER, SEL_AVB1_AVTP_MATCH_0), + + PINMUX_IPSR_MSEL(IP0SR6_27_24, AVB1_TXC, SEL_AVB1_TXC_1), + PINMUX_IPSR_MSEL(IP0SR6_27_24, AVB1_MII_TXC, SEL_AVB1_TXC_0), + + PINMUX_IPSR_MSEL(IP0SR6_31_28, AVB1_TX_CTL, SEL_AVB1_TX_CTL_1), + PINMUX_IPSR_MSEL(IP0SR6_31_28, AVB1_MII_TX_EN, SEL_AVB1_TX_CTL_0), + + /* IP1SR6 */ + PINMUX_IPSR_GPSR(IP1SR6_3_0, AVB1_RXC), + PINMUX_IPSR_GPSR(IP1SR6_3_0, AVB1_MII_RXC), + + PINMUX_IPSR_GPSR(IP1SR6_7_4, AVB1_RX_CTL), + PINMUX_IPSR_GPSR(IP1SR6_7_4, AVB1_MII_RX_DV), + + PINMUX_IPSR_MSEL(IP1SR6_11_8, AVB1_AVTP_PPS, SEL_AVB1_AVTP_PPS_1), + PINMUX_IPSR_MSEL(IP1SR6_11_8, AVB1_MII_COL, SEL_AVB1_AVTP_PPS_0), + + PINMUX_IPSR_GPSR(IP1SR6_15_12, AVB1_AVTP_CAPTURE), + PINMUX_IPSR_GPSR(IP1SR6_15_12, AVB1_MII_CRS), + + PINMUX_IPSR_MSEL(IP1SR6_19_16, AVB1_TD1, SEL_AVB1_TD1_1), + PINMUX_IPSR_MSEL(IP1SR6_19_16, AVB1_MII_TD1, SEL_AVB1_TD1_0), + + PINMUX_IPSR_MSEL(IP1SR6_23_20, AVB1_TD0, SEL_AVB1_TD0_1), + PINMUX_IPSR_MSEL(IP1SR6_23_20, AVB1_MII_TD0, SEL_AVB1_TD0_0), + + PINMUX_IPSR_GPSR(IP1SR6_27_24, AVB1_RD1), + PINMUX_IPSR_GPSR(IP1SR6_27_24, AVB1_MII_RD1), + + PINMUX_IPSR_GPSR(IP1SR6_31_28, AVB1_RD0), + PINMUX_IPSR_GPSR(IP1SR6_31_28, AVB1_MII_RD0), + + /* IP2SR6 */ + PINMUX_IPSR_MSEL(IP2SR6_3_0, AVB1_TD2, SEL_AVB1_TD2_1), + PINMUX_IPSR_MSEL(IP2SR6_3_0, AVB1_MII_TD2, SEL_AVB1_TD2_0), + + PINMUX_IPSR_GPSR(IP2SR6_7_4, AVB1_RD2), + PINMUX_IPSR_GPSR(IP2SR6_7_4, AVB1_MII_RD2), + + PINMUX_IPSR_MSEL(IP2SR6_11_8, AVB1_TD3, SEL_AVB1_TD3_1), + PINMUX_IPSR_MSEL(IP2SR6_11_8, AVB1_MII_TD3, SEL_AVB1_TD3_0), + + PINMUX_IPSR_GPSR(IP2SR6_15_12, AVB1_RD3), + PINMUX_IPSR_GPSR(IP2SR6_15_12, AVB1_MII_RD3), + + PINMUX_IPSR_GPSR(IP2SR6_19_16, AVB1_TXCREFCLK), + + /* IP0SR7 */ + PINMUX_IPSR_MSEL(IP0SR7_3_0, AVB0_AVTP_PPS, SEL_AVB0_AVTP_PPS_1), + PINMUX_IPSR_MSEL(IP0SR7_3_0, AVB0_MII_COL, SEL_AVB0_AVTP_PPS_0), + + PINMUX_IPSR_GPSR(IP0SR7_7_4, AVB0_AVTP_CAPTURE), + PINMUX_IPSR_GPSR(IP0SR7_7_4, AVB0_MII_CRS), + + PINMUX_IPSR_MSEL(IP0SR7_11_8, AVB0_AVTP_MATCH, SEL_AVB0_AVTP_MATCH_1), + PINMUX_IPSR_MSEL(IP0SR7_11_8, AVB0_MII_RX_ER, SEL_AVB0_AVTP_MATCH_0), + PINMUX_IPSR_MSEL(IP0SR7_11_8, CC5_OSCOUT, SEL_AVB0_AVTP_MATCH_0), + + PINMUX_IPSR_MSEL(IP0SR7_15_12, AVB0_TD3, SEL_AVB0_TD3_1), + PINMUX_IPSR_MSEL(IP0SR7_15_12, AVB0_MII_TD3, SEL_AVB0_TD3_0), + + PINMUX_IPSR_GPSR(IP0SR7_19_16, AVB0_LINK), + PINMUX_IPSR_GPSR(IP0SR7_19_16, AVB0_MII_TX_ER), + + PINMUX_IPSR_GPSR(IP0SR7_23_20, AVB0_PHY_INT), + + PINMUX_IPSR_MSEL(IP0SR7_27_24, AVB0_TD2, SEL_AVB0_TD2_1), + PINMUX_IPSR_MSEL(IP0SR7_27_24, AVB0_MII_TD2, SEL_AVB0_TD2_0), + + PINMUX_IPSR_MSEL(IP0SR7_31_28, AVB0_TD1, SEL_AVB0_TD1_1), + PINMUX_IPSR_MSEL(IP0SR7_31_28, AVB0_MII_TD1, SEL_AVB0_TD1_0), + + /* IP1SR7 */ + PINMUX_IPSR_GPSR(IP1SR7_3_0, AVB0_RD3), + PINMUX_IPSR_GPSR(IP1SR7_3_0, AVB0_MII_RD3), + + PINMUX_IPSR_GPSR(IP1SR7_7_4, AVB0_TXCREFCLK), + + PINMUX_IPSR_MSEL(IP1SR7_11_8, AVB0_MAGIC, SEL_AVB0_MAGIC_1), + + PINMUX_IPSR_MSEL(IP1SR7_15_12, AVB0_TD0, SEL_AVB0_TD0_1), + PINMUX_IPSR_MSEL(IP1SR7_15_12, AVB0_MII_TD0, SEL_AVB0_TD0_0), + + PINMUX_IPSR_GPSR(IP1SR7_19_16, AVB0_RD2), + PINMUX_IPSR_GPSR(IP1SR7_19_16, AVB0_MII_RD2), + + PINMUX_IPSR_MSEL(IP1SR7_23_20, AVB0_MDC, SEL_AVB0_MDC_1), + + PINMUX_IPSR_GPSR(IP1SR7_27_24, AVB0_MDIO), + + PINMUX_IPSR_MSEL(IP1SR7_31_28, AVB0_TXC, SEL_AVB0_TXC_1), + PINMUX_IPSR_MSEL(IP1SR7_31_28, AVB0_MII_TXC, SEL_AVB0_TXC_0), + + /* IP2SR7 */ + PINMUX_IPSR_MSEL(IP2SR7_3_0, AVB0_TX_CTL, SEL_AVB0_TX_CTL_1), + PINMUX_IPSR_MSEL(IP2SR7_3_0, AVB0_MII_TX_EN, SEL_AVB0_TX_CTL_0), + + PINMUX_IPSR_GPSR(IP2SR7_7_4, AVB0_RD1), + PINMUX_IPSR_GPSR(IP2SR7_7_4, AVB0_MII_RD1), + + PINMUX_IPSR_GPSR(IP2SR7_11_8, AVB0_RD0), + PINMUX_IPSR_GPSR(IP2SR7_11_8, AVB0_MII_RD0), + + PINMUX_IPSR_GPSR(IP2SR7_15_12, AVB0_RXC), + PINMUX_IPSR_GPSR(IP2SR7_15_12, AVB0_MII_RXC), + + PINMUX_IPSR_GPSR(IP2SR7_19_16, AVB0_RX_CTL), + PINMUX_IPSR_GPSR(IP2SR7_19_16, AVB0_MII_RX_DV), + + /* IP0SR8 */ + PINMUX_IPSR_MSEL(IP0SR8_3_0, SCL0, SEL_SCL0_0), + PINMUX_IPSR_MSEL(IP0SR8_7_4, SDA0, SEL_SDA0_0), + PINMUX_IPSR_MSEL(IP0SR8_11_8, SCL1, SEL_SCL1_0), + PINMUX_IPSR_MSEL(IP0SR8_15_12, SDA1, SEL_SDA1_0), + PINMUX_IPSR_MSEL(IP0SR8_19_16, SCL2, SEL_SCL2_0), + PINMUX_IPSR_MSEL(IP0SR8_23_20, SDA2, SEL_SDA2_0), + PINMUX_IPSR_MSEL(IP0SR8_27_24, SCL3, SEL_SCL3_0), + PINMUX_IPSR_MSEL(IP0SR8_31_28, SDA3, SEL_SDA3_0), + + /* IP1SR8 */ + PINMUX_IPSR_MSEL(IP1SR8_3_0, SCL4, SEL_SCL4_0), + PINMUX_IPSR_MSEL(IP1SR8_3_0, HRX2, SEL_SCL4_0), + PINMUX_IPSR_MSEL(IP1SR8_3_0, SCK4, SEL_SCL4_0), + + PINMUX_IPSR_MSEL(IP1SR8_7_4, SDA4, SEL_SDA4_0), + PINMUX_IPSR_MSEL(IP1SR8_7_4, HTX2, SEL_SDA4_0), + PINMUX_IPSR_MSEL(IP1SR8_7_4, CTS4_N, SEL_SDA4_0), + + PINMUX_IPSR_MSEL(IP1SR8_11_8, SCL5, SEL_SCL5_0), + PINMUX_IPSR_MSEL(IP1SR8_11_8, HRTS2_N, SEL_SCL5_0), + PINMUX_IPSR_MSEL(IP1SR8_11_8, RTS4_N, SEL_SCL5_0), + + PINMUX_IPSR_MSEL(IP1SR8_15_12, SDA5, SEL_SDA5_0), + PINMUX_IPSR_MSEL(IP1SR8_15_12, SCIF_CLK2, SEL_SDA5_0), + + PINMUX_IPSR_GPSR(IP1SR8_19_16, HCTS2_N), + PINMUX_IPSR_GPSR(IP1SR8_19_16, TX4), + + PINMUX_IPSR_GPSR(IP1SR8_23_20, HSCK2), + PINMUX_IPSR_GPSR(IP1SR8_23_20, RX4), +}; + +/* + * Pins not associated with a GPIO port. + */ +enum { + GP_ASSIGN_LAST(), +}; + +static const struct sh_pfc_pin pinmux_pins[] = { + PINMUX_GPIO_GP_ALL(), +}; + +/* - AVB0 ------------------------------------------------ */ +static const unsigned int avb0_link_pins[] = { + /* AVB0_LINK */ + RCAR_GP_PIN(7, 4), +}; +static const unsigned int avb0_link_mux[] = { + AVB0_LINK_MARK, +}; +static const unsigned int avb0_magic_pins[] = { + /* AVB0_MAGIC */ + RCAR_GP_PIN(7, 10), +}; +static const unsigned int avb0_magic_mux[] = { + AVB0_MAGIC_MARK, +}; +static const unsigned int avb0_phy_int_pins[] = { + /* AVB0_PHY_INT */ + RCAR_GP_PIN(7, 5), +}; +static const unsigned int avb0_phy_int_mux[] = { + AVB0_PHY_INT_MARK, +}; +static const unsigned int avb0_mdio_pins[] = { + /* AVB0_MDC, AVB0_MDIO */ + RCAR_GP_PIN(7, 13), RCAR_GP_PIN(7, 14), +}; +static const unsigned int avb0_mdio_mux[] = { + AVB0_MDC_MARK, AVB0_MDIO_MARK, +}; +static const unsigned int avb0_rgmii_pins[] = { + /* + * AVB0_TX_CTL, AVB0_TXC, AVB0_TD0, AVB0_TD1, AVB0_TD2, AVB0_TD3, + * AVB0_RX_CTL, AVB0_RXC, AVB0_RD0, AVB0_RD1, AVB0_RD2, AVB0_RD3, + */ + RCAR_GP_PIN(7, 16), RCAR_GP_PIN(7, 15), + RCAR_GP_PIN(7, 11), RCAR_GP_PIN(7, 7), + RCAR_GP_PIN(7, 6), RCAR_GP_PIN(7, 3), + RCAR_GP_PIN(7, 20), RCAR_GP_PIN(7, 19), + RCAR_GP_PIN(7, 18), RCAR_GP_PIN(7, 17), + RCAR_GP_PIN(7, 12), RCAR_GP_PIN(7, 8), +}; +static const unsigned int avb0_rgmii_mux[] = { + AVB0_TX_CTL_MARK, AVB0_TXC_MARK, + AVB0_TD0_MARK, AVB0_TD1_MARK, + AVB0_TD2_MARK, AVB0_TD3_MARK, + AVB0_RX_CTL_MARK, AVB0_RXC_MARK, + AVB0_RD0_MARK, AVB0_RD1_MARK, + AVB0_RD2_MARK, AVB0_RD3_MARK, +}; +static const unsigned int avb0_txcrefclk_pins[] = { + /* AVB0_TXCREFCLK */ + RCAR_GP_PIN(7, 9), +}; +static const unsigned int avb0_txcrefclk_mux[] = { + AVB0_TXCREFCLK_MARK, +}; +static const unsigned int avb0_avtp_pps_pins[] = { + /* AVB0_AVTP_PPS */ + RCAR_GP_PIN(7, 0), +}; +static const unsigned int avb0_avtp_pps_mux[] = { + AVB0_AVTP_PPS_MARK, +}; +static const unsigned int avb0_avtp_capture_pins[] = { + /* AVB0_AVTP_CAPTURE */ + RCAR_GP_PIN(7, 1), +}; +static const unsigned int avb0_avtp_capture_mux[] = { + AVB0_AVTP_CAPTURE_MARK, +}; +static const unsigned int avb0_avtp_match_pins[] = { + /* AVB0_AVTP_MATCH */ + RCAR_GP_PIN(7, 2), +}; +static const unsigned int avb0_avtp_match_mux[] = { + AVB0_AVTP_MATCH_MARK, +}; + +/* - AVB1 ------------------------------------------------ */ +static const unsigned int avb1_link_pins[] = { + /* AVB1_LINK */ + RCAR_GP_PIN(6, 4), +}; +static const unsigned int avb1_link_mux[] = { + AVB1_LINK_MARK, +}; +static const unsigned int avb1_magic_pins[] = { + /* AVB1_MAGIC */ + RCAR_GP_PIN(6, 1), +}; +static const unsigned int avb1_magic_mux[] = { + AVB1_MAGIC_MARK, +}; +static const unsigned int avb1_phy_int_pins[] = { + /* AVB1_PHY_INT */ + RCAR_GP_PIN(6, 3), +}; +static const unsigned int avb1_phy_int_mux[] = { + AVB1_PHY_INT_MARK, +}; +static const unsigned int avb1_mdio_pins[] = { + /* AVB1_MDC, AVB1_MDIO */ + RCAR_GP_PIN(6, 2), RCAR_GP_PIN(6, 0), +}; +static const unsigned int avb1_mdio_mux[] = { + AVB1_MDC_MARK, AVB1_MDIO_MARK, +}; +static const unsigned int avb1_rgmii_pins[] = { + /* + * AVB1_TX_CTL, AVB1_TXC, AVB1_TD0, AVB1_TD1, AVB1_TD2, AVB1_TD3, + * AVB1_RX_CTL, AVB1_RXC, AVB1_RD0, AVB1_RD1, AVB1_RD2, AVB1_RD3, + */ + RCAR_GP_PIN(6, 7), RCAR_GP_PIN(6, 6), + RCAR_GP_PIN(6, 13), RCAR_GP_PIN(6, 12), + RCAR_GP_PIN(6, 16), RCAR_GP_PIN(6, 18), + RCAR_GP_PIN(6, 9), RCAR_GP_PIN(6, 8), + RCAR_GP_PIN(6, 15), RCAR_GP_PIN(6, 14), + RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 19), +}; +static const unsigned int avb1_rgmii_mux[] = { + AVB1_TX_CTL_MARK, AVB1_TXC_MARK, + AVB1_TD0_MARK, AVB1_TD1_MARK, + AVB1_TD2_MARK, AVB1_TD3_MARK, + AVB1_RX_CTL_MARK, AVB1_RXC_MARK, + AVB1_RD0_MARK, AVB1_RD1_MARK, + AVB1_RD2_MARK, AVB1_RD3_MARK, +}; +static const unsigned int avb1_txcrefclk_pins[] = { + /* AVB1_TXCREFCLK */ + RCAR_GP_PIN(6, 20), +}; +static const unsigned int avb1_txcrefclk_mux[] = { + AVB1_TXCREFCLK_MARK, +}; +static const unsigned int avb1_avtp_pps_pins[] = { + /* AVB1_AVTP_PPS */ + RCAR_GP_PIN(6, 10), +}; +static const unsigned int avb1_avtp_pps_mux[] = { + AVB1_AVTP_PPS_MARK, +}; +static const unsigned int avb1_avtp_capture_pins[] = { + /* AVB1_AVTP_CAPTURE */ + RCAR_GP_PIN(6, 11), +}; +static const unsigned int avb1_avtp_capture_mux[] = { + AVB1_AVTP_CAPTURE_MARK, +}; +static const unsigned int avb1_avtp_match_pins[] = { + /* AVB1_AVTP_MATCH */ + RCAR_GP_PIN(6, 5), +}; +static const unsigned int avb1_avtp_match_mux[] = { + AVB1_AVTP_MATCH_MARK, +}; + +/* - AVB2 ------------------------------------------------ */ +static const unsigned int avb2_link_pins[] = { + /* AVB2_LINK */ + RCAR_GP_PIN(5, 3), +}; +static const unsigned int avb2_link_mux[] = { + AVB2_LINK_MARK, +}; +static const unsigned int avb2_magic_pins[] = { + /* AVB2_MAGIC */ + RCAR_GP_PIN(5, 5), +}; +static const unsigned int avb2_magic_mux[] = { + AVB2_MAGIC_MARK, +}; +static const unsigned int avb2_phy_int_pins[] = { + /* AVB2_PHY_INT */ + RCAR_GP_PIN(5, 4), +}; +static const unsigned int avb2_phy_int_mux[] = { + AVB2_PHY_INT_MARK, +}; +static const unsigned int avb2_mdio_pins[] = { + /* AVB2_MDC, AVB2_MDIO */ + RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 10), +}; +static const unsigned int avb2_mdio_mux[] = { + AVB2_MDC_MARK, AVB2_MDIO_MARK, +}; +static const unsigned int avb2_rgmii_pins[] = { + /* + * AVB2_TX_CTL, AVB2_TXC, AVB2_TD0, AVB2_TD1, AVB2_TD2, AVB2_TD3, + * AVB2_RX_CTL, AVB2_RXC, AVB2_RD0, AVB2_RD1, AVB2_RD2, AVB2_RD3, + */ + RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 16), + RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 12), + RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 8), + RCAR_GP_PIN(5, 20), RCAR_GP_PIN(5, 18), + RCAR_GP_PIN(5, 17), RCAR_GP_PIN(5, 14), + RCAR_GP_PIN(5, 13), RCAR_GP_PIN(5, 9), +}; +static const unsigned int avb2_rgmii_mux[] = { + AVB2_TX_CTL_MARK, AVB2_TXC_MARK, + AVB2_TD0_MARK, AVB2_TD1_MARK, + AVB2_TD2_MARK, AVB2_TD3_MARK, + AVB2_RX_CTL_MARK, AVB2_RXC_MARK, + AVB2_RD0_MARK, AVB2_RD1_MARK, + AVB2_RD2_MARK, AVB2_RD3_MARK, +}; +static const unsigned int avb2_txcrefclk_pins[] = { + /* AVB2_TXCREFCLK */ + RCAR_GP_PIN(5, 7), +}; +static const unsigned int avb2_txcrefclk_mux[] = { + AVB2_TXCREFCLK_MARK, +}; +static const unsigned int avb2_avtp_pps_pins[] = { + /* AVB2_AVTP_PPS */ + RCAR_GP_PIN(5, 0), +}; +static const unsigned int avb2_avtp_pps_mux[] = { + AVB2_AVTP_PPS_MARK, +}; +static const unsigned int avb2_avtp_capture_pins[] = { + /* AVB2_AVTP_CAPTURE */ + RCAR_GP_PIN(5, 1), +}; +static const unsigned int avb2_avtp_capture_mux[] = { + AVB2_AVTP_CAPTURE_MARK, +}; +static const unsigned int avb2_avtp_match_pins[] = { + /* AVB2_AVTP_MATCH */ + RCAR_GP_PIN(5, 2), +}; +static const unsigned int avb2_avtp_match_mux[] = { + AVB2_AVTP_MATCH_MARK, +}; + +/* - CANFD0 ----------------------------------------------------------------- */ +static const unsigned int canfd0_data_pins[] = { + /* CANFD0_TX, CANFD0_RX */ + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), +}; +static const unsigned int canfd0_data_mux[] = { + CANFD0_TX_MARK, CANFD0_RX_MARK, +}; + +/* - CANFD1 ----------------------------------------------------------------- */ +static const unsigned int canfd1_data_pins[] = { + /* CANFD1_TX, CANFD1_RX */ + RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1), +}; +static const unsigned int canfd1_data_mux[] = { + CANFD1_TX_MARK, CANFD1_RX_MARK, +}; + +/* - CANFD2 ----------------------------------------------------------------- */ +static const unsigned int canfd2_data_pins[] = { + /* CANFD2_TX, CANFD2_RX */ + RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), +}; +static const unsigned int canfd2_data_mux[] = { + CANFD2_TX_MARK, CANFD2_RX_MARK, +}; + +/* - CANFD3 ----------------------------------------------------------------- */ +static const unsigned int canfd3_data_pins[] = { + /* CANFD3_TX, CANFD3_RX */ + RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15), +}; +static const unsigned int canfd3_data_mux[] = { + CANFD3_TX_MARK, CANFD3_RX_MARK, +}; + +/* - CANFD4 ----------------------------------------------------------------- */ +static const unsigned int canfd4_data_pins[] = { + /* CANFD4_TX, CANFD4_RX */ + RCAR_GP_PIN(2, 16), RCAR_GP_PIN(2, 17), +}; +static const unsigned int canfd4_data_mux[] = { + CANFD4_TX_MARK, CANFD4_RX_MARK, +}; + +/* - CANFD5 ----------------------------------------------------------------- */ +static const unsigned int canfd5_data_pins[] = { + /* CANFD5_TX, CANFD5_RX */ + RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3), +}; +static const unsigned int canfd5_data_mux[] = { + CANFD5_TX_MARK, CANFD5_RX_MARK, +}; + +/* - CANFD5_B ----------------------------------------------------------------- */ +static const unsigned int canfd5_data_b_pins[] = { + /* CANFD5_TX_B, CANFD5_RX_B */ + RCAR_GP_PIN(1, 8), RCAR_GP_PIN(1, 9), +}; +static const unsigned int canfd5_data_b_mux[] = { + CANFD5_TX_B_MARK, CANFD5_RX_B_MARK, +}; + +/* - CANFD6 ----------------------------------------------------------------- */ +static const unsigned int canfd6_data_pins[] = { + /* CANFD6_TX, CANFD6_RX */ + RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8), +}; +static const unsigned int canfd6_data_mux[] = { + CANFD6_TX_MARK, CANFD6_RX_MARK, +}; + +/* - CANFD7 ----------------------------------------------------------------- */ +static const unsigned int canfd7_data_pins[] = { + /* CANFD7_TX, CANFD7_RX */ + RCAR_GP_PIN(2, 18), RCAR_GP_PIN(2, 19), +}; +static const unsigned int canfd7_data_mux[] = { + CANFD7_TX_MARK, CANFD7_RX_MARK, +}; + +/* - CANFD Clock ------------------------------------------------------------ */ +static const unsigned int can_clk_pins[] = { + /* CAN_CLK */ + RCAR_GP_PIN(2, 9), +}; +static const unsigned int can_clk_mux[] = { + CAN_CLK_MARK, +}; + +/* - HSCIF0 ----------------------------------------------------------------- */ +static const unsigned int hscif0_data_pins[] = { + /* HRX0, HTX0 */ + RCAR_GP_PIN(1, 16), RCAR_GP_PIN(1, 12), +}; +static const unsigned int hscif0_data_mux[] = { + HRX0_MARK, HTX0_MARK, +}; +static const unsigned int hscif0_clk_pins[] = { + /* HSCK0 */ + RCAR_GP_PIN(1, 15), +}; +static const unsigned int hscif0_clk_mux[] = { + HSCK0_MARK, +}; +static const unsigned int hscif0_ctrl_pins[] = { + /* HRTS0_N, HCTS0_N */ + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 13), +}; +static const unsigned int hscif0_ctrl_mux[] = { + HRTS0_N_MARK, HCTS0_N_MARK, +}; + +/* - HSCIF1 ----------------------------------------------------------------- */ +static const unsigned int hscif1_data_pins[] = { + /* HRX1, HTX1 */ + RCAR_GP_PIN(0, 15), RCAR_GP_PIN(0, 14), +}; +static const unsigned int hscif1_data_mux[] = { + HRX1_MARK, HTX1_MARK, +}; +static const unsigned int hscif1_clk_pins[] = { + /* HSCK1 */ + RCAR_GP_PIN(0, 18), +}; +static const unsigned int hscif1_clk_mux[] = { + HSCK1_MARK, +}; +static const unsigned int hscif1_ctrl_pins[] = { + /* HRTS1_N, HCTS1_N */ + RCAR_GP_PIN(0, 17), RCAR_GP_PIN(0, 16), +}; +static const unsigned int hscif1_ctrl_mux[] = { + HRTS1_N_MARK, HCTS1_N_MARK, +}; + +/* - HSCIF1_X---------------------------------------------------------------- */ +static const unsigned int hscif1_data_x_pins[] = { + /* HRX1_X, HTX1_X */ + RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), +}; +static const unsigned int hscif1_data_x_mux[] = { + HRX1_X_MARK, HTX1_X_MARK, +}; +static const unsigned int hscif1_clk_x_pins[] = { + /* HSCK1_X */ + RCAR_GP_PIN(1, 10), +}; +static const unsigned int hscif1_clk_x_mux[] = { + HSCK1_X_MARK, +}; +static const unsigned int hscif1_ctrl_x_pins[] = { + /* HRTS1_N_X, HCTS1_N_X */ + RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 8), +}; +static const unsigned int hscif1_ctrl_x_mux[] = { + HRTS1_N_X_MARK, HCTS1_N_X_MARK, +}; + +/* - HSCIF2 ----------------------------------------------------------------- */ +static const unsigned int hscif2_data_pins[] = { + /* HRX2, HTX2 */ + RCAR_GP_PIN(8, 8), RCAR_GP_PIN(8, 9), +}; +static const unsigned int hscif2_data_mux[] = { + HRX2_MARK, HTX2_MARK, +}; +static const unsigned int hscif2_clk_pins[] = { + /* HSCK2 */ + RCAR_GP_PIN(8, 13), +}; +static const unsigned int hscif2_clk_mux[] = { + HSCK2_MARK, +}; +static const unsigned int hscif2_ctrl_pins[] = { + /* HRTS2_N, HCTS2_N */ + RCAR_GP_PIN(8, 10), RCAR_GP_PIN(8, 12), +}; +static const unsigned int hscif2_ctrl_mux[] = { + HRTS2_N_MARK, HCTS2_N_MARK, +}; + +/* - HSCIF3 ----------------------------------------------------------------- */ +static const unsigned int hscif3_data_pins[] = { + /* HRX3, HTX3 */ + RCAR_GP_PIN(1, 24), RCAR_GP_PIN(1, 28), +}; +static const unsigned int hscif3_data_mux[] = { + HRX3_MARK, HTX3_MARK, +}; +static const unsigned int hscif3_clk_pins[] = { + /* HSCK3 */ + RCAR_GP_PIN(1, 25), +}; +static const unsigned int hscif3_clk_mux[] = { + HSCK3_MARK, +}; +static const unsigned int hscif3_ctrl_pins[] = { + /* HRTS3_N, HCTS3_N */ + RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 27), +}; +static const unsigned int hscif3_ctrl_mux[] = { + HRTS3_N_MARK, HCTS3_N_MARK, +}; + +/* - HSCIF3_A ----------------------------------------------------------------- */ +static const unsigned int hscif3_data_a_pins[] = { + /* HRX3_A, HTX3_A */ + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 0), +}; +static const unsigned int hscif3_data_a_mux[] = { + HRX3_A_MARK, HTX3_A_MARK, +}; +static const unsigned int hscif3_clk_a_pins[] = { + /* HSCK3_A */ + RCAR_GP_PIN(1, 3), +}; +static const unsigned int hscif3_clk_a_mux[] = { + HSCK3_A_MARK, +}; +static const unsigned int hscif3_ctrl_a_pins[] = { + /* HRTS3_N_A, HCTS3_N_A */ + RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 1), +}; +static const unsigned int hscif3_ctrl_a_mux[] = { + HRTS3_N_A_MARK, HCTS3_N_A_MARK, +}; + +/* - I2C0 ------------------------------------------------------------------- */ +static const unsigned int i2c0_pins[] = { + /* SDA0, SCL0 */ + RCAR_GP_PIN(8, 1), RCAR_GP_PIN(8, 0), +}; +static const unsigned int i2c0_mux[] = { + SDA0_MARK, SCL0_MARK, +}; + +/* - I2C1 ------------------------------------------------------------------- */ +static const unsigned int i2c1_pins[] = { + /* SDA1, SCL1 */ + RCAR_GP_PIN(8, 3), RCAR_GP_PIN(8, 2), +}; +static const unsigned int i2c1_mux[] = { + SDA1_MARK, SCL1_MARK, +}; + +/* - I2C2 ------------------------------------------------------------------- */ +static const unsigned int i2c2_pins[] = { + /* SDA2, SCL2 */ + RCAR_GP_PIN(8, 5), RCAR_GP_PIN(8, 4), +}; +static const unsigned int i2c2_mux[] = { + SDA2_MARK, SCL2_MARK, +}; + +/* - I2C3 ------------------------------------------------------------------- */ +static const unsigned int i2c3_pins[] = { + /* SDA3, SCL3 */ + RCAR_GP_PIN(8, 7), RCAR_GP_PIN(8, 6), +}; +static const unsigned int i2c3_mux[] = { + SDA3_MARK, SCL3_MARK, +}; + +/* - I2C4 ------------------------------------------------------------------- */ +static const unsigned int i2c4_pins[] = { + /* SDA4, SCL4 */ + RCAR_GP_PIN(8, 9), RCAR_GP_PIN(8, 8), +}; +static const unsigned int i2c4_mux[] = { + SDA4_MARK, SCL4_MARK, +}; + +/* - I2C5 ------------------------------------------------------------------- */ +static const unsigned int i2c5_pins[] = { + /* SDA5, SCL5 */ + RCAR_GP_PIN(8, 11), RCAR_GP_PIN(8, 10), +}; +static const unsigned int i2c5_mux[] = { + SDA5_MARK, SCL5_MARK, +}; + +/* - MMC -------------------------------------------------------------------- */ +static const unsigned int mmc_data_pins[] = { + /* MMC_SD_D[0:3], MMC_D[4:7] */ + RCAR_GP_PIN(3, 1), RCAR_GP_PIN(3, 0), + RCAR_GP_PIN(3, 2), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 7), RCAR_GP_PIN(3, 6), + RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 8), +}; +static const unsigned int mmc_data_mux[] = { + MMC_SD_D0_MARK, MMC_SD_D1_MARK, + MMC_SD_D2_MARK, MMC_SD_D3_MARK, + MMC_D4_MARK, MMC_D5_MARK, + MMC_D6_MARK, MMC_D7_MARK, +}; +static const unsigned int mmc_ctrl_pins[] = { + /* MMC_SD_CLK, MMC_SD_CMD */ + RCAR_GP_PIN(3, 3), RCAR_GP_PIN(3, 10), +}; +static const unsigned int mmc_ctrl_mux[] = { + MMC_SD_CLK_MARK, MMC_SD_CMD_MARK, +}; +static const unsigned int mmc_cd_pins[] = { + /* SD_CD */ + RCAR_GP_PIN(3, 11), +}; +static const unsigned int mmc_cd_mux[] = { + SD_CD_MARK, +}; +static const unsigned int mmc_wp_pins[] = { + /* SD_WP */ + RCAR_GP_PIN(3, 12), +}; +static const unsigned int mmc_wp_mux[] = { + SD_WP_MARK, +}; +static const unsigned int mmc_ds_pins[] = { + /* MMC_DS */ + RCAR_GP_PIN(3, 4), +}; +static const unsigned int mmc_ds_mux[] = { + MMC_DS_MARK, +}; + +/* - MSIOF0 ----------------------------------------------------------------- */ +static const unsigned int msiof0_clk_pins[] = { + /* MSIOF0_SCK */ + RCAR_GP_PIN(1, 10), +}; +static const unsigned int msiof0_clk_mux[] = { + MSIOF0_SCK_MARK, +}; +static const unsigned int msiof0_sync_pins[] = { + /* MSIOF0_SYNC */ + RCAR_GP_PIN(1, 8), +}; +static const unsigned int msiof0_sync_mux[] = { + MSIOF0_SYNC_MARK, +}; +static const unsigned int msiof0_ss1_pins[] = { + /* MSIOF0_SS1 */ + RCAR_GP_PIN(1, 7), +}; +static const unsigned int msiof0_ss1_mux[] = { + MSIOF0_SS1_MARK, +}; +static const unsigned int msiof0_ss2_pins[] = { + /* MSIOF0_SS2 */ + RCAR_GP_PIN(1, 6), +}; +static const unsigned int msiof0_ss2_mux[] = { + MSIOF0_SS2_MARK, +}; +static const unsigned int msiof0_txd_pins[] = { + /* MSIOF0_TXD */ + RCAR_GP_PIN(1, 9), +}; +static const unsigned int msiof0_txd_mux[] = { + MSIOF0_TXD_MARK, +}; +static const unsigned int msiof0_rxd_pins[] = { + /* MSIOF0_RXD */ + RCAR_GP_PIN(1, 11), +}; +static const unsigned int msiof0_rxd_mux[] = { + MSIOF0_RXD_MARK, +}; + +/* - MSIOF1 ----------------------------------------------------------------- */ +static const unsigned int msiof1_clk_pins[] = { + /* MSIOF1_SCK */ + RCAR_GP_PIN(1, 3), +}; +static const unsigned int msiof1_clk_mux[] = { + MSIOF1_SCK_MARK, +}; +static const unsigned int msiof1_sync_pins[] = { + /* MSIOF1_SYNC */ + RCAR_GP_PIN(1, 2), +}; +static const unsigned int msiof1_sync_mux[] = { + MSIOF1_SYNC_MARK, +}; +static const unsigned int msiof1_ss1_pins[] = { + /* MSIOF1_SS1 */ + RCAR_GP_PIN(1, 1), +}; +static const unsigned int msiof1_ss1_mux[] = { + MSIOF1_SS1_MARK, +}; +static const unsigned int msiof1_ss2_pins[] = { + /* MSIOF1_SS2 */ + RCAR_GP_PIN(1, 0), +}; +static const unsigned int msiof1_ss2_mux[] = { + MSIOF1_SS2_MARK, +}; +static const unsigned int msiof1_txd_pins[] = { + /* MSIOF1_TXD */ + RCAR_GP_PIN(1, 4), +}; +static const unsigned int msiof1_txd_mux[] = { + MSIOF1_TXD_MARK, +}; +static const unsigned int msiof1_rxd_pins[] = { + /* MSIOF1_RXD */ + RCAR_GP_PIN(1, 5), +}; +static const unsigned int msiof1_rxd_mux[] = { + MSIOF1_RXD_MARK, +}; + +/* - MSIOF2 ----------------------------------------------------------------- */ +static const unsigned int msiof2_clk_pins[] = { + /* MSIOF2_SCK */ + RCAR_GP_PIN(0, 17), +}; +static const unsigned int msiof2_clk_mux[] = { + MSIOF2_SCK_MARK, +}; +static const unsigned int msiof2_sync_pins[] = { + /* MSIOF2_SYNC */ + RCAR_GP_PIN(0, 15), +}; +static const unsigned int msiof2_sync_mux[] = { + MSIOF2_SYNC_MARK, +}; +static const unsigned int msiof2_ss1_pins[] = { + /* MSIOF2_SS1 */ + RCAR_GP_PIN(0, 14), +}; +static const unsigned int msiof2_ss1_mux[] = { + MSIOF2_SS1_MARK, +}; +static const unsigned int msiof2_ss2_pins[] = { + /* MSIOF2_SS2 */ + RCAR_GP_PIN(0, 13), +}; +static const unsigned int msiof2_ss2_mux[] = { + MSIOF2_SS2_MARK, +}; +static const unsigned int msiof2_txd_pins[] = { + /* MSIOF2_TXD */ + RCAR_GP_PIN(0, 16), +}; +static const unsigned int msiof2_txd_mux[] = { + MSIOF2_TXD_MARK, +}; +static const unsigned int msiof2_rxd_pins[] = { + /* MSIOF2_RXD */ + RCAR_GP_PIN(0, 18), +}; +static const unsigned int msiof2_rxd_mux[] = { + MSIOF2_RXD_MARK, +}; + +/* - MSIOF3 ----------------------------------------------------------------- */ +static const unsigned int msiof3_clk_pins[] = { + /* MSIOF3_SCK */ + RCAR_GP_PIN(0, 3), +}; +static const unsigned int msiof3_clk_mux[] = { + MSIOF3_SCK_MARK, +}; +static const unsigned int msiof3_sync_pins[] = { + /* MSIOF3_SYNC */ + RCAR_GP_PIN(0, 6), +}; +static const unsigned int msiof3_sync_mux[] = { + MSIOF3_SYNC_MARK, +}; +static const unsigned int msiof3_ss1_pins[] = { + /* MSIOF3_SS1 */ + RCAR_GP_PIN(0, 1), +}; +static const unsigned int msiof3_ss1_mux[] = { + MSIOF3_SS1_MARK, +}; +static const unsigned int msiof3_ss2_pins[] = { + /* MSIOF3_SS2 */ + RCAR_GP_PIN(0, 2), +}; +static const unsigned int msiof3_ss2_mux[] = { + MSIOF3_SS2_MARK, +}; +static const unsigned int msiof3_txd_pins[] = { + /* MSIOF3_TXD */ + RCAR_GP_PIN(0, 4), +}; +static const unsigned int msiof3_txd_mux[] = { + MSIOF3_TXD_MARK, +}; +static const unsigned int msiof3_rxd_pins[] = { + /* MSIOF3_RXD */ + RCAR_GP_PIN(0, 5), +}; +static const unsigned int msiof3_rxd_mux[] = { + MSIOF3_RXD_MARK, +}; + +/* - MSIOF4 ----------------------------------------------------------------- */ +static const unsigned int msiof4_clk_pins[] = { + /* MSIOF4_SCK */ + RCAR_GP_PIN(1, 25), +}; +static const unsigned int msiof4_clk_mux[] = { + MSIOF4_SCK_MARK, +}; +static const unsigned int msiof4_sync_pins[] = { + /* MSIOF4_SYNC */ + RCAR_GP_PIN(1, 28), +}; +static const unsigned int msiof4_sync_mux[] = { + MSIOF4_SYNC_MARK, +}; +static const unsigned int msiof4_ss1_pins[] = { + /* MSIOF4_SS1 */ + RCAR_GP_PIN(1, 23), +}; +static const unsigned int msiof4_ss1_mux[] = { + MSIOF4_SS1_MARK, +}; +static const unsigned int msiof4_ss2_pins[] = { + /* MSIOF4_SS2 */ + RCAR_GP_PIN(1, 24), +}; +static const unsigned int msiof4_ss2_mux[] = { + MSIOF4_SS2_MARK, +}; +static const unsigned int msiof4_txd_pins[] = { + /* MSIOF4_TXD */ + RCAR_GP_PIN(1, 26), +}; +static const unsigned int msiof4_txd_mux[] = { + MSIOF4_TXD_MARK, +}; +static const unsigned int msiof4_rxd_pins[] = { + /* MSIOF4_RXD */ + RCAR_GP_PIN(1, 27), +}; +static const unsigned int msiof4_rxd_mux[] = { + MSIOF4_RXD_MARK, +}; + +/* - MSIOF5 ----------------------------------------------------------------- */ +static const unsigned int msiof5_clk_pins[] = { + /* MSIOF5_SCK */ + RCAR_GP_PIN(0, 11), +}; +static const unsigned int msiof5_clk_mux[] = { + MSIOF5_SCK_MARK, +}; +static const unsigned int msiof5_sync_pins[] = { + /* MSIOF5_SYNC */ + RCAR_GP_PIN(0, 9), +}; +static const unsigned int msiof5_sync_mux[] = { + MSIOF5_SYNC_MARK, +}; +static const unsigned int msiof5_ss1_pins[] = { + /* MSIOF5_SS1 */ + RCAR_GP_PIN(0, 8), +}; +static const unsigned int msiof5_ss1_mux[] = { + MSIOF5_SS1_MARK, +}; +static const unsigned int msiof5_ss2_pins[] = { + /* MSIOF5_SS2 */ + RCAR_GP_PIN(0, 7), +}; +static const unsigned int msiof5_ss2_mux[] = { + MSIOF5_SS2_MARK, +}; +static const unsigned int msiof5_txd_pins[] = { + /* MSIOF5_TXD */ + RCAR_GP_PIN(0, 10), +}; +static const unsigned int msiof5_txd_mux[] = { + MSIOF5_TXD_MARK, +}; +static const unsigned int msiof5_rxd_pins[] = { + /* MSIOF5_RXD */ + RCAR_GP_PIN(0, 12), +}; +static const unsigned int msiof5_rxd_mux[] = { + MSIOF5_RXD_MARK, +}; + +/* - PCIE ------------------------------------------------------------------- */ +static const unsigned int pcie0_clkreq_n_pins[] = { + /* PCIE0_CLKREQ_N */ + RCAR_GP_PIN(4, 21), +}; + +static const unsigned int pcie0_clkreq_n_mux[] = { + PCIE0_CLKREQ_N_MARK, +}; + +static const unsigned int pcie1_clkreq_n_pins[] = { + /* PCIE1_CLKREQ_N */ + RCAR_GP_PIN(4, 22), +}; + +static const unsigned int pcie1_clkreq_n_mux[] = { + PCIE1_CLKREQ_N_MARK, +}; + +/* - PWM0_A ------------------------------------------------------------------- */ +static const unsigned int pwm0_a_pins[] = { + /* PWM0_A */ + RCAR_GP_PIN(1, 15), +}; +static const unsigned int pwm0_a_mux[] = { + PWM0_A_MARK, +}; + +/* - PWM1_A ------------------------------------------------------------------- */ +static const unsigned int pwm1_a_pins[] = { + /* PWM1_A */ + RCAR_GP_PIN(3, 13), +}; +static const unsigned int pwm1_a_mux[] = { + PWM1_A_MARK, +}; + +/* - PWM1_B ------------------------------------------------------------------- */ +static const unsigned int pwm1_b_pins[] = { + /* PWM1_B */ + RCAR_GP_PIN(2, 13), +}; +static const unsigned int pwm1_b_mux[] = { + PWM1_B_MARK, +}; + +/* - PWM2_B ------------------------------------------------------------------- */ +static const unsigned int pwm2_b_pins[] = { + /* PWM2_B */ + RCAR_GP_PIN(2, 14), +}; +static const unsigned int pwm2_b_mux[] = { + PWM2_B_MARK, +}; + +/* - PWM3_A ------------------------------------------------------------------- */ +static const unsigned int pwm3_a_pins[] = { + /* PWM3_A */ + RCAR_GP_PIN(1, 22), +}; +static const unsigned int pwm3_a_mux[] = { + PWM3_A_MARK, +}; + +/* - PWM3_B ------------------------------------------------------------------- */ +static const unsigned int pwm3_b_pins[] = { + /* PWM3_B */ + RCAR_GP_PIN(2, 15), +}; +static const unsigned int pwm3_b_mux[] = { + PWM3_B_MARK, +}; + +/* - PWM4 ------------------------------------------------------------------- */ +static const unsigned int pwm4_pins[] = { + /* PWM4 */ + RCAR_GP_PIN(2, 16), +}; +static const unsigned int pwm4_mux[] = { + PWM4_MARK, +}; + +/* - PWM5 ------------------------------------------------------------------- */ +static const unsigned int pwm5_pins[] = { + /* PWM5 */ + RCAR_GP_PIN(2, 17), +}; +static const unsigned int pwm5_mux[] = { + PWM5_MARK, +}; + +/* - PWM6 ------------------------------------------------------------------- */ +static const unsigned int pwm6_pins[] = { + /* PWM6 */ + RCAR_GP_PIN(2, 18), +}; +static const unsigned int pwm6_mux[] = { + PWM6_MARK, +}; + +/* - PWM7 ------------------------------------------------------------------- */ +static const unsigned int pwm7_pins[] = { + /* PWM7 */ + RCAR_GP_PIN(2, 19), +}; +static const unsigned int pwm7_mux[] = { + PWM7_MARK, +}; + +/* - PWM8_A ------------------------------------------------------------------- */ +static const unsigned int pwm8_a_pins[] = { + /* PWM8_A */ + RCAR_GP_PIN(1, 13), +}; +static const unsigned int pwm8_a_mux[] = { + PWM8_A_MARK, +}; + +/* - PWM9_A ------------------------------------------------------------------- */ +static const unsigned int pwm9_a_pins[] = { + /* PWM9_A */ + RCAR_GP_PIN(1, 14), +}; +static const unsigned int pwm9_a_mux[] = { + PWM9_A_MARK, +}; + +/* - QSPI0 ------------------------------------------------------------------ */ +static const unsigned int qspi0_ctrl_pins[] = { + /* SPCLK, SSL */ + RCAR_GP_PIN(3, 20), RCAR_GP_PIN(3, 15), +}; +static const unsigned int qspi0_ctrl_mux[] = { + QSPI0_SPCLK_MARK, QSPI0_SSL_MARK, +}; +static const unsigned int qspi0_data_pins[] = { + /* MOSI_IO0, MISO_IO1, IO2, IO3 */ + RCAR_GP_PIN(3, 19), RCAR_GP_PIN(3, 18), + RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 16), +}; +static const unsigned int qspi0_data_mux[] = { + QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK, + QSPI0_IO2_MARK, QSPI0_IO3_MARK +}; + +/* - QSPI1 ------------------------------------------------------------------ */ +static const unsigned int qspi1_ctrl_pins[] = { + /* SPCLK, SSL */ + RCAR_GP_PIN(3, 22), RCAR_GP_PIN(3, 25), +}; +static const unsigned int qspi1_ctrl_mux[] = { + QSPI1_SPCLK_MARK, QSPI1_SSL_MARK, +}; +static const unsigned int qspi1_data_pins[] = { + /* MOSI_IO0, MISO_IO1, IO2, IO3 */ + RCAR_GP_PIN(3, 21), RCAR_GP_PIN(3, 23), + RCAR_GP_PIN(3, 24), RCAR_GP_PIN(3, 26), +}; +static const unsigned int qspi1_data_mux[] = { + QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK, + QSPI1_IO2_MARK, QSPI1_IO3_MARK +}; + +/* - SCIF0 ------------------------------------------------------------------ */ +static const unsigned int scif0_data_pins[] = { + /* RX0, TX0 */ + RCAR_GP_PIN(1, 16), RCAR_GP_PIN(1, 12), +}; +static const unsigned int scif0_data_mux[] = { + RX0_MARK, TX0_MARK, +}; +static const unsigned int scif0_clk_pins[] = { + /* SCK0 */ + RCAR_GP_PIN(1, 15), +}; +static const unsigned int scif0_clk_mux[] = { + SCK0_MARK, +}; +static const unsigned int scif0_ctrl_pins[] = { + /* RTS0_N, CTS0_N */ + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 13), +}; +static const unsigned int scif0_ctrl_mux[] = { + RTS0_N_MARK, CTS0_N_MARK, +}; + +/* - SCIF1 ------------------------------------------------------------------ */ +static const unsigned int scif1_data_pins[] = { + /* RX1, TX1 */ + RCAR_GP_PIN(0, 15), RCAR_GP_PIN(0, 14), +}; +static const unsigned int scif1_data_mux[] = { + RX1_MARK, TX1_MARK, +}; +static const unsigned int scif1_clk_pins[] = { + /* SCK1 */ + RCAR_GP_PIN(0, 18), +}; +static const unsigned int scif1_clk_mux[] = { + SCK1_MARK, +}; +static const unsigned int scif1_ctrl_pins[] = { + /* RTS1_N, CTS1_N */ + RCAR_GP_PIN(0, 17), RCAR_GP_PIN(0, 16), +}; +static const unsigned int scif1_ctrl_mux[] = { + RTS1_N_MARK, CTS1_N_MARK, +}; + +/* - SCIF1_X ------------------------------------------------------------------ */ +static const unsigned int scif1_data_x_pins[] = { + /* RX1_X, TX1_X */ + RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), +}; +static const unsigned int scif1_data_x_mux[] = { + RX1_X_MARK, TX1_X_MARK, +}; +static const unsigned int scif1_clk_x_pins[] = { + /* SCK1_X */ + RCAR_GP_PIN(1, 10), +}; +static const unsigned int scif1_clk_x_mux[] = { + SCK1_X_MARK, +}; +static const unsigned int scif1_ctrl_x_pins[] = { + /* RTS1_N_X, CTS1_N_X */ + RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 8), +}; +static const unsigned int scif1_ctrl_x_mux[] = { + RTS1_N_X_MARK, CTS1_N_X_MARK, +}; + +/* - SCIF3 ------------------------------------------------------------------ */ +static const unsigned int scif3_data_pins[] = { + /* RX3, TX3 */ + RCAR_GP_PIN(1, 1), RCAR_GP_PIN(1, 0), +}; +static const unsigned int scif3_data_mux[] = { + RX3_MARK, TX3_MARK, +}; +static const unsigned int scif3_clk_pins[] = { + /* SCK3 */ + RCAR_GP_PIN(1, 4), +}; +static const unsigned int scif3_clk_mux[] = { + SCK3_MARK, +}; +static const unsigned int scif3_ctrl_pins[] = { + /* RTS3_N, CTS3_N */ + RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3), +}; +static const unsigned int scif3_ctrl_mux[] = { + RTS3_N_MARK, CTS3_N_MARK, +}; + +/* - SCIF3_A ------------------------------------------------------------------ */ +static const unsigned int scif3_data_a_pins[] = { + /* RX3_A, TX3_A */ + RCAR_GP_PIN(1, 27), RCAR_GP_PIN(1, 28), +}; +static const unsigned int scif3_data_a_mux[] = { + RX3_A_MARK, TX3_A_MARK, +}; +static const unsigned int scif3_clk_a_pins[] = { + /* SCK3_A */ + RCAR_GP_PIN(1, 24), +}; +static const unsigned int scif3_clk_a_mux[] = { + SCK3_A_MARK, +}; +static const unsigned int scif3_ctrl_a_pins[] = { + /* RTS3_N_A, CTS3_N_A */ + RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25), +}; +static const unsigned int scif3_ctrl_a_mux[] = { + RTS3_N_A_MARK, CTS3_N_A_MARK, +}; + +/* - SCIF4 ------------------------------------------------------------------ */ +static const unsigned int scif4_data_pins[] = { + /* RX4, TX4 */ + RCAR_GP_PIN(8, 13), RCAR_GP_PIN(8, 12), +}; +static const unsigned int scif4_data_mux[] = { + RX4_MARK, TX4_MARK, +}; +static const unsigned int scif4_clk_pins[] = { + /* SCK4 */ + RCAR_GP_PIN(8, 8), +}; +static const unsigned int scif4_clk_mux[] = { + SCK4_MARK, +}; +static const unsigned int scif4_ctrl_pins[] = { + /* RTS4_N, CTS4_N */ + RCAR_GP_PIN(8, 10), RCAR_GP_PIN(8, 9), +}; +static const unsigned int scif4_ctrl_mux[] = { + RTS4_N_MARK, CTS4_N_MARK, +}; + +/* - SCIF Clock ------------------------------------------------------------- */ +static const unsigned int scif_clk_pins[] = { + /* SCIF_CLK */ + RCAR_GP_PIN(1, 17), +}; +static const unsigned int scif_clk_mux[] = { + SCIF_CLK_MARK, +}; + +/* - TPU ------------------------------------------------------------------- */ +static const unsigned int tpu_to0_pins[] = { + /* TPU0TO0 */ + RCAR_GP_PIN(2, 8), +}; +static const unsigned int tpu_to0_mux[] = { + TPU0TO0_MARK, +}; +static const unsigned int tpu_to1_pins[] = { + /* TPU0TO1 */ + RCAR_GP_PIN(2, 7), +}; +static const unsigned int tpu_to1_mux[] = { + TPU0TO1_MARK, +}; +static const unsigned int tpu_to2_pins[] = { + /* TPU0TO2 */ + RCAR_GP_PIN(2, 12), +}; +static const unsigned int tpu_to2_mux[] = { + TPU0TO2_MARK, +}; +static const unsigned int tpu_to3_pins[] = { + /* TPU0TO3 */ + RCAR_GP_PIN(2, 13), +}; +static const unsigned int tpu_to3_mux[] = { + TPU0TO3_MARK, +}; + +/* - TPU_A ------------------------------------------------------------------- */ +static const unsigned int tpu_to0_a_pins[] = { + /* TPU0TO0_A */ + RCAR_GP_PIN(1, 25), +}; +static const unsigned int tpu_to0_a_mux[] = { + TPU0TO0_A_MARK, +}; +static const unsigned int tpu_to1_a_pins[] = { + /* TPU0TO1_A */ + RCAR_GP_PIN(1, 26), +}; +static const unsigned int tpu_to1_a_mux[] = { + TPU0TO1_A_MARK, +}; +static const unsigned int tpu_to2_a_pins[] = { + /* TPU0TO2_A */ + RCAR_GP_PIN(2, 0), +}; +static const unsigned int tpu_to2_a_mux[] = { + TPU0TO2_A_MARK, +}; +static const unsigned int tpu_to3_a_pins[] = { + /* TPU0TO3_A */ + RCAR_GP_PIN(2, 1), +}; +static const unsigned int tpu_to3_a_mux[] = { + TPU0TO3_A_MARK, +}; + +/* - TSN0 ------------------------------------------------ */ +static const unsigned int tsn0_link_pins[] = { + /* TSN0_LINK */ + RCAR_GP_PIN(4, 4), +}; +static const unsigned int tsn0_link_mux[] = { + TSN0_LINK_MARK, +}; +static const unsigned int tsn0_phy_int_pins[] = { + /* TSN0_PHY_INT */ + RCAR_GP_PIN(4, 3), +}; +static const unsigned int tsn0_phy_int_mux[] = { + TSN0_PHY_INT_MARK, +}; +static const unsigned int tsn0_mdio_pins[] = { + /* TSN0_MDC, TSN0_MDIO */ + RCAR_GP_PIN(4, 1), RCAR_GP_PIN(4, 0), +}; +static const unsigned int tsn0_mdio_mux[] = { + TSN0_MDC_MARK, TSN0_MDIO_MARK, +}; +static const unsigned int tsn0_rgmii_pins[] = { + /* + * TSN0_TX_CTL, TSN0_TXC, TSN0_TD0, TSN0_TD1, TSN0_TD2, TSN0_TD3, + * TSN0_RX_CTL, TSN0_RXC, TSN0_RD0, TSN0_RD1, TSN0_RD2, TSN0_RD3, + */ + RCAR_GP_PIN(4, 9), RCAR_GP_PIN(4, 12), + RCAR_GP_PIN(4, 15), RCAR_GP_PIN(4, 14), + RCAR_GP_PIN(4, 19), RCAR_GP_PIN(4, 18), + RCAR_GP_PIN(4, 7), RCAR_GP_PIN(4, 11), + RCAR_GP_PIN(4, 10), RCAR_GP_PIN(4, 13), + RCAR_GP_PIN(4, 17), RCAR_GP_PIN(4, 16), +}; +static const unsigned int tsn0_rgmii_mux[] = { + TSN0_TX_CTL_MARK, TSN0_TXC_MARK, + TSN0_TD0_MARK, TSN0_TD1_MARK, + TSN0_TD2_MARK, TSN0_TD3_MARK, + TSN0_RX_CTL_MARK, TSN0_RXC_MARK, + TSN0_RD0_MARK, TSN0_RD1_MARK, + TSN0_RD2_MARK, TSN0_RD3_MARK, +}; +static const unsigned int tsn0_txcrefclk_pins[] = { + /* TSN0_TXCREFCLK */ + RCAR_GP_PIN(4, 20), +}; +static const unsigned int tsn0_txcrefclk_mux[] = { + TSN0_TXCREFCLK_MARK, +}; +static const unsigned int tsn0_avtp_pps_pins[] = { + /* TSN0_AVTP_PPS0, TSN0_AVTP_PPS1 */ + RCAR_GP_PIN(4, 8), RCAR_GP_PIN(4, 2), +}; +static const unsigned int tsn0_avtp_pps_mux[] = { + TSN0_AVTP_PPS0_MARK, TSN0_AVTP_PPS1_MARK, +}; +static const unsigned int tsn0_avtp_capture_pins[] = { + /* TSN0_AVTP_CAPTURE */ + RCAR_GP_PIN(4, 6), +}; +static const unsigned int tsn0_avtp_capture_mux[] = { + TSN0_AVTP_CAPTURE_MARK, +}; +static const unsigned int tsn0_avtp_match_pins[] = { + /* TSN0_AVTP_MATCH */ + RCAR_GP_PIN(4, 5), +}; +static const unsigned int tsn0_avtp_match_mux[] = { + TSN0_AVTP_MATCH_MARK, +}; + +static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(avb0_link), + SH_PFC_PIN_GROUP(avb0_magic), + SH_PFC_PIN_GROUP(avb0_phy_int), + SH_PFC_PIN_GROUP(avb0_mdio), + SH_PFC_PIN_GROUP(avb0_rgmii), + SH_PFC_PIN_GROUP(avb0_txcrefclk), + SH_PFC_PIN_GROUP(avb0_avtp_pps), + SH_PFC_PIN_GROUP(avb0_avtp_capture), + SH_PFC_PIN_GROUP(avb0_avtp_match), + + SH_PFC_PIN_GROUP(avb1_link), + SH_PFC_PIN_GROUP(avb1_magic), + SH_PFC_PIN_GROUP(avb1_phy_int), + SH_PFC_PIN_GROUP(avb1_mdio), + SH_PFC_PIN_GROUP(avb1_rgmii), + SH_PFC_PIN_GROUP(avb1_txcrefclk), + SH_PFC_PIN_GROUP(avb1_avtp_pps), + SH_PFC_PIN_GROUP(avb1_avtp_capture), + SH_PFC_PIN_GROUP(avb1_avtp_match), + + SH_PFC_PIN_GROUP(avb2_link), + SH_PFC_PIN_GROUP(avb2_magic), + SH_PFC_PIN_GROUP(avb2_phy_int), + SH_PFC_PIN_GROUP(avb2_mdio), + SH_PFC_PIN_GROUP(avb2_rgmii), + SH_PFC_PIN_GROUP(avb2_txcrefclk), + SH_PFC_PIN_GROUP(avb2_avtp_pps), + SH_PFC_PIN_GROUP(avb2_avtp_capture), + SH_PFC_PIN_GROUP(avb2_avtp_match), + + SH_PFC_PIN_GROUP(canfd0_data), + SH_PFC_PIN_GROUP(canfd1_data), + SH_PFC_PIN_GROUP(canfd2_data), + SH_PFC_PIN_GROUP(canfd3_data), + SH_PFC_PIN_GROUP(canfd4_data), + SH_PFC_PIN_GROUP(canfd5_data), /* suffix might be updated */ + SH_PFC_PIN_GROUP(canfd5_data_b), /* suffix might be updated */ + SH_PFC_PIN_GROUP(canfd6_data), + SH_PFC_PIN_GROUP(canfd7_data), + SH_PFC_PIN_GROUP(can_clk), + + SH_PFC_PIN_GROUP(hscif0_data), + SH_PFC_PIN_GROUP(hscif0_clk), + SH_PFC_PIN_GROUP(hscif0_ctrl), + SH_PFC_PIN_GROUP(hscif1_data), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif1_clk), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif1_ctrl), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif1_data_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif1_clk_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif1_ctrl_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif2_data), + SH_PFC_PIN_GROUP(hscif2_clk), + SH_PFC_PIN_GROUP(hscif2_ctrl), + SH_PFC_PIN_GROUP(hscif3_data), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif3_clk), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif3_ctrl), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif3_data_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif3_clk_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(hscif3_ctrl_a), /* suffix might be updated */ + + SH_PFC_PIN_GROUP(i2c0), + SH_PFC_PIN_GROUP(i2c1), + SH_PFC_PIN_GROUP(i2c2), + SH_PFC_PIN_GROUP(i2c3), + SH_PFC_PIN_GROUP(i2c4), + SH_PFC_PIN_GROUP(i2c5), + + BUS_DATA_PIN_GROUP(mmc_data, 1), + BUS_DATA_PIN_GROUP(mmc_data, 4), + BUS_DATA_PIN_GROUP(mmc_data, 8), + SH_PFC_PIN_GROUP(mmc_ctrl), + SH_PFC_PIN_GROUP(mmc_cd), + SH_PFC_PIN_GROUP(mmc_wp), + SH_PFC_PIN_GROUP(mmc_ds), + + SH_PFC_PIN_GROUP(msiof0_clk), + SH_PFC_PIN_GROUP(msiof0_sync), + SH_PFC_PIN_GROUP(msiof0_ss1), + SH_PFC_PIN_GROUP(msiof0_ss2), + SH_PFC_PIN_GROUP(msiof0_txd), + SH_PFC_PIN_GROUP(msiof0_rxd), + + SH_PFC_PIN_GROUP(msiof1_clk), + SH_PFC_PIN_GROUP(msiof1_sync), + SH_PFC_PIN_GROUP(msiof1_ss1), + SH_PFC_PIN_GROUP(msiof1_ss2), + SH_PFC_PIN_GROUP(msiof1_txd), + SH_PFC_PIN_GROUP(msiof1_rxd), + + SH_PFC_PIN_GROUP(msiof2_clk), + SH_PFC_PIN_GROUP(msiof2_sync), + SH_PFC_PIN_GROUP(msiof2_ss1), + SH_PFC_PIN_GROUP(msiof2_ss2), + SH_PFC_PIN_GROUP(msiof2_txd), + SH_PFC_PIN_GROUP(msiof2_rxd), + + SH_PFC_PIN_GROUP(msiof3_clk), + SH_PFC_PIN_GROUP(msiof3_sync), + SH_PFC_PIN_GROUP(msiof3_ss1), + SH_PFC_PIN_GROUP(msiof3_ss2), + SH_PFC_PIN_GROUP(msiof3_txd), + SH_PFC_PIN_GROUP(msiof3_rxd), + + SH_PFC_PIN_GROUP(msiof4_clk), + SH_PFC_PIN_GROUP(msiof4_sync), + SH_PFC_PIN_GROUP(msiof4_ss1), + SH_PFC_PIN_GROUP(msiof4_ss2), + SH_PFC_PIN_GROUP(msiof4_txd), + SH_PFC_PIN_GROUP(msiof4_rxd), + + SH_PFC_PIN_GROUP(msiof5_clk), + SH_PFC_PIN_GROUP(msiof5_sync), + SH_PFC_PIN_GROUP(msiof5_ss1), + SH_PFC_PIN_GROUP(msiof5_ss2), + SH_PFC_PIN_GROUP(msiof5_txd), + SH_PFC_PIN_GROUP(msiof5_rxd), + + SH_PFC_PIN_GROUP(pcie0_clkreq_n), + SH_PFC_PIN_GROUP(pcie1_clkreq_n), + + SH_PFC_PIN_GROUP(pwm0_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(pwm1_a), + SH_PFC_PIN_GROUP(pwm1_b), + SH_PFC_PIN_GROUP(pwm2_b), /* suffix might be updated */ + SH_PFC_PIN_GROUP(pwm3_a), + SH_PFC_PIN_GROUP(pwm3_b), + SH_PFC_PIN_GROUP(pwm4), + SH_PFC_PIN_GROUP(pwm5), + SH_PFC_PIN_GROUP(pwm6), + SH_PFC_PIN_GROUP(pwm7), + SH_PFC_PIN_GROUP(pwm8_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(pwm9_a), /* suffix might be updated */ + + SH_PFC_PIN_GROUP(qspi0_ctrl), + BUS_DATA_PIN_GROUP(qspi0_data, 2), + BUS_DATA_PIN_GROUP(qspi0_data, 4), + SH_PFC_PIN_GROUP(qspi1_ctrl), + BUS_DATA_PIN_GROUP(qspi1_data, 2), + BUS_DATA_PIN_GROUP(qspi1_data, 4), + + SH_PFC_PIN_GROUP(scif0_data), + SH_PFC_PIN_GROUP(scif0_clk), + SH_PFC_PIN_GROUP(scif0_ctrl), + SH_PFC_PIN_GROUP(scif1_data), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif1_clk), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif1_ctrl), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif1_data_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif1_clk_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif1_ctrl_x), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_data), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_clk), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_ctrl), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_data_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_clk_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif3_ctrl_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(scif4_data), + SH_PFC_PIN_GROUP(scif4_clk), + SH_PFC_PIN_GROUP(scif4_ctrl), + SH_PFC_PIN_GROUP(scif_clk), + + SH_PFC_PIN_GROUP(tpu_to0), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to0_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to1), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to1_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to2), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to2_a), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to3), /* suffix might be updated */ + SH_PFC_PIN_GROUP(tpu_to3_a), /* suffix might be updated */ + + SH_PFC_PIN_GROUP(tsn0_link), + SH_PFC_PIN_GROUP(tsn0_phy_int), + SH_PFC_PIN_GROUP(tsn0_mdio), + SH_PFC_PIN_GROUP(tsn0_rgmii), + SH_PFC_PIN_GROUP(tsn0_txcrefclk), + SH_PFC_PIN_GROUP(tsn0_avtp_pps), + SH_PFC_PIN_GROUP(tsn0_avtp_capture), + SH_PFC_PIN_GROUP(tsn0_avtp_match), +}; + +static const char * const avb0_groups[] = { + "avb0_link", + "avb0_magic", + "avb0_phy_int", + "avb0_mdio", + "avb0_rgmii", + "avb0_txcrefclk", + "avb0_avtp_pps", + "avb0_avtp_capture", + "avb0_avtp_match", +}; + +static const char * const avb1_groups[] = { + "avb1_link", + "avb1_magic", + "avb1_phy_int", + "avb1_mdio", + "avb1_rgmii", + "avb1_txcrefclk", + "avb1_avtp_pps", + "avb1_avtp_capture", + "avb1_avtp_match", +}; + +static const char * const avb2_groups[] = { + "avb2_link", + "avb2_magic", + "avb2_phy_int", + "avb2_mdio", + "avb2_rgmii", + "avb2_txcrefclk", + "avb2_avtp_pps", + "avb2_avtp_capture", + "avb2_avtp_match", +}; + +static const char * const canfd0_groups[] = { + "canfd0_data", +}; + +static const char * const canfd1_groups[] = { + "canfd1_data", +}; + +static const char * const canfd2_groups[] = { + "canfd2_data", +}; + +static const char * const canfd3_groups[] = { + "canfd3_data", +}; + +static const char * const canfd4_groups[] = { + "canfd4_data", +}; + +static const char * const canfd5_groups[] = { + /* suffix might be updated */ + "canfd5_data", + "canfd5_data_b", +}; + +static const char * const canfd6_groups[] = { + "canfd6_data", +}; + +static const char * const canfd7_groups[] = { + "canfd7_data", +}; + +static const char * const can_clk_groups[] = { + "can_clk", +}; + +static const char * const hscif0_groups[] = { + "hscif0_data", + "hscif0_clk", + "hscif0_ctrl", +}; + +static const char * const hscif1_groups[] = { + /* suffix might be updated */ + "hscif1_data", + "hscif1_clk", + "hscif1_ctrl", + "hscif1_data_x", + "hscif1_clk_x", + "hscif1_ctrl_x", +}; + +static const char * const hscif2_groups[] = { + "hscif2_data", + "hscif2_clk", + "hscif2_ctrl", +}; + +static const char * const hscif3_groups[] = { + /* suffix might be updated */ + "hscif3_data", + "hscif3_clk", + "hscif3_ctrl", + "hscif3_data_a", + "hscif3_clk_a", + "hscif3_ctrl_a", +}; + +static const char * const i2c0_groups[] = { + "i2c0", +}; + +static const char * const i2c1_groups[] = { + "i2c1", +}; + +static const char * const i2c2_groups[] = { + "i2c2", +}; + +static const char * const i2c3_groups[] = { + "i2c3", +}; + +static const char * const i2c4_groups[] = { + "i2c4", +}; + +static const char * const i2c5_groups[] = { + "i2c5", +}; + +static const char * const mmc_groups[] = { + "mmc_data1", + "mmc_data4", + "mmc_data8", + "mmc_ctrl", + "mmc_cd", + "mmc_wp", + "mmc_ds", +}; + +static const char * const msiof0_groups[] = { + "msiof0_clk", + "msiof0_sync", + "msiof0_ss1", + "msiof0_ss2", + "msiof0_txd", + "msiof0_rxd", +}; + +static const char * const msiof1_groups[] = { + "msiof1_clk", + "msiof1_sync", + "msiof1_ss1", + "msiof1_ss2", + "msiof1_txd", + "msiof1_rxd", +}; + +static const char * const msiof2_groups[] = { + "msiof2_clk", + "msiof2_sync", + "msiof2_ss1", + "msiof2_ss2", + "msiof2_txd", + "msiof2_rxd", +}; + +static const char * const msiof3_groups[] = { + "msiof3_clk", + "msiof3_sync", + "msiof3_ss1", + "msiof3_ss2", + "msiof3_txd", + "msiof3_rxd", +}; + +static const char * const msiof4_groups[] = { + "msiof4_clk", + "msiof4_sync", + "msiof4_ss1", + "msiof4_ss2", + "msiof4_txd", + "msiof4_rxd", +}; + +static const char * const msiof5_groups[] = { + "msiof5_clk", + "msiof5_sync", + "msiof5_ss1", + "msiof5_ss2", + "msiof5_txd", + "msiof5_rxd", +}; + +static const char * const pcie_groups[] = { + "pcie0_clkreq_n", + "pcie1_clkreq_n", +}; + +static const char * const pwm0_groups[] = { + /* suffix might be updated */ + "pwm0_a", +}; + +static const char * const pwm1_groups[] = { + "pwm1_a", + "pwm1_b", +}; + +static const char * const pwm2_groups[] = { + /* suffix might be updated */ + "pwm2_b", +}; + +static const char * const pwm3_groups[] = { + "pwm3_a", + "pwm3_b", +}; + +static const char * const pwm4_groups[] = { + "pwm4", +}; + +static const char * const pwm5_groups[] = { + "pwm5", +}; + +static const char * const pwm6_groups[] = { + "pwm6", +}; + +static const char * const pwm7_groups[] = { + "pwm7", +}; + +static const char * const pwm8_groups[] = { + /* suffix might be updated */ + "pwm8_a", +}; + +static const char * const pwm9_groups[] = { + /* suffix might be updated */ + "pwm9_a", +}; + +static const char * const qspi0_groups[] = { + "qspi0_ctrl", + "qspi0_data2", + "qspi0_data4", +}; + +static const char * const qspi1_groups[] = { + "qspi1_ctrl", + "qspi1_data2", + "qspi1_data4", +}; + +static const char * const scif0_groups[] = { + "scif0_data", + "scif0_clk", + "scif0_ctrl", +}; + +static const char * const scif1_groups[] = { + /* suffix might be updated */ + "scif1_data", + "scif1_clk", + "scif1_ctrl", + "scif1_data_x", + "scif1_clk_x", + "scif1_ctrl_x", +}; + +static const char * const scif3_groups[] = { + /* suffix might be updated */ + "scif3_data", + "scif3_clk", + "scif3_ctrl", + "scif3_data_a", + "scif3_clk_a", + "scif3_ctrl_a", +}; + +static const char * const scif4_groups[] = { + "scif4_data", + "scif4_clk", + "scif4_ctrl", +}; + +static const char * const scif_clk_groups[] = { + "scif_clk", +}; + +static const char * const tpu_groups[] = { + /* suffix might be updated */ + "tpu_to0", + "tpu_to0_a", + "tpu_to1", + "tpu_to1_a", + "tpu_to2", + "tpu_to2_a", + "tpu_to3", + "tpu_to3_a", +}; + +static const char * const tsn0_groups[] = { + "tsn0_link", + "tsn0_phy_int", + "tsn0_mdio", + "tsn0_rgmii", + "tsn0_txcrefclk", + "tsn0_avtp_pps", + "tsn0_avtp_capture", + "tsn0_avtp_match", +}; + +static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(avb0), + SH_PFC_FUNCTION(avb1), + SH_PFC_FUNCTION(avb2), + + SH_PFC_FUNCTION(canfd0), + SH_PFC_FUNCTION(canfd1), + SH_PFC_FUNCTION(canfd2), + SH_PFC_FUNCTION(canfd3), + SH_PFC_FUNCTION(canfd4), + SH_PFC_FUNCTION(canfd5), + SH_PFC_FUNCTION(canfd6), + SH_PFC_FUNCTION(canfd7), + SH_PFC_FUNCTION(can_clk), + + SH_PFC_FUNCTION(hscif0), + SH_PFC_FUNCTION(hscif1), + SH_PFC_FUNCTION(hscif2), + SH_PFC_FUNCTION(hscif3), + + SH_PFC_FUNCTION(i2c0), + SH_PFC_FUNCTION(i2c1), + SH_PFC_FUNCTION(i2c2), + SH_PFC_FUNCTION(i2c3), + SH_PFC_FUNCTION(i2c4), + SH_PFC_FUNCTION(i2c5), + + SH_PFC_FUNCTION(mmc), + + SH_PFC_FUNCTION(msiof0), + SH_PFC_FUNCTION(msiof1), + SH_PFC_FUNCTION(msiof2), + SH_PFC_FUNCTION(msiof3), + SH_PFC_FUNCTION(msiof4), + SH_PFC_FUNCTION(msiof5), + + SH_PFC_FUNCTION(pcie), + + SH_PFC_FUNCTION(pwm0), + SH_PFC_FUNCTION(pwm1), + SH_PFC_FUNCTION(pwm2), + SH_PFC_FUNCTION(pwm3), + SH_PFC_FUNCTION(pwm4), + SH_PFC_FUNCTION(pwm5), + SH_PFC_FUNCTION(pwm6), + SH_PFC_FUNCTION(pwm7), + SH_PFC_FUNCTION(pwm8), + SH_PFC_FUNCTION(pwm9), + + SH_PFC_FUNCTION(qspi0), + SH_PFC_FUNCTION(qspi1), + + SH_PFC_FUNCTION(scif0), + SH_PFC_FUNCTION(scif1), + SH_PFC_FUNCTION(scif3), + SH_PFC_FUNCTION(scif4), + SH_PFC_FUNCTION(scif_clk), + + SH_PFC_FUNCTION(tpu), + + SH_PFC_FUNCTION(tsn0), +}; + +static const struct pinmux_cfg_reg pinmux_config_regs[] = { +#define F_(x, y) FN_##y +#define FM(x) FN_##x + { PINMUX_CFG_REG_VAR("GPSR0", 0xE6050040, 32, + GROUP(-13, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP0_31_19 RESERVED */ + GP_0_18_FN, GPSR0_18, + GP_0_17_FN, GPSR0_17, + GP_0_16_FN, GPSR0_16, + GP_0_15_FN, GPSR0_15, + GP_0_14_FN, GPSR0_14, + GP_0_13_FN, GPSR0_13, + GP_0_12_FN, GPSR0_12, + GP_0_11_FN, GPSR0_11, + GP_0_10_FN, GPSR0_10, + GP_0_9_FN, GPSR0_9, + GP_0_8_FN, GPSR0_8, + GP_0_7_FN, GPSR0_7, + GP_0_6_FN, GPSR0_6, + GP_0_5_FN, GPSR0_5, + GP_0_4_FN, GPSR0_4, + GP_0_3_FN, GPSR0_3, + GP_0_2_FN, GPSR0_2, + GP_0_1_FN, GPSR0_1, + GP_0_0_FN, GPSR0_0, )) + }, + { PINMUX_CFG_REG("GPSR1", 0xE6050840, 32, 1, GROUP( + 0, 0, + 0, 0, + 0, 0, + GP_1_28_FN, GPSR1_28, + GP_1_27_FN, GPSR1_27, + GP_1_26_FN, GPSR1_26, + GP_1_25_FN, GPSR1_25, + GP_1_24_FN, GPSR1_24, + GP_1_23_FN, GPSR1_23, + GP_1_22_FN, GPSR1_22, + GP_1_21_FN, GPSR1_21, + GP_1_20_FN, GPSR1_20, + GP_1_19_FN, GPSR1_19, + GP_1_18_FN, GPSR1_18, + GP_1_17_FN, GPSR1_17, + GP_1_16_FN, GPSR1_16, + GP_1_15_FN, GPSR1_15, + GP_1_14_FN, GPSR1_14, + GP_1_13_FN, GPSR1_13, + GP_1_12_FN, GPSR1_12, + GP_1_11_FN, GPSR1_11, + GP_1_10_FN, GPSR1_10, + GP_1_9_FN, GPSR1_9, + GP_1_8_FN, GPSR1_8, + GP_1_7_FN, GPSR1_7, + GP_1_6_FN, GPSR1_6, + GP_1_5_FN, GPSR1_5, + GP_1_4_FN, GPSR1_4, + GP_1_3_FN, GPSR1_3, + GP_1_2_FN, GPSR1_2, + GP_1_1_FN, GPSR1_1, + GP_1_0_FN, GPSR1_0, )) + }, + { PINMUX_CFG_REG_VAR("GPSR2", 0xE6058040, 32, + GROUP(-12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP2_31_20 RESERVED */ + GP_2_19_FN, GPSR2_19, + GP_2_18_FN, GPSR2_18, + GP_2_17_FN, GPSR2_17, + GP_2_16_FN, GPSR2_16, + GP_2_15_FN, GPSR2_15, + GP_2_14_FN, GPSR2_14, + GP_2_13_FN, GPSR2_13, + GP_2_12_FN, GPSR2_12, + GP_2_11_FN, GPSR2_11, + GP_2_10_FN, GPSR2_10, + GP_2_9_FN, GPSR2_9, + GP_2_8_FN, GPSR2_8, + GP_2_7_FN, GPSR2_7, + GP_2_6_FN, GPSR2_6, + GP_2_5_FN, GPSR2_5, + GP_2_4_FN, GPSR2_4, + GP_2_3_FN, GPSR2_3, + GP_2_2_FN, GPSR2_2, + GP_2_1_FN, GPSR2_1, + GP_2_0_FN, GPSR2_0, )) + }, + { PINMUX_CFG_REG("GPSR3", 0xE6058840, 32, 1, GROUP( + 0, 0, + 0, 0, + GP_3_29_FN, GPSR3_29, + GP_3_28_FN, GPSR3_28, + GP_3_27_FN, GPSR3_27, + GP_3_26_FN, GPSR3_26, + GP_3_25_FN, GPSR3_25, + GP_3_24_FN, GPSR3_24, + GP_3_23_FN, GPSR3_23, + GP_3_22_FN, GPSR3_22, + GP_3_21_FN, GPSR3_21, + GP_3_20_FN, GPSR3_20, + GP_3_19_FN, GPSR3_19, + GP_3_18_FN, GPSR3_18, + GP_3_17_FN, GPSR3_17, + GP_3_16_FN, GPSR3_16, + GP_3_15_FN, GPSR3_15, + GP_3_14_FN, GPSR3_14, + GP_3_13_FN, GPSR3_13, + GP_3_12_FN, GPSR3_12, + GP_3_11_FN, GPSR3_11, + GP_3_10_FN, GPSR3_10, + GP_3_9_FN, GPSR3_9, + GP_3_8_FN, GPSR3_8, + GP_3_7_FN, GPSR3_7, + GP_3_6_FN, GPSR3_6, + GP_3_5_FN, GPSR3_5, + GP_3_4_FN, GPSR3_4, + GP_3_3_FN, GPSR3_3, + GP_3_2_FN, GPSR3_2, + GP_3_1_FN, GPSR3_1, + GP_3_0_FN, GPSR3_0, )) + }, + { PINMUX_CFG_REG("GPSR4", 0xE6060040, 32, 1, GROUP( + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + GP_4_24_FN, GPSR4_24, + GP_4_23_FN, GPSR4_23, + GP_4_22_FN, GPSR4_22, + GP_4_21_FN, GPSR4_21, + GP_4_20_FN, GPSR4_20, + GP_4_19_FN, GPSR4_19, + GP_4_18_FN, GPSR4_18, + GP_4_17_FN, GPSR4_17, + GP_4_16_FN, GPSR4_16, + GP_4_15_FN, GPSR4_15, + GP_4_14_FN, GPSR4_14, + GP_4_13_FN, GPSR4_13, + GP_4_12_FN, GPSR4_12, + GP_4_11_FN, GPSR4_11, + GP_4_10_FN, GPSR4_10, + GP_4_9_FN, GPSR4_9, + GP_4_8_FN, GPSR4_8, + GP_4_7_FN, GPSR4_7, + GP_4_6_FN, GPSR4_6, + GP_4_5_FN, GPSR4_5, + GP_4_4_FN, GPSR4_4, + GP_4_3_FN, GPSR4_3, + GP_4_2_FN, GPSR4_2, + GP_4_1_FN, GPSR4_1, + GP_4_0_FN, GPSR4_0, )) + }, + { PINMUX_CFG_REG_VAR("GPSR5", 0xE6060840, 32, + GROUP(-11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP5_31_21 RESERVED */ + GP_5_20_FN, GPSR5_20, + GP_5_19_FN, GPSR5_19, + GP_5_18_FN, GPSR5_18, + GP_5_17_FN, GPSR5_17, + GP_5_16_FN, GPSR5_16, + GP_5_15_FN, GPSR5_15, + GP_5_14_FN, GPSR5_14, + GP_5_13_FN, GPSR5_13, + GP_5_12_FN, GPSR5_12, + GP_5_11_FN, GPSR5_11, + GP_5_10_FN, GPSR5_10, + GP_5_9_FN, GPSR5_9, + GP_5_8_FN, GPSR5_8, + GP_5_7_FN, GPSR5_7, + GP_5_6_FN, GPSR5_6, + GP_5_5_FN, GPSR5_5, + GP_5_4_FN, GPSR5_4, + GP_5_3_FN, GPSR5_3, + GP_5_2_FN, GPSR5_2, + GP_5_1_FN, GPSR5_1, + GP_5_0_FN, GPSR5_0, )) + }, + { PINMUX_CFG_REG_VAR("GPSR6", 0xE6061040, 32, + GROUP(-11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP6_31_21 RESERVED */ + GP_6_20_FN, GPSR6_20, + GP_6_19_FN, GPSR6_19, + GP_6_18_FN, GPSR6_18, + GP_6_17_FN, GPSR6_17, + GP_6_16_FN, GPSR6_16, + GP_6_15_FN, GPSR6_15, + GP_6_14_FN, GPSR6_14, + GP_6_13_FN, GPSR6_13, + GP_6_12_FN, GPSR6_12, + GP_6_11_FN, GPSR6_11, + GP_6_10_FN, GPSR6_10, + GP_6_9_FN, GPSR6_9, + GP_6_8_FN, GPSR6_8, + GP_6_7_FN, GPSR6_7, + GP_6_6_FN, GPSR6_6, + GP_6_5_FN, GPSR6_5, + GP_6_4_FN, GPSR6_4, + GP_6_3_FN, GPSR6_3, + GP_6_2_FN, GPSR6_2, + GP_6_1_FN, GPSR6_1, + GP_6_0_FN, GPSR6_0, )) + }, + { PINMUX_CFG_REG_VAR("GPSR7", 0xE6061840, 32, + GROUP(-11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP7_31_21 RESERVED */ + GP_7_20_FN, GPSR7_20, + GP_7_19_FN, GPSR7_19, + GP_7_18_FN, GPSR7_18, + GP_7_17_FN, GPSR7_17, + GP_7_16_FN, GPSR7_16, + GP_7_15_FN, GPSR7_15, + GP_7_14_FN, GPSR7_14, + GP_7_13_FN, GPSR7_13, + GP_7_12_FN, GPSR7_12, + GP_7_11_FN, GPSR7_11, + GP_7_10_FN, GPSR7_10, + GP_7_9_FN, GPSR7_9, + GP_7_8_FN, GPSR7_8, + GP_7_7_FN, GPSR7_7, + GP_7_6_FN, GPSR7_6, + GP_7_5_FN, GPSR7_5, + GP_7_4_FN, GPSR7_4, + GP_7_3_FN, GPSR7_3, + GP_7_2_FN, GPSR7_2, + GP_7_1_FN, GPSR7_1, + GP_7_0_FN, GPSR7_0, )) + }, + { PINMUX_CFG_REG_VAR("GPSR8", 0xE6068040, 32, + GROUP(-18, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* GP8_31_14 RESERVED */ + GP_8_13_FN, GPSR8_13, + GP_8_12_FN, GPSR8_12, + GP_8_11_FN, GPSR8_11, + GP_8_10_FN, GPSR8_10, + GP_8_9_FN, GPSR8_9, + GP_8_8_FN, GPSR8_8, + GP_8_7_FN, GPSR8_7, + GP_8_6_FN, GPSR8_6, + GP_8_5_FN, GPSR8_5, + GP_8_4_FN, GPSR8_4, + GP_8_3_FN, GPSR8_3, + GP_8_2_FN, GPSR8_2, + GP_8_1_FN, GPSR8_1, + GP_8_0_FN, GPSR8_0, )) + }, +#undef F_ +#undef FM + +#define F_(x, y) x, +#define FM(x) FN_##x, + { PINMUX_CFG_REG("IP0SR0", 0xE6050060, 32, 4, GROUP( + IP0SR0_31_28 + IP0SR0_27_24 + IP0SR0_23_20 + IP0SR0_19_16 + IP0SR0_15_12 + IP0SR0_11_8 + IP0SR0_7_4 + IP0SR0_3_0)) + }, + { PINMUX_CFG_REG("IP1SR0", 0xE6050064, 32, 4, GROUP( + IP1SR0_31_28 + IP1SR0_27_24 + IP1SR0_23_20 + IP1SR0_19_16 + IP1SR0_15_12 + IP1SR0_11_8 + IP1SR0_7_4 + IP1SR0_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP2SR0", 0xE6050068, 32, + GROUP(-20, 4, 4, 4), + GROUP( + /* IP2SR0_31_12 RESERVED */ + IP2SR0_11_8 + IP2SR0_7_4 + IP2SR0_3_0)) + }, + { PINMUX_CFG_REG("IP0SR1", 0xE6050860, 32, 4, GROUP( + IP0SR1_31_28 + IP0SR1_27_24 + IP0SR1_23_20 + IP0SR1_19_16 + IP0SR1_15_12 + IP0SR1_11_8 + IP0SR1_7_4 + IP0SR1_3_0)) + }, + { PINMUX_CFG_REG("IP1SR1", 0xE6050864, 32, 4, GROUP( + IP1SR1_31_28 + IP1SR1_27_24 + IP1SR1_23_20 + IP1SR1_19_16 + IP1SR1_15_12 + IP1SR1_11_8 + IP1SR1_7_4 + IP1SR1_3_0)) + }, + { PINMUX_CFG_REG("IP2SR1", 0xE6050868, 32, 4, GROUP( + IP2SR1_31_28 + IP2SR1_27_24 + IP2SR1_23_20 + IP2SR1_19_16 + IP2SR1_15_12 + IP2SR1_11_8 + IP2SR1_7_4 + IP2SR1_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP3SR1", 0xE605086C, 32, + GROUP(-12, 4, 4, 4, 4, 4), + GROUP( + /* IP3SR1_31_20 RESERVED */ + IP3SR1_19_16 + IP3SR1_15_12 + IP3SR1_11_8 + IP3SR1_7_4 + IP3SR1_3_0)) + }, + { PINMUX_CFG_REG("IP0SR2", 0xE6058060, 32, 4, GROUP( + IP0SR2_31_28 + IP0SR2_27_24 + IP0SR2_23_20 + IP0SR2_19_16 + IP0SR2_15_12 + IP0SR2_11_8 + IP0SR2_7_4 + IP0SR2_3_0)) + }, + { PINMUX_CFG_REG("IP1SR2", 0xE6058064, 32, 4, GROUP( + IP1SR2_31_28 + IP1SR2_27_24 + IP1SR2_23_20 + IP1SR2_19_16 + IP1SR2_15_12 + IP1SR2_11_8 + IP1SR2_7_4 + IP1SR2_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP2SR2", 0xE6058068, 32, + GROUP(-16, 4, 4, 4, 4), + GROUP( + /* IP2SR2_31_16 RESERVED */ + IP2SR2_15_12 + IP2SR2_11_8 + IP2SR2_7_4 + IP2SR2_3_0)) + }, + { PINMUX_CFG_REG("IP0SR3", 0xE6058860, 32, 4, GROUP( + IP0SR3_31_28 + IP0SR3_27_24 + IP0SR3_23_20 + IP0SR3_19_16 + IP0SR3_15_12 + IP0SR3_11_8 + IP0SR3_7_4 + IP0SR3_3_0)) + }, + { PINMUX_CFG_REG("IP1SR3", 0xE6058864, 32, 4, GROUP( + IP1SR3_31_28 + IP1SR3_27_24 + IP1SR3_23_20 + IP1SR3_19_16 + IP1SR3_15_12 + IP1SR3_11_8 + IP1SR3_7_4 + IP1SR3_3_0)) + }, + { PINMUX_CFG_REG("IP2SR3", 0xE6058868, 32, 4, GROUP( + IP2SR3_31_28 + IP2SR3_27_24 + IP2SR3_23_20 + IP2SR3_19_16 + IP2SR3_15_12 + IP2SR3_11_8 + IP2SR3_7_4 + IP2SR3_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP3SR3", 0xE605886C, 32, + GROUP(-8, 4, 4, 4, 4, 4, 4), + GROUP( + /* IP3SR3_31_24 RESERVED */ + IP3SR3_23_20 + IP3SR3_19_16 + IP3SR3_15_12 + IP3SR3_11_8 + IP3SR3_7_4 + IP3SR3_3_0)) + }, + { PINMUX_CFG_REG("IP0SR6", 0xE6061060, 32, 4, GROUP( + IP0SR6_31_28 + IP0SR6_27_24 + IP0SR6_23_20 + IP0SR6_19_16 + IP0SR6_15_12 + IP0SR6_11_8 + IP0SR6_7_4 + IP0SR6_3_0)) + }, + { PINMUX_CFG_REG("IP1SR6", 0xE6061064, 32, 4, GROUP( + IP1SR6_31_28 + IP1SR6_27_24 + IP1SR6_23_20 + IP1SR6_19_16 + IP1SR6_15_12 + IP1SR6_11_8 + IP1SR6_7_4 + IP1SR6_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP2SR6", 0xE6061068, 32, + GROUP(-12, 4, 4, 4, 4, 4), + GROUP( + /* IP2SR6_31_20 RESERVED */ + IP2SR6_19_16 + IP2SR6_15_12 + IP2SR6_11_8 + IP2SR6_7_4 + IP2SR6_3_0)) + }, + { PINMUX_CFG_REG("IP0SR7", 0xE6061860, 32, 4, GROUP( + IP0SR7_31_28 + IP0SR7_27_24 + IP0SR7_23_20 + IP0SR7_19_16 + IP0SR7_15_12 + IP0SR7_11_8 + IP0SR7_7_4 + IP0SR7_3_0)) + }, + { PINMUX_CFG_REG("IP1SR7", 0xE6061864, 32, 4, GROUP( + IP1SR7_31_28 + IP1SR7_27_24 + IP1SR7_23_20 + IP1SR7_19_16 + IP1SR7_15_12 + IP1SR7_11_8 + IP1SR7_7_4 + IP1SR7_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP2SR7", 0xE6061868, 32, + GROUP(-12, 4, 4, 4, 4, 4), + GROUP( + /* IP2SR7_31_20 RESERVED */ + IP2SR7_19_16 + IP2SR7_15_12 + IP2SR7_11_8 + IP2SR7_7_4 + IP2SR7_3_0)) + }, + { PINMUX_CFG_REG("IP0SR8", 0xE6068060, 32, 4, GROUP( + IP0SR8_31_28 + IP0SR8_27_24 + IP0SR8_23_20 + IP0SR8_19_16 + IP0SR8_15_12 + IP0SR8_11_8 + IP0SR8_7_4 + IP0SR8_3_0)) + }, + { PINMUX_CFG_REG_VAR("IP1SR8", 0xE6068064, 32, + GROUP(-8, 4, 4, 4, 4, 4, 4), + GROUP( + /* IP1SR8_31_24 RESERVED */ + IP1SR8_23_20 + IP1SR8_19_16 + IP1SR8_15_12 + IP1SR8_11_8 + IP1SR8_7_4 + IP1SR8_3_0)) + }, +#undef F_ +#undef FM + +#define F_(x, y) x, +#define FM(x) FN_##x, + { PINMUX_CFG_REG_VAR("MOD_SEL4", 0xE6060100, 32, + GROUP(-12, 1, 1, -2, 1, 1, -1, 1, -2, 1, 1, -2, 1, + -2, 1, 1, -1), + GROUP( + /* RESERVED 31-20 */ + MOD_SEL4_19 + MOD_SEL4_18 + /* RESERVED 17-16 */ + MOD_SEL4_15 + MOD_SEL4_14 + /* RESERVED 13 */ + MOD_SEL4_12 + /* RESERVED 11-10 */ + MOD_SEL4_9 + MOD_SEL4_8 + /* RESERVED 7-6 */ + MOD_SEL4_5 + /* RESERVED 4-3 */ + MOD_SEL4_2 + MOD_SEL4_1 + /* RESERVED 0 */ + )) + }, + { PINMUX_CFG_REG_VAR("MOD_SEL5", 0xE6060900, 32, + GROUP(-12, 1, -2, 1, 1, -2, 1, 1, -2, 1, -1, + 1, 1, -2, 1, -1, 1), + GROUP( + /* RESERVED 31-20 */ + MOD_SEL5_19 + /* RESERVED 18-17 */ + MOD_SEL5_16 + MOD_SEL5_15 + /* RESERVED 14-13 */ + MOD_SEL5_12 + MOD_SEL5_11 + /* RESERVED 10-9 */ + MOD_SEL5_8 + /* RESERVED 7 */ + MOD_SEL5_6 + MOD_SEL5_5 + /* RESERVED 4-3 */ + MOD_SEL5_2 + /* RESERVED 1 */ + MOD_SEL5_0)) + }, + { PINMUX_CFG_REG_VAR("MOD_SEL6", 0xE6061100, 32, + GROUP(-13, 1, -1, 1, -2, 1, 1, + -1, 1, -2, 1, 1, 1, -2, 1, 1, -1), + GROUP( + /* RESERVED 31-19 */ + MOD_SEL6_18 + /* RESERVED 17 */ + MOD_SEL6_16 + /* RESERVED 15-14 */ + MOD_SEL6_13 + MOD_SEL6_12 + /* RESERVED 11 */ + MOD_SEL6_10 + /* RESERVED 9-8 */ + MOD_SEL6_7 + MOD_SEL6_6 + MOD_SEL6_5 + /* RESERVED 4-3 */ + MOD_SEL6_2 + MOD_SEL6_1 + /* RESERVED 0 */ + )) + }, + { PINMUX_CFG_REG_VAR("MOD_SEL7", 0xE6061900, 32, + GROUP(-15, 1, 1, -1, 1, -1, 1, 1, -2, 1, 1, + -2, 1, 1, -1, 1), + GROUP( + /* RESERVED 31-17 */ + MOD_SEL7_16 + MOD_SEL7_15 + /* RESERVED 14 */ + MOD_SEL7_13 + /* RESERVED 12 */ + MOD_SEL7_11 + MOD_SEL7_10 + /* RESERVED 9-8 */ + MOD_SEL7_7 + MOD_SEL7_6 + /* RESERVED 5-4 */ + MOD_SEL7_3 + MOD_SEL7_2 + /* RESERVED 1 */ + MOD_SEL7_0)) + }, + { PINMUX_CFG_REG_VAR("MOD_SEL8", 0xE6068100, 32, + GROUP(-20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + GROUP( + /* RESERVED 31-12 */ + MOD_SEL8_11 + MOD_SEL8_10 + MOD_SEL8_9 + MOD_SEL8_8 + MOD_SEL8_7 + MOD_SEL8_6 + MOD_SEL8_5 + MOD_SEL8_4 + MOD_SEL8_3 + MOD_SEL8_2 + MOD_SEL8_1 + MOD_SEL8_0)) + }, + { }, +}; + +static const struct pinmux_drive_reg pinmux_drive_regs[] = { + { PINMUX_DRIVE_REG("DRV0CTRL0", 0xE6050080) { + { RCAR_GP_PIN(0, 7), 28, 3 }, /* MSIOF5_SS2 */ + { RCAR_GP_PIN(0, 6), 24, 3 }, /* IRQ0 */ + { RCAR_GP_PIN(0, 5), 20, 3 }, /* IRQ1 */ + { RCAR_GP_PIN(0, 4), 16, 3 }, /* IRQ2 */ + { RCAR_GP_PIN(0, 3), 12, 3 }, /* IRQ3 */ + { RCAR_GP_PIN(0, 2), 8, 3 }, /* GP0_02 */ + { RCAR_GP_PIN(0, 1), 4, 3 }, /* GP0_01 */ + { RCAR_GP_PIN(0, 0), 0, 3 }, /* GP0_00 */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL0", 0xE6050084) { + { RCAR_GP_PIN(0, 15), 28, 3 }, /* MSIOF2_SYNC */ + { RCAR_GP_PIN(0, 14), 24, 3 }, /* MSIOF2_SS1 */ + { RCAR_GP_PIN(0, 13), 20, 3 }, /* MSIOF2_SS2 */ + { RCAR_GP_PIN(0, 12), 16, 3 }, /* MSIOF5_RXD */ + { RCAR_GP_PIN(0, 11), 12, 3 }, /* MSIOF5_SCK */ + { RCAR_GP_PIN(0, 10), 8, 3 }, /* MSIOF5_TXD */ + { RCAR_GP_PIN(0, 9), 4, 3 }, /* MSIOF5_SYNC */ + { RCAR_GP_PIN(0, 8), 0, 3 }, /* MSIOF5_SS1 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL0", 0xE6050088) { + { RCAR_GP_PIN(0, 18), 8, 3 }, /* MSIOF2_RXD */ + { RCAR_GP_PIN(0, 17), 4, 3 }, /* MSIOF2_SCK */ + { RCAR_GP_PIN(0, 16), 0, 3 }, /* MSIOF2_TXD */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL1", 0xE6050880) { + { RCAR_GP_PIN(1, 7), 28, 3 }, /* MSIOF0_SS1 */ + { RCAR_GP_PIN(1, 6), 24, 3 }, /* MSIOF0_SS2 */ + { RCAR_GP_PIN(1, 5), 20, 3 }, /* MSIOF1_RXD */ + { RCAR_GP_PIN(1, 4), 16, 3 }, /* MSIOF1_TXD */ + { RCAR_GP_PIN(1, 3), 12, 3 }, /* MSIOF1_SCK */ + { RCAR_GP_PIN(1, 2), 8, 3 }, /* MSIOF1_SYNC */ + { RCAR_GP_PIN(1, 1), 4, 3 }, /* MSIOF1_SS1 */ + { RCAR_GP_PIN(1, 0), 0, 3 }, /* MSIOF1_SS2 */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL1", 0xE6050884) { + { RCAR_GP_PIN(1, 15), 28, 3 }, /* HSCK0 */ + { RCAR_GP_PIN(1, 14), 24, 3 }, /* HRTS0_N */ + { RCAR_GP_PIN(1, 13), 20, 3 }, /* HCTS0_N */ + { RCAR_GP_PIN(1, 12), 16, 3 }, /* HTX0 */ + { RCAR_GP_PIN(1, 11), 12, 3 }, /* MSIOF0_RXD */ + { RCAR_GP_PIN(1, 10), 8, 3 }, /* MSIOF0_SCK */ + { RCAR_GP_PIN(1, 9), 4, 3 }, /* MSIOF0_TXD */ + { RCAR_GP_PIN(1, 8), 0, 3 }, /* MSIOF0_SYNC */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL1", 0xE6050888) { + { RCAR_GP_PIN(1, 23), 28, 3 }, /* GP1_23 */ + { RCAR_GP_PIN(1, 22), 24, 3 }, /* AUDIO_CLKIN */ + { RCAR_GP_PIN(1, 21), 20, 3 }, /* AUDIO_CLKOUT */ + { RCAR_GP_PIN(1, 20), 16, 3 }, /* SSI_SD */ + { RCAR_GP_PIN(1, 19), 12, 3 }, /* SSI_WS */ + { RCAR_GP_PIN(1, 18), 8, 3 }, /* SSI_SCK */ + { RCAR_GP_PIN(1, 17), 4, 3 }, /* SCIF_CLK */ + { RCAR_GP_PIN(1, 16), 0, 3 }, /* HRX0 */ + } }, + { PINMUX_DRIVE_REG("DRV3CTRL1", 0xE605088C) { + { RCAR_GP_PIN(1, 28), 16, 3 }, /* HTX3 */ + { RCAR_GP_PIN(1, 27), 12, 3 }, /* HCTS3_N */ + { RCAR_GP_PIN(1, 26), 8, 3 }, /* HRTS3_N */ + { RCAR_GP_PIN(1, 25), 4, 3 }, /* HSCK3 */ + { RCAR_GP_PIN(1, 24), 0, 3 }, /* HRX3 */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL2", 0xE6058080) { + { RCAR_GP_PIN(2, 7), 28, 3 }, /* TPU0TO1 */ + { RCAR_GP_PIN(2, 6), 24, 3 }, /* FXR_TXDB */ + { RCAR_GP_PIN(2, 5), 20, 3 }, /* FXR_TXENB_N */ + { RCAR_GP_PIN(2, 4), 16, 3 }, /* RXDB_EXTFXR */ + { RCAR_GP_PIN(2, 3), 12, 3 }, /* CLK_EXTFXR */ + { RCAR_GP_PIN(2, 2), 8, 3 }, /* RXDA_EXTFXR */ + { RCAR_GP_PIN(2, 1), 4, 3 }, /* FXR_TXENA_N */ + { RCAR_GP_PIN(2, 0), 0, 3 }, /* FXR_TXDA */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL2", 0xE6058084) { + { RCAR_GP_PIN(2, 15), 28, 3 }, /* CANFD3_RX */ + { RCAR_GP_PIN(2, 14), 24, 3 }, /* CANFD3_TX */ + { RCAR_GP_PIN(2, 13), 20, 3 }, /* CANFD2_RX */ + { RCAR_GP_PIN(2, 12), 16, 3 }, /* CANFD2_TX */ + { RCAR_GP_PIN(2, 11), 12, 3 }, /* CANFD0_RX */ + { RCAR_GP_PIN(2, 10), 8, 3 }, /* CANFD0_TX */ + { RCAR_GP_PIN(2, 9), 4, 3 }, /* CAN_CLK */ + { RCAR_GP_PIN(2, 8), 0, 3 }, /* TPU0TO0 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL2", 0xE6058088) { + { RCAR_GP_PIN(2, 19), 12, 3 }, /* CANFD7_RX */ + { RCAR_GP_PIN(2, 18), 8, 3 }, /* CANFD7_TX */ + { RCAR_GP_PIN(2, 17), 4, 3 }, /* CANFD4_RX */ + { RCAR_GP_PIN(2, 16), 0, 3 }, /* CANFD4_TX */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL3", 0xE6058880) { + { RCAR_GP_PIN(3, 7), 28, 3 }, /* MMC_D4 */ + { RCAR_GP_PIN(3, 6), 24, 3 }, /* MMC_D5 */ + { RCAR_GP_PIN(3, 5), 20, 3 }, /* MMC_SD_D3 */ + { RCAR_GP_PIN(3, 4), 16, 3 }, /* MMC_DS */ + { RCAR_GP_PIN(3, 3), 12, 3 }, /* MMC_SD_CLK */ + { RCAR_GP_PIN(3, 2), 8, 3 }, /* MMC_SD_D2 */ + { RCAR_GP_PIN(3, 1), 4, 3 }, /* MMC_SD_D0 */ + { RCAR_GP_PIN(3, 0), 0, 3 }, /* MMC_SD_D1 */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL3", 0xE6058884) { + { RCAR_GP_PIN(3, 15), 28, 2 }, /* QSPI0_SSL */ + { RCAR_GP_PIN(3, 14), 24, 2 }, /* IPC_CLKOUT */ + { RCAR_GP_PIN(3, 13), 20, 2 }, /* IPC_CLKIN */ + { RCAR_GP_PIN(3, 12), 16, 3 }, /* SD_WP */ + { RCAR_GP_PIN(3, 11), 12, 3 }, /* SD_CD */ + { RCAR_GP_PIN(3, 10), 8, 3 }, /* MMC_SD_CMD */ + { RCAR_GP_PIN(3, 9), 4, 3 }, /* MMC_D6*/ + { RCAR_GP_PIN(3, 8), 0, 3 }, /* MMC_D7 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL3", 0xE6058888) { + { RCAR_GP_PIN(3, 23), 28, 2 }, /* QSPI1_MISO_IO1 */ + { RCAR_GP_PIN(3, 22), 24, 2 }, /* QSPI1_SPCLK */ + { RCAR_GP_PIN(3, 21), 20, 2 }, /* QSPI1_MOSI_IO0 */ + { RCAR_GP_PIN(3, 20), 16, 2 }, /* QSPI0_SPCLK */ + { RCAR_GP_PIN(3, 19), 12, 2 }, /* QSPI0_MOSI_IO0 */ + { RCAR_GP_PIN(3, 18), 8, 2 }, /* QSPI0_MISO_IO1 */ + { RCAR_GP_PIN(3, 17), 4, 2 }, /* QSPI0_IO2 */ + { RCAR_GP_PIN(3, 16), 0, 2 }, /* QSPI0_IO3 */ + } }, + { PINMUX_DRIVE_REG("DRV3CTRL3", 0xE605888C) { + { RCAR_GP_PIN(3, 29), 20, 2 }, /* RPC_INT_N */ + { RCAR_GP_PIN(3, 28), 16, 2 }, /* RPC_WP_N */ + { RCAR_GP_PIN(3, 27), 12, 2 }, /* RPC_RESET_N */ + { RCAR_GP_PIN(3, 26), 8, 2 }, /* QSPI1_IO3 */ + { RCAR_GP_PIN(3, 25), 4, 2 }, /* QSPI1_SSL */ + { RCAR_GP_PIN(3, 24), 0, 2 }, /* QSPI1_IO2 */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL4", 0xE6060080) { + { RCAR_GP_PIN(4, 7), 28, 3 }, /* TSN0_RX_CTL */ + { RCAR_GP_PIN(4, 6), 24, 3 }, /* TSN0_AVTP_CAPTURE */ + { RCAR_GP_PIN(4, 5), 20, 3 }, /* TSN0_AVTP_MATCH */ + { RCAR_GP_PIN(4, 4), 16, 3 }, /* TSN0_LINK */ + { RCAR_GP_PIN(4, 3), 12, 3 }, /* TSN0_PHY_INT */ + { RCAR_GP_PIN(4, 2), 8, 3 }, /* TSN0_AVTP_PPS1 */ + { RCAR_GP_PIN(4, 1), 4, 3 }, /* TSN0_MDC */ + { RCAR_GP_PIN(4, 0), 0, 3 }, /* TSN0_MDIO */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL4", 0xE6060084) { + { RCAR_GP_PIN(4, 15), 28, 3 }, /* TSN0_TD0 */ + { RCAR_GP_PIN(4, 14), 24, 3 }, /* TSN0_TD1 */ + { RCAR_GP_PIN(4, 13), 20, 3 }, /* TSN0_RD1 */ + { RCAR_GP_PIN(4, 12), 16, 3 }, /* TSN0_TXC */ + { RCAR_GP_PIN(4, 11), 12, 3 }, /* TSN0_RXC */ + { RCAR_GP_PIN(4, 10), 8, 3 }, /* TSN0_RD0 */ + { RCAR_GP_PIN(4, 9), 4, 3 }, /* TSN0_TX_CTL */ + { RCAR_GP_PIN(4, 8), 0, 3 }, /* TSN0_AVTP_PPS0 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL4", 0xE6060088) { + { RCAR_GP_PIN(4, 23), 28, 3 }, /* AVS0 */ + { RCAR_GP_PIN(4, 22), 24, 3 }, /* PCIE1_CLKREQ_N */ + { RCAR_GP_PIN(4, 21), 20, 3 }, /* PCIE0_CLKREQ_N */ + { RCAR_GP_PIN(4, 20), 16, 3 }, /* TSN0_TXCREFCLK */ + { RCAR_GP_PIN(4, 19), 12, 3 }, /* TSN0_TD2 */ + { RCAR_GP_PIN(4, 18), 8, 3 }, /* TSN0_TD3 */ + { RCAR_GP_PIN(4, 17), 4, 3 }, /* TSN0_RD2 */ + { RCAR_GP_PIN(4, 16), 0, 3 }, /* TSN0_RD3 */ + } }, + { PINMUX_DRIVE_REG("DRV3CTRL4", 0xE606008C) { + { RCAR_GP_PIN(4, 24), 0, 3 }, /* AVS1 */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL5", 0xE6060880) { + { RCAR_GP_PIN(5, 7), 28, 3 }, /* AVB2_TXCREFCLK */ + { RCAR_GP_PIN(5, 6), 24, 3 }, /* AVB2_MDC */ + { RCAR_GP_PIN(5, 5), 20, 3 }, /* AVB2_MAGIC */ + { RCAR_GP_PIN(5, 4), 16, 3 }, /* AVB2_PHY_INT */ + { RCAR_GP_PIN(5, 3), 12, 3 }, /* AVB2_LINK */ + { RCAR_GP_PIN(5, 2), 8, 3 }, /* AVB2_AVTP_MATCH */ + { RCAR_GP_PIN(5, 1), 4, 3 }, /* AVB2_AVTP_CAPTURE */ + { RCAR_GP_PIN(5, 0), 0, 3 }, /* AVB2_AVTP_PPS */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL5", 0xE6060884) { + { RCAR_GP_PIN(5, 15), 28, 3 }, /* AVB2_TD0 */ + { RCAR_GP_PIN(5, 14), 24, 3 }, /* AVB2_RD1 */ + { RCAR_GP_PIN(5, 13), 20, 3 }, /* AVB2_RD2 */ + { RCAR_GP_PIN(5, 12), 16, 3 }, /* AVB2_TD1 */ + { RCAR_GP_PIN(5, 11), 12, 3 }, /* AVB2_TD2 */ + { RCAR_GP_PIN(5, 10), 8, 3 }, /* AVB2_MDIO */ + { RCAR_GP_PIN(5, 9), 4, 3 }, /* AVB2_RD3 */ + { RCAR_GP_PIN(5, 8), 0, 3 }, /* AVB2_TD3 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL5", 0xE6060888) { + { RCAR_GP_PIN(5, 20), 16, 3 }, /* AVB2_RX_CTL */ + { RCAR_GP_PIN(5, 19), 12, 3 }, /* AVB2_TX_CTL */ + { RCAR_GP_PIN(5, 18), 8, 3 }, /* AVB2_RXC */ + { RCAR_GP_PIN(5, 17), 4, 3 }, /* AVB2_RD0 */ + { RCAR_GP_PIN(5, 16), 0, 3 }, /* AVB2_TXC */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL6", 0xE6061080) { + { RCAR_GP_PIN(6, 7), 28, 3 }, /* AVB1_TX_CTL */ + { RCAR_GP_PIN(6, 6), 24, 3 }, /* AVB1_TXC */ + { RCAR_GP_PIN(6, 5), 20, 3 }, /* AVB1_AVTP_MATCH */ + { RCAR_GP_PIN(6, 4), 16, 3 }, /* AVB1_LINK */ + { RCAR_GP_PIN(6, 3), 12, 3 }, /* AVB1_PHY_INT */ + { RCAR_GP_PIN(6, 2), 8, 3 }, /* AVB1_MDC */ + { RCAR_GP_PIN(6, 1), 4, 3 }, /* AVB1_MAGIC */ + { RCAR_GP_PIN(6, 0), 0, 3 }, /* AVB1_MDIO */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL6", 0xE6061084) { + { RCAR_GP_PIN(6, 15), 28, 3 }, /* AVB1_RD0 */ + { RCAR_GP_PIN(6, 14), 24, 3 }, /* AVB1_RD1 */ + { RCAR_GP_PIN(6, 13), 20, 3 }, /* AVB1_TD0 */ + { RCAR_GP_PIN(6, 12), 16, 3 }, /* AVB1_TD1 */ + { RCAR_GP_PIN(6, 11), 12, 3 }, /* AVB1_AVTP_CAPTURE */ + { RCAR_GP_PIN(6, 10), 8, 3 }, /* AVB1_AVTP_PPS */ + { RCAR_GP_PIN(6, 9), 4, 3 }, /* AVB1_RX_CTL */ + { RCAR_GP_PIN(6, 8), 0, 3 }, /* AVB1_RXC */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL6", 0xE6061088) { + { RCAR_GP_PIN(6, 20), 16, 3 }, /* AVB1_TXCREFCLK */ + { RCAR_GP_PIN(6, 19), 12, 3 }, /* AVB1_RD3 */ + { RCAR_GP_PIN(6, 18), 8, 3 }, /* AVB1_TD3 */ + { RCAR_GP_PIN(6, 17), 4, 3 }, /* AVB1_RD2 */ + { RCAR_GP_PIN(6, 16), 0, 3 }, /* AVB1_TD2 */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL7", 0xE6061880) { + { RCAR_GP_PIN(7, 7), 28, 3 }, /* AVB0_TD1 */ + { RCAR_GP_PIN(7, 6), 24, 3 }, /* AVB0_TD2 */ + { RCAR_GP_PIN(7, 5), 20, 3 }, /* AVB0_PHY_INT */ + { RCAR_GP_PIN(7, 4), 16, 3 }, /* AVB0_LINK */ + { RCAR_GP_PIN(7, 3), 12, 3 }, /* AVB0_TD3 */ + { RCAR_GP_PIN(7, 2), 8, 3 }, /* AVB0_AVTP_MATCH */ + { RCAR_GP_PIN(7, 1), 4, 3 }, /* AVB0_AVTP_CAPTURE */ + { RCAR_GP_PIN(7, 0), 0, 3 }, /* AVB0_AVTP_PPS */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL7", 0xE6061884) { + { RCAR_GP_PIN(7, 15), 28, 3 }, /* AVB0_TXC */ + { RCAR_GP_PIN(7, 14), 24, 3 }, /* AVB0_MDIO */ + { RCAR_GP_PIN(7, 13), 20, 3 }, /* AVB0_MDC */ + { RCAR_GP_PIN(7, 12), 16, 3 }, /* AVB0_RD2 */ + { RCAR_GP_PIN(7, 11), 12, 3 }, /* AVB0_TD0 */ + { RCAR_GP_PIN(7, 10), 8, 3 }, /* AVB0_MAGIC */ + { RCAR_GP_PIN(7, 9), 4, 3 }, /* AVB0_TXCREFCLK */ + { RCAR_GP_PIN(7, 8), 0, 3 }, /* AVB0_RD3 */ + } }, + { PINMUX_DRIVE_REG("DRV2CTRL7", 0xE6061888) { + { RCAR_GP_PIN(7, 20), 16, 3 }, /* AVB0_RX_CTL */ + { RCAR_GP_PIN(7, 19), 12, 3 }, /* AVB0_RXC */ + { RCAR_GP_PIN(7, 18), 8, 3 }, /* AVB0_RD0 */ + { RCAR_GP_PIN(7, 17), 4, 3 }, /* AVB0_RD1 */ + { RCAR_GP_PIN(7, 16), 0, 3 }, /* AVB0_TX_CTL */ + } }, + { PINMUX_DRIVE_REG("DRV0CTRL8", 0xE6068080) { + { RCAR_GP_PIN(8, 7), 28, 3 }, /* SDA3 */ + { RCAR_GP_PIN(8, 6), 24, 3 }, /* SCL3 */ + { RCAR_GP_PIN(8, 5), 20, 3 }, /* SDA2 */ + { RCAR_GP_PIN(8, 4), 16, 3 }, /* SCL2 */ + { RCAR_GP_PIN(8, 3), 12, 3 }, /* SDA1 */ + { RCAR_GP_PIN(8, 2), 8, 3 }, /* SCL1 */ + { RCAR_GP_PIN(8, 1), 4, 3 }, /* SDA0 */ + { RCAR_GP_PIN(8, 0), 0, 3 }, /* SCL0 */ + } }, + { PINMUX_DRIVE_REG("DRV1CTRL8", 0xE6068084) { + { RCAR_GP_PIN(8, 13), 20, 3 }, /* GP8_13 */ + { RCAR_GP_PIN(8, 12), 16, 3 }, /* GP8_12 */ + { RCAR_GP_PIN(8, 11), 12, 3 }, /* SDA5 */ + { RCAR_GP_PIN(8, 10), 8, 3 }, /* SCL5 */ + { RCAR_GP_PIN(8, 9), 4, 3 }, /* SDA4 */ + { RCAR_GP_PIN(8, 8), 0, 3 }, /* SCL4 */ + } }, + { }, +}; + +enum ioctrl_regs { + POC0, + POC1, + POC3, + POC4, + POC5, + POC6, + POC7, + POC8, +}; + +static const struct pinmux_ioctrl_reg pinmux_ioctrl_regs[] = { + [POC0] = { 0xE60500A0, }, + [POC1] = { 0xE60508A0, }, + [POC3] = { 0xE60588A0, }, + [POC4] = { 0xE60600A0, }, + [POC5] = { 0xE60608A0, }, + [POC6] = { 0xE60610A0, }, + [POC7] = { 0xE60618A0, }, + [POC8] = { 0xE60680A0, }, + { /* sentinel */ }, +}; + +static int r8a779g0_pin_to_pocctrl(unsigned int pin, u32 *pocctrl) +{ + int bit = pin & 0x1f; + + *pocctrl = pinmux_ioctrl_regs[POC0].reg; + if (pin >= RCAR_GP_PIN(0, 0) && pin <= RCAR_GP_PIN(0, 18)) + return bit; + + *pocctrl = pinmux_ioctrl_regs[POC1].reg; + if (pin >= RCAR_GP_PIN(1, 0) && pin <= RCAR_GP_PIN(1, 22)) + return bit; + + *pocctrl = pinmux_ioctrl_regs[POC3].reg; + if (pin >= RCAR_GP_PIN(3, 0) && pin <= RCAR_GP_PIN(3, 12)) + return bit; + + *pocctrl = pinmux_ioctrl_regs[POC8].reg; + if (pin >= RCAR_GP_PIN(8, 0) && pin <= RCAR_GP_PIN(8, 13)) + return bit; + + return -EINVAL; +} + +static const struct pinmux_bias_reg pinmux_bias_regs[] = { + { PINMUX_BIAS_REG("PUEN0", 0xE60500C0, "PUD0", 0xE60500E0) { + [ 0] = RCAR_GP_PIN(0, 0), /* GP0_00 */ + [ 1] = RCAR_GP_PIN(0, 1), /* GP0_01 */ + [ 2] = RCAR_GP_PIN(0, 2), /* GP0_02 */ + [ 3] = RCAR_GP_PIN(0, 3), /* IRQ3 */ + [ 4] = RCAR_GP_PIN(0, 4), /* IRQ2 */ + [ 5] = RCAR_GP_PIN(0, 5), /* IRQ1 */ + [ 6] = RCAR_GP_PIN(0, 6), /* IRQ0 */ + [ 7] = RCAR_GP_PIN(0, 7), /* MSIOF5_SS2 */ + [ 8] = RCAR_GP_PIN(0, 8), /* MSIOF5_SS1 */ + [ 9] = RCAR_GP_PIN(0, 9), /* MSIOF5_SYNC */ + [10] = RCAR_GP_PIN(0, 10), /* MSIOF5_TXD */ + [11] = RCAR_GP_PIN(0, 11), /* MSIOF5_SCK */ + [12] = RCAR_GP_PIN(0, 12), /* MSIOF5_RXD */ + [13] = RCAR_GP_PIN(0, 13), /* MSIOF2_SS2 */ + [14] = RCAR_GP_PIN(0, 14), /* MSIOF2_SS1 */ + [15] = RCAR_GP_PIN(0, 15), /* MSIOF2_SYNC */ + [16] = RCAR_GP_PIN(0, 16), /* MSIOF2_TXD */ + [17] = RCAR_GP_PIN(0, 17), /* MSIOF2_SCK */ + [18] = RCAR_GP_PIN(0, 18), /* MSIOF2_RXD */ + [19] = SH_PFC_PIN_NONE, + [20] = SH_PFC_PIN_NONE, + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN1", 0xE60508C0, "PUD1", 0xE60508E0) { + [ 0] = RCAR_GP_PIN(1, 0), /* MSIOF1_SS2 */ + [ 1] = RCAR_GP_PIN(1, 1), /* MSIOF1_SS1 */ + [ 2] = RCAR_GP_PIN(1, 2), /* MSIOF1_SYNC */ + [ 3] = RCAR_GP_PIN(1, 3), /* MSIOF1_SCK */ + [ 4] = RCAR_GP_PIN(1, 4), /* MSIOF1_TXD */ + [ 5] = RCAR_GP_PIN(1, 5), /* MSIOF1_RXD */ + [ 6] = RCAR_GP_PIN(1, 6), /* MSIOF0_SS2 */ + [ 7] = RCAR_GP_PIN(1, 7), /* MSIOF0_SS1 */ + [ 8] = RCAR_GP_PIN(1, 8), /* MSIOF0_SYNC */ + [ 9] = RCAR_GP_PIN(1, 9), /* MSIOF0_TXD */ + [10] = RCAR_GP_PIN(1, 10), /* MSIOF0_SCK */ + [11] = RCAR_GP_PIN(1, 11), /* MSIOF0_RXD */ + [12] = RCAR_GP_PIN(1, 12), /* HTX0 */ + [13] = RCAR_GP_PIN(1, 13), /* HCTS0_N */ + [14] = RCAR_GP_PIN(1, 14), /* HRTS0_N */ + [15] = RCAR_GP_PIN(1, 15), /* HSCK0 */ + [16] = RCAR_GP_PIN(1, 16), /* HRX0 */ + [17] = RCAR_GP_PIN(1, 17), /* SCIF_CLK */ + [18] = RCAR_GP_PIN(1, 18), /* SSI_SCK */ + [19] = RCAR_GP_PIN(1, 19), /* SSI_WS */ + [20] = RCAR_GP_PIN(1, 20), /* SSI_SD */ + [21] = RCAR_GP_PIN(1, 21), /* AUDIO_CLKOUT */ + [22] = RCAR_GP_PIN(1, 22), /* AUDIO_CLKIN */ + [23] = RCAR_GP_PIN(1, 23), /* GP1_23 */ + [24] = RCAR_GP_PIN(1, 24), /* HRX3 */ + [25] = RCAR_GP_PIN(1, 25), /* HSCK3 */ + [26] = RCAR_GP_PIN(1, 26), /* HRTS3_N */ + [27] = RCAR_GP_PIN(1, 27), /* HCTS3_N */ + [28] = RCAR_GP_PIN(1, 28), /* HTX3 */ + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN2", 0xE60580C0, "PUD2", 0xE60580E0) { + [ 0] = RCAR_GP_PIN(2, 0), /* FXR_TXDA */ + [ 1] = RCAR_GP_PIN(2, 1), /* FXR_TXENA_N */ + [ 2] = RCAR_GP_PIN(2, 2), /* RXDA_EXTFXR */ + [ 3] = RCAR_GP_PIN(2, 3), /* CLK_EXTFXR */ + [ 4] = RCAR_GP_PIN(2, 4), /* RXDB_EXTFXR */ + [ 5] = RCAR_GP_PIN(2, 5), /* FXR_TXENB_N */ + [ 6] = RCAR_GP_PIN(2, 6), /* FXR_TXDB */ + [ 7] = RCAR_GP_PIN(2, 7), /* TPU0TO1 */ + [ 8] = RCAR_GP_PIN(2, 8), /* TPU0TO0 */ + [ 9] = RCAR_GP_PIN(2, 9), /* CAN_CLK */ + [10] = RCAR_GP_PIN(2, 10), /* CANFD0_TX */ + [11] = RCAR_GP_PIN(2, 11), /* CANFD0_RX */ + [12] = RCAR_GP_PIN(2, 12), /* CANFD2_TX */ + [13] = RCAR_GP_PIN(2, 13), /* CANFD2_RX */ + [14] = RCAR_GP_PIN(2, 14), /* CANFD3_TX */ + [15] = RCAR_GP_PIN(2, 15), /* CANFD3_RX */ + [16] = RCAR_GP_PIN(2, 16), /* CANFD4_TX */ + [17] = RCAR_GP_PIN(2, 17), /* CANFD4_RX */ + [18] = RCAR_GP_PIN(2, 18), /* CANFD7_TX */ + [19] = RCAR_GP_PIN(2, 19), /* CANFD7_RX */ + [20] = SH_PFC_PIN_NONE, + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN3", 0xE60588C0, "PUD3", 0xE60588E0) { + [ 0] = RCAR_GP_PIN(3, 0), /* MMC_SD_D1 */ + [ 1] = RCAR_GP_PIN(3, 1), /* MMC_SD_D0 */ + [ 2] = RCAR_GP_PIN(3, 2), /* MMC_SD_D2 */ + [ 3] = RCAR_GP_PIN(3, 3), /* MMC_SD_CLK */ + [ 4] = RCAR_GP_PIN(3, 4), /* MMC_DS */ + [ 5] = RCAR_GP_PIN(3, 5), /* MMC_SD_D3 */ + [ 6] = RCAR_GP_PIN(3, 6), /* MMC_D5 */ + [ 7] = RCAR_GP_PIN(3, 7), /* MMC_D4 */ + [ 8] = RCAR_GP_PIN(3, 8), /* MMC_D7 */ + [ 9] = RCAR_GP_PIN(3, 9), /* MMC_D6 */ + [10] = RCAR_GP_PIN(3, 10), /* MMC_SD_CMD */ + [11] = RCAR_GP_PIN(3, 11), /* SD_CD */ + [12] = RCAR_GP_PIN(3, 12), /* SD_WP */ + [13] = RCAR_GP_PIN(3, 13), /* IPC_CLKIN */ + [14] = RCAR_GP_PIN(3, 14), /* IPC_CLKOUT */ + [15] = RCAR_GP_PIN(3, 15), /* QSPI0_SSL */ + [16] = RCAR_GP_PIN(3, 16), /* QSPI0_IO3 */ + [17] = RCAR_GP_PIN(3, 17), /* QSPI0_IO2 */ + [18] = RCAR_GP_PIN(3, 18), /* QSPI0_MISO_IO1 */ + [19] = RCAR_GP_PIN(3, 19), /* QSPI0_MOSI_IO0 */ + [20] = RCAR_GP_PIN(3, 20), /* QSPI0_SPCLK */ + [21] = RCAR_GP_PIN(3, 21), /* QSPI1_MOSI_IO0 */ + [22] = RCAR_GP_PIN(3, 22), /* QSPI1_SPCLK */ + [23] = RCAR_GP_PIN(3, 23), /* QSPI1_MISO_IO1 */ + [24] = RCAR_GP_PIN(3, 24), /* QSPI1_IO2 */ + [25] = RCAR_GP_PIN(3, 25), /* QSPI1_SSL */ + [26] = RCAR_GP_PIN(3, 26), /* QSPI1_IO3 */ + [27] = RCAR_GP_PIN(3, 27), /* RPC_RESET_N */ + [28] = RCAR_GP_PIN(3, 28), /* RPC_WP_N */ + [29] = RCAR_GP_PIN(3, 29), /* RPC_INT_N */ + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN4", 0xE60600C0, "PUD4", 0xE60600E0) { + [ 0] = RCAR_GP_PIN(4, 0), /* TSN0_MDIO */ + [ 1] = RCAR_GP_PIN(4, 1), /* TSN0_MDC */ + [ 2] = RCAR_GP_PIN(4, 2), /* TSN0_AVTP_PPS1 */ + [ 3] = RCAR_GP_PIN(4, 3), /* TSN0_PHY_INT */ + [ 4] = RCAR_GP_PIN(4, 4), /* TSN0_LINK */ + [ 5] = RCAR_GP_PIN(4, 5), /* TSN0_AVTP_MATCH */ + [ 6] = RCAR_GP_PIN(4, 6), /* TSN0_AVTP_CAPTURE */ + [ 7] = RCAR_GP_PIN(4, 7), /* TSN0_RX_CTL */ + [ 8] = RCAR_GP_PIN(4, 8), /* TSN0_AVTP_PPS0 */ + [ 9] = RCAR_GP_PIN(4, 9), /* TSN0_TX_CTL */ + [10] = RCAR_GP_PIN(4, 10), /* TSN0_RD0 */ + [11] = RCAR_GP_PIN(4, 11), /* TSN0_RXC */ + [12] = RCAR_GP_PIN(4, 12), /* TSN0_TXC */ + [13] = RCAR_GP_PIN(4, 13), /* TSN0_RD1 */ + [14] = RCAR_GP_PIN(4, 14), /* TSN0_TD1 */ + [15] = RCAR_GP_PIN(4, 15), /* TSN0_TD0 */ + [16] = RCAR_GP_PIN(4, 16), /* TSN0_RD3 */ + [17] = RCAR_GP_PIN(4, 17), /* TSN0_RD2 */ + [18] = RCAR_GP_PIN(4, 18), /* TSN0_TD3 */ + [19] = RCAR_GP_PIN(4, 19), /* TSN0_TD2 */ + [20] = RCAR_GP_PIN(4, 20), /* TSN0_TXCREFCLK */ + [21] = RCAR_GP_PIN(4, 21), /* PCIE0_CLKREQ_N */ + [22] = RCAR_GP_PIN(4, 22), /* PCIE1_CLKREQ_N */ + [23] = RCAR_GP_PIN(4, 23), /* AVS0 */ + [24] = RCAR_GP_PIN(4, 24), /* AVS1 */ + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN5", 0xE60608C0, "PUD5", 0xE60608E0) { + [ 0] = RCAR_GP_PIN(5, 0), /* AVB2_AVTP_PPS */ + [ 1] = RCAR_GP_PIN(5, 1), /* AVB0_AVTP_CAPTURE */ + [ 2] = RCAR_GP_PIN(5, 2), /* AVB2_AVTP_MATCH */ + [ 3] = RCAR_GP_PIN(5, 3), /* AVB2_LINK */ + [ 4] = RCAR_GP_PIN(5, 4), /* AVB2_PHY_INT */ + [ 5] = RCAR_GP_PIN(5, 5), /* AVB2_MAGIC */ + [ 6] = RCAR_GP_PIN(5, 6), /* AVB2_MDC */ + [ 7] = RCAR_GP_PIN(5, 7), /* AVB2_TXCREFCLK */ + [ 8] = RCAR_GP_PIN(5, 8), /* AVB2_TD3 */ + [ 9] = RCAR_GP_PIN(5, 9), /* AVB2_RD3 */ + [10] = RCAR_GP_PIN(5, 10), /* AVB2_MDIO */ + [11] = RCAR_GP_PIN(5, 11), /* AVB2_TD2 */ + [12] = RCAR_GP_PIN(5, 12), /* AVB2_TD1 */ + [13] = RCAR_GP_PIN(5, 13), /* AVB2_RD2 */ + [14] = RCAR_GP_PIN(5, 14), /* AVB2_RD1 */ + [15] = RCAR_GP_PIN(5, 15), /* AVB2_TD0 */ + [16] = RCAR_GP_PIN(5, 16), /* AVB2_TXC */ + [17] = RCAR_GP_PIN(5, 17), /* AVB2_RD0 */ + [18] = RCAR_GP_PIN(5, 18), /* AVB2_RXC */ + [19] = RCAR_GP_PIN(5, 19), /* AVB2_TX_CTL */ + [20] = RCAR_GP_PIN(5, 20), /* AVB2_RX_CTL */ + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN6", 0xE60610C0, "PUD6", 0xE60610E0) { + [ 0] = RCAR_GP_PIN(6, 0), /* AVB1_MDIO */ + [ 1] = RCAR_GP_PIN(6, 1), /* AVB1_MAGIC */ + [ 2] = RCAR_GP_PIN(6, 2), /* AVB1_MDC */ + [ 3] = RCAR_GP_PIN(6, 3), /* AVB1_PHY_INT */ + [ 4] = RCAR_GP_PIN(6, 4), /* AVB1_LINK */ + [ 5] = RCAR_GP_PIN(6, 5), /* AVB1_AVTP_MATCH */ + [ 6] = RCAR_GP_PIN(6, 6), /* AVB1_TXC */ + [ 7] = RCAR_GP_PIN(6, 7), /* AVB1_TX_CTL */ + [ 8] = RCAR_GP_PIN(6, 8), /* AVB1_RXC */ + [ 9] = RCAR_GP_PIN(6, 9), /* AVB1_RX_CTL */ + [10] = RCAR_GP_PIN(6, 10), /* AVB1_AVTP_PPS */ + [11] = RCAR_GP_PIN(6, 11), /* AVB1_AVTP_CAPTURE */ + [12] = RCAR_GP_PIN(6, 12), /* AVB1_TD1 */ + [13] = RCAR_GP_PIN(6, 13), /* AVB1_TD0 */ + [14] = RCAR_GP_PIN(6, 14), /* AVB1_RD1*/ + [15] = RCAR_GP_PIN(6, 15), /* AVB1_RD0 */ + [16] = RCAR_GP_PIN(6, 16), /* AVB1_TD2 */ + [17] = RCAR_GP_PIN(6, 17), /* AVB1_RD2 */ + [18] = RCAR_GP_PIN(6, 18), /* AVB1_TD3 */ + [19] = RCAR_GP_PIN(6, 19), /* AVB1_RD3 */ + [20] = RCAR_GP_PIN(6, 20), /* AVB1_TXCREFCLK */ + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN7", 0xE60618C0, "PUD7", 0xE60618E0) { + [ 0] = RCAR_GP_PIN(7, 0), /* AVB0_AVTP_PPS */ + [ 1] = RCAR_GP_PIN(7, 1), /* AVB0_AVTP_CAPTURE */ + [ 2] = RCAR_GP_PIN(7, 2), /* AVB0_AVTP_MATCH */ + [ 3] = RCAR_GP_PIN(7, 3), /* AVB0_TD3 */ + [ 4] = RCAR_GP_PIN(7, 4), /* AVB0_LINK */ + [ 5] = RCAR_GP_PIN(7, 5), /* AVB0_PHY_INT */ + [ 6] = RCAR_GP_PIN(7, 6), /* AVB0_TD2 */ + [ 7] = RCAR_GP_PIN(7, 7), /* AVB0_TD1 */ + [ 8] = RCAR_GP_PIN(7, 8), /* AVB0_RD3 */ + [ 9] = RCAR_GP_PIN(7, 9), /* AVB0_TXCREFCLK */ + [10] = RCAR_GP_PIN(7, 10), /* AVB0_MAGIC */ + [11] = RCAR_GP_PIN(7, 11), /* AVB0_TD0 */ + [12] = RCAR_GP_PIN(7, 12), /* AVB0_RD2 */ + [13] = RCAR_GP_PIN(7, 13), /* AVB0_MDC */ + [14] = RCAR_GP_PIN(7, 14), /* AVB0_MDIO */ + [15] = RCAR_GP_PIN(7, 15), /* AVB0_TXC */ + [16] = RCAR_GP_PIN(7, 16), /* AVB0_TX_CTL */ + [17] = RCAR_GP_PIN(7, 17), /* AVB0_RD1 */ + [18] = RCAR_GP_PIN(7, 18), /* AVB0_RD0 */ + [19] = RCAR_GP_PIN(7, 19), /* AVB0_RXC */ + [20] = RCAR_GP_PIN(7, 20), /* AVB0_RX_CTL */ + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { PINMUX_BIAS_REG("PUEN8", 0xE60680C0, "PUD8", 0xE60680E0) { + [ 0] = RCAR_GP_PIN(8, 0), /* SCL0 */ + [ 1] = RCAR_GP_PIN(8, 1), /* SDA0 */ + [ 2] = RCAR_GP_PIN(8, 2), /* SCL1 */ + [ 3] = RCAR_GP_PIN(8, 3), /* SDA1 */ + [ 4] = RCAR_GP_PIN(8, 4), /* SCL2 */ + [ 5] = RCAR_GP_PIN(8, 5), /* SDA2 */ + [ 6] = RCAR_GP_PIN(8, 6), /* SCL3 */ + [ 7] = RCAR_GP_PIN(8, 7), /* SDA3 */ + [ 8] = RCAR_GP_PIN(8, 8), /* SCL4 */ + [ 9] = RCAR_GP_PIN(8, 9), /* SDA4 */ + [10] = RCAR_GP_PIN(8, 10), /* SCL5 */ + [11] = RCAR_GP_PIN(8, 11), /* SDA5 */ + [12] = RCAR_GP_PIN(8, 12), /* GP8_12 */ + [13] = RCAR_GP_PIN(8, 13), /* GP8_13 */ + [14] = SH_PFC_PIN_NONE, + [15] = SH_PFC_PIN_NONE, + [16] = SH_PFC_PIN_NONE, + [17] = SH_PFC_PIN_NONE, + [18] = SH_PFC_PIN_NONE, + [19] = SH_PFC_PIN_NONE, + [20] = SH_PFC_PIN_NONE, + [21] = SH_PFC_PIN_NONE, + [22] = SH_PFC_PIN_NONE, + [23] = SH_PFC_PIN_NONE, + [24] = SH_PFC_PIN_NONE, + [25] = SH_PFC_PIN_NONE, + [26] = SH_PFC_PIN_NONE, + [27] = SH_PFC_PIN_NONE, + [28] = SH_PFC_PIN_NONE, + [29] = SH_PFC_PIN_NONE, + [30] = SH_PFC_PIN_NONE, + [31] = SH_PFC_PIN_NONE, + } }, + { /* sentinel */ }, +}; + +static const struct sh_pfc_soc_operations r8a779g0_pin_ops = { + .pin_to_pocctrl = r8a779g0_pin_to_pocctrl, + .get_bias = rcar_pinmux_get_bias, + .set_bias = rcar_pinmux_set_bias, +}; + +const struct sh_pfc_soc_info r8a779g0_pinmux_info = { + .name = "r8a779g0_pfc", + .ops = &r8a779g0_pin_ops, + .unlock_reg = 0x1ff, /* PMMRn mask */ + + .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END }, + + .pins = pinmux_pins, + .nr_pins = ARRAY_SIZE(pinmux_pins), + .groups = pinmux_groups, + .nr_groups = ARRAY_SIZE(pinmux_groups), + .functions = pinmux_functions, + .nr_functions = ARRAY_SIZE(pinmux_functions), + + .cfg_regs = pinmux_config_regs, + .drive_regs = pinmux_drive_regs, + .bias_regs = pinmux_bias_regs, + .ioctrl_regs = pinmux_ioctrl_regs, + + .pinmux_data = pinmux_data, + .pinmux_data_size = ARRAY_SIZE(pinmux_data), +}; diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index c47eed9d948f..a43824fd9505 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -527,6 +527,8 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, if (!(cfg & PIN_CFG_IEN)) return -EINVAL; arg = rzg2l_read_pin_config(pctrl, IEN(port_offset), bit, IEN_MASK); + if (!arg) + return -EINVAL; break; case PIN_CONFIG_POWER_SOURCE: { diff --git a/drivers/pinctrl/renesas/pinctrl-rzv2m.c b/drivers/pinctrl/renesas/pinctrl-rzv2m.c new file mode 100644 index 000000000000..e8c18198bebd --- /dev/null +++ b/drivers/pinctrl/renesas/pinctrl-rzv2m.c @@ -0,0 +1,1119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/V2M Pin Control and GPIO driver core + * + * Based on: + * Renesas RZ/G2L Pin Control and GPIO driver core + * + * Copyright (C) 2022 Renesas Electronics Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/spinlock.h> + +#include <dt-bindings/pinctrl/rzv2m-pinctrl.h> + +#include "../core.h" +#include "../pinconf.h" +#include "../pinmux.h" + +#define DRV_NAME "pinctrl-rzv2m" + +/* + * Use 16 lower bits [15:0] for pin identifier + * Use 16 higher bits [31:16] for pin mux function + */ +#define MUX_PIN_ID_MASK GENMASK(15, 0) +#define MUX_FUNC_MASK GENMASK(31, 16) +#define MUX_FUNC(pinconf) FIELD_GET(MUX_FUNC_MASK, (pinconf)) + +/* PIN capabilities */ +#define PIN_CFG_GRP_1_8V_2 1 +#define PIN_CFG_GRP_1_8V_3 2 +#define PIN_CFG_GRP_SWIO_1 3 +#define PIN_CFG_GRP_SWIO_2 4 +#define PIN_CFG_GRP_3_3V 5 +#define PIN_CFG_GRP_MASK GENMASK(2, 0) +#define PIN_CFG_BIAS BIT(3) +#define PIN_CFG_DRV BIT(4) +#define PIN_CFG_SLEW BIT(5) + +#define RZV2M_MPXED_PIN_FUNCS (PIN_CFG_BIAS | \ + PIN_CFG_DRV | \ + PIN_CFG_SLEW) + +/* + * n indicates number of pins in the port, a is the register index + * and f is pin configuration capabilities supported. + */ +#define RZV2M_GPIO_PORT_PACK(n, a, f) (((n) << 24) | ((a) << 16) | (f)) +#define RZV2M_GPIO_PORT_GET_PINCNT(x) FIELD_GET(GENMASK(31, 24), (x)) +#define RZV2M_GPIO_PORT_GET_INDEX(x) FIELD_GET(GENMASK(23, 16), (x)) +#define RZV2M_GPIO_PORT_GET_CFGS(x) FIELD_GET(GENMASK(15, 0), (x)) + +#define RZV2M_DEDICATED_PORT_IDX 22 + +/* + * BIT(31) indicates dedicated pin, b is the register bits (b * 16) + * and f is the pin configuration capabilities supported. + */ +#define RZV2M_SINGLE_PIN BIT(31) +#define RZV2M_SINGLE_PIN_PACK(b, f) (RZV2M_SINGLE_PIN | \ + ((RZV2M_DEDICATED_PORT_IDX) << 24) | \ + ((b) << 16) | (f)) +#define RZV2M_SINGLE_PIN_GET_PORT(x) FIELD_GET(GENMASK(30, 24), (x)) +#define RZV2M_SINGLE_PIN_GET_BIT(x) FIELD_GET(GENMASK(23, 16), (x)) +#define RZV2M_SINGLE_PIN_GET_CFGS(x) FIELD_GET(GENMASK(15, 0), (x)) + +#define RZV2M_PIN_ID_TO_PORT(id) ((id) / RZV2M_PINS_PER_PORT) +#define RZV2M_PIN_ID_TO_PIN(id) ((id) % RZV2M_PINS_PER_PORT) + +#define DO(n) (0x00 + (n) * 0x40) +#define OE(n) (0x04 + (n) * 0x40) +#define IE(n) (0x08 + (n) * 0x40) +#define PFSEL(n) (0x10 + (n) * 0x40) +#define DI(n) (0x20 + (n) * 0x40) +#define PUPD(n) (0x24 + (n) * 0x40) +#define DRV(n) ((n) < RZV2M_DEDICATED_PORT_IDX ? (0x28 + (n) * 0x40) \ + : 0x590) +#define SR(n) ((n) < RZV2M_DEDICATED_PORT_IDX ? (0x2c + (n) * 0x40) \ + : 0x594) +#define DI_MSK(n) (0x30 + (n) * 0x40) +#define EN_MSK(n) (0x34 + (n) * 0x40) + +#define PFC_MASK 0x07 +#define PUPD_MASK 0x03 +#define DRV_MASK 0x03 + +struct rzv2m_dedicated_configs { + const char *name; + u32 config; +}; + +struct rzv2m_pinctrl_data { + const char * const *port_pins; + const u32 *port_pin_configs; + const struct rzv2m_dedicated_configs *dedicated_pins; + unsigned int n_port_pins; + unsigned int n_dedicated_pins; +}; + +struct rzv2m_pinctrl { + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + struct pinctrl_pin_desc *pins; + + const struct rzv2m_pinctrl_data *data; + void __iomem *base; + struct device *dev; + struct clk *clk; + + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range gpio_range; + + spinlock_t lock; +}; + +static const unsigned int drv_1_8V_group2_uA[] = { 1800, 3800, 7800, 11000 }; +static const unsigned int drv_1_8V_group3_uA[] = { 1600, 3200, 6400, 9600 }; +static const unsigned int drv_SWIO_group2_3_3V_uA[] = { 9000, 11000, 13000, 18000 }; +static const unsigned int drv_3_3V_group_uA[] = { 2000, 4000, 8000, 12000 }; + +/* Helper for registers that have a write enable bit in the upper word */ +static void rzv2m_writel_we(void __iomem *addr, u8 shift, u8 value) +{ + writel((BIT(16) | value) << shift, addr); +} + +static void rzv2m_pinctrl_set_pfc_mode(struct rzv2m_pinctrl *pctrl, + u8 port, u8 pin, u8 func) +{ + void __iomem *addr; + + /* Mask input/output */ + rzv2m_writel_we(pctrl->base + DI_MSK(port), pin, 1); + rzv2m_writel_we(pctrl->base + EN_MSK(port), pin, 1); + + /* Select the function and set the write enable bits */ + addr = pctrl->base + PFSEL(port) + (pin / 4) * 4; + writel(((PFC_MASK << 16) | func) << ((pin % 4) * 4), addr); + + /* Unmask input/output */ + rzv2m_writel_we(pctrl->base + EN_MSK(port), pin, 0); + rzv2m_writel_we(pctrl->base + DI_MSK(port), pin, 0); +}; + +static int rzv2m_pinctrl_set_mux(struct pinctrl_dev *pctldev, + unsigned int func_selector, + unsigned int group_selector) +{ + struct rzv2m_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct function_desc *func; + unsigned int i, *psel_val; + struct group_desc *group; + int *pins; + + func = pinmux_generic_get_function(pctldev, func_selector); + if (!func) + return -EINVAL; + group = pinctrl_generic_get_group(pctldev, group_selector); + if (!group) + return -EINVAL; + + psel_val = func->data; + pins = group->pins; + + for (i = 0; i < group->num_pins; i++) { + dev_dbg(pctrl->dev, "port:%u pin: %u PSEL:%u\n", + RZV2M_PIN_ID_TO_PORT(pins[i]), RZV2M_PIN_ID_TO_PIN(pins[i]), + psel_val[i]); + rzv2m_pinctrl_set_pfc_mode(pctrl, RZV2M_PIN_ID_TO_PORT(pins[i]), + RZV2M_PIN_ID_TO_PIN(pins[i]), psel_val[i]); + } + + return 0; +}; + +static int rzv2m_map_add_config(struct pinctrl_map *map, + const char *group_or_pin, + enum pinctrl_map_type type, + unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *cfgs; + + cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), + GFP_KERNEL); + if (!cfgs) + return -ENOMEM; + + map->type = type; + map->data.configs.group_or_pin = group_or_pin; + map->data.configs.configs = cfgs; + map->data.configs.num_configs = num_configs; + + return 0; +} + +static int rzv2m_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps, + unsigned int *index) +{ + struct rzv2m_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct pinctrl_map *maps = *map; + unsigned int nmaps = *num_maps; + unsigned long *configs = NULL; + unsigned int *pins, *psel_val; + unsigned int num_pinmux = 0; + unsigned int idx = *index; + unsigned int num_pins, i; + unsigned int num_configs; + struct property *pinmux; + struct property *prop; + int ret, gsel, fsel; + const char **pin_fn; + const char *pin; + + pinmux = of_find_property(np, "pinmux", NULL); + if (pinmux) + num_pinmux = pinmux->length / sizeof(u32); + + ret = of_property_count_strings(np, "pins"); + if (ret == -EINVAL) { + num_pins = 0; + } else if (ret < 0) { + dev_err(pctrl->dev, "Invalid pins list in DT\n"); + return ret; + } else { + num_pins = ret; + } + + if (!num_pinmux && !num_pins) + return 0; + + if (num_pinmux && num_pins) { + dev_err(pctrl->dev, + "DT node must contain either a pinmux or pins and not both\n"); + return -EINVAL; + } + + ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); + if (ret < 0) + return ret; + + if (num_pins && !num_configs) { + dev_err(pctrl->dev, "DT node must contain a config\n"); + ret = -ENODEV; + goto done; + } + + if (num_pinmux) + nmaps += 1; + + if (num_pins) + nmaps += num_pins; + + maps = krealloc_array(maps, nmaps, sizeof(*maps), GFP_KERNEL); + if (!maps) { + ret = -ENOMEM; + goto done; + } + + *map = maps; + *num_maps = nmaps; + if (num_pins) { + of_property_for_each_string(np, "pins", prop, pin) { + ret = rzv2m_map_add_config(&maps[idx], pin, + PIN_MAP_TYPE_CONFIGS_PIN, + configs, num_configs); + if (ret < 0) + goto done; + + idx++; + } + ret = 0; + goto done; + } + + pins = devm_kcalloc(pctrl->dev, num_pinmux, sizeof(*pins), GFP_KERNEL); + psel_val = devm_kcalloc(pctrl->dev, num_pinmux, sizeof(*psel_val), + GFP_KERNEL); + pin_fn = devm_kzalloc(pctrl->dev, sizeof(*pin_fn), GFP_KERNEL); + if (!pins || !psel_val || !pin_fn) { + ret = -ENOMEM; + goto done; + } + + /* Collect pin locations and mux settings from DT properties */ + for (i = 0; i < num_pinmux; ++i) { + u32 value; + + ret = of_property_read_u32_index(np, "pinmux", i, &value); + if (ret) + goto done; + pins[i] = value & MUX_PIN_ID_MASK; + psel_val[i] = MUX_FUNC(value); + } + + /* Register a single pin group listing all the pins we read from DT */ + gsel = pinctrl_generic_add_group(pctldev, np->name, pins, num_pinmux, NULL); + if (gsel < 0) { + ret = gsel; + goto done; + } + + /* + * Register a single group function where the 'data' is an array PSEL + * register values read from DT. + */ + pin_fn[0] = np->name; + fsel = pinmux_generic_add_function(pctldev, np->name, pin_fn, 1, + psel_val); + if (fsel < 0) { + ret = fsel; + goto remove_group; + } + + maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; + maps[idx].data.mux.group = np->name; + maps[idx].data.mux.function = np->name; + idx++; + + dev_dbg(pctrl->dev, "Parsed %pOF with %d pins\n", np, num_pinmux); + ret = 0; + goto done; + +remove_group: + pinctrl_generic_remove_group(pctldev, gsel); +done: + *index = idx; + kfree(configs); + return ret; +} + +static void rzv2m_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned int num_maps) +{ + unsigned int i; + + if (!map) + return; + + for (i = 0; i < num_maps; ++i) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || + map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + } + kfree(map); +} + +static int rzv2m_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct rzv2m_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct device_node *child; + unsigned int index; + int ret; + + *map = NULL; + *num_maps = 0; + index = 0; + + for_each_child_of_node(np, child) { + ret = rzv2m_dt_subnode_to_map(pctldev, child, map, + num_maps, &index); + if (ret < 0) { + of_node_put(child); + goto done; + } + } + + if (*num_maps == 0) { + ret = rzv2m_dt_subnode_to_map(pctldev, np, map, + num_maps, &index); + if (ret < 0) + goto done; + } + + if (*num_maps) + return 0; + + dev_err(pctrl->dev, "no mapping found in node %pOF\n", np); + ret = -EINVAL; + +done: + if (ret < 0) + rzv2m_dt_free_map(pctldev, *map, *num_maps); + + return ret; +} + +static int rzv2m_validate_gpio_pin(struct rzv2m_pinctrl *pctrl, + u32 cfg, u32 port, u8 bit) +{ + u8 pincount = RZV2M_GPIO_PORT_GET_PINCNT(cfg); + u32 port_index = RZV2M_GPIO_PORT_GET_INDEX(cfg); + u32 data; + + if (bit >= pincount || port >= pctrl->data->n_port_pins) + return -EINVAL; + + data = pctrl->data->port_pin_configs[port]; + if (port_index != RZV2M_GPIO_PORT_GET_INDEX(data)) + return -EINVAL; + + return 0; +} + +static void rzv2m_rmw_pin_config(struct rzv2m_pinctrl *pctrl, u32 offset, + u8 shift, u32 mask, u32 val) +{ + void __iomem *addr = pctrl->base + offset; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&pctrl->lock, flags); + reg = readl(addr) & ~(mask << shift); + writel(reg | (val << shift), addr); + spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static int rzv2m_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int _pin, + unsigned long *config) +{ + struct rzv2m_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + const struct pinctrl_pin_desc *pin = &pctrl->desc.pins[_pin]; + unsigned int *pin_data = pin->drv_data; + unsigned int arg = 0; + u32 port; + u32 cfg; + u8 bit; + u32 val; + + if (!pin_data) + return -EINVAL; + + if (*pin_data & RZV2M_SINGLE_PIN) { + port = RZV2M_SINGLE_PIN_GET_PORT(*pin_data); + cfg = RZV2M_SINGLE_PIN_GET_CFGS(*pin_data); + bit = RZV2M_SINGLE_PIN_GET_BIT(*pin_data); + } else { + cfg = RZV2M_GPIO_PORT_GET_CFGS(*pin_data); + port = RZV2M_PIN_ID_TO_PORT(_pin); + bit = RZV2M_PIN_ID_TO_PIN(_pin); + + if (rzv2m_validate_gpio_pin(pctrl, *pin_data, RZV2M_PIN_ID_TO_PORT(_pin), bit)) + return -EINVAL; + } + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: { + enum pin_config_param bias; + + if (!(cfg & PIN_CFG_BIAS)) + return -EINVAL; + + /* PUPD uses 2-bits per pin */ + bit *= 2; + + switch ((readl(pctrl->base + PUPD(port)) >> bit) & PUPD_MASK) { + case 0: + bias = PIN_CONFIG_BIAS_PULL_DOWN; + break; + case 2: + bias = PIN_CONFIG_BIAS_PULL_UP; + break; + default: + bias = PIN_CONFIG_BIAS_DISABLE; + } + + if (bias != param) + return -EINVAL; + break; + } + + case PIN_CONFIG_DRIVE_STRENGTH_UA: + if (!(cfg & PIN_CFG_DRV)) + return -EINVAL; + + /* DRV uses 2-bits per pin */ + bit *= 2; + + val = (readl(pctrl->base + DRV(port)) >> bit) & DRV_MASK; + + switch (cfg & PIN_CFG_GRP_MASK) { + case PIN_CFG_GRP_1_8V_2: + arg = drv_1_8V_group2_uA[val]; + break; + case PIN_CFG_GRP_1_8V_3: + arg = drv_1_8V_group3_uA[val]; + break; + case PIN_CFG_GRP_SWIO_2: + arg = drv_SWIO_group2_3_3V_uA[val]; + break; + case PIN_CFG_GRP_SWIO_1: + case PIN_CFG_GRP_3_3V: + arg = drv_3_3V_group_uA[val]; + break; + default: + return -EINVAL; + } + + break; + + case PIN_CONFIG_SLEW_RATE: + if (!(cfg & PIN_CFG_SLEW)) + return -EINVAL; + + arg = readl(pctrl->base + SR(port)) & BIT(bit); + break; + + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +}; + +static int rzv2m_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int _pin, + unsigned long *_configs, + unsigned int num_configs) +{ + struct rzv2m_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct pinctrl_pin_desc *pin = &pctrl->desc.pins[_pin]; + unsigned int *pin_data = pin->drv_data; + enum pin_config_param param; + u32 port; + unsigned int i; + u32 cfg; + u8 bit; + u32 val; + + if (!pin_data) + return -EINVAL; + + if (*pin_data & RZV2M_SINGLE_PIN) { + port = RZV2M_SINGLE_PIN_GET_PORT(*pin_data); + cfg = RZV2M_SINGLE_PIN_GET_CFGS(*pin_data); + bit = RZV2M_SINGLE_PIN_GET_BIT(*pin_data); + } else { + cfg = RZV2M_GPIO_PORT_GET_CFGS(*pin_data); + port = RZV2M_PIN_ID_TO_PORT(_pin); + bit = RZV2M_PIN_ID_TO_PIN(_pin); + + if (rzv2m_validate_gpio_pin(pctrl, *pin_data, RZV2M_PIN_ID_TO_PORT(_pin), bit)) + return -EINVAL; + } + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(_configs[i]); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!(cfg & PIN_CFG_BIAS)) + return -EINVAL; + + /* PUPD uses 2-bits per pin */ + bit *= 2; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + val = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + val = 2; + break; + default: + val = 1; + } + + rzv2m_rmw_pin_config(pctrl, PUPD(port), bit, PUPD_MASK, val); + break; + + case PIN_CONFIG_DRIVE_STRENGTH_UA: { + unsigned int arg = pinconf_to_config_argument(_configs[i]); + const unsigned int *drv_strengths; + unsigned int index; + + if (!(cfg & PIN_CFG_DRV)) + return -EINVAL; + + switch (cfg & PIN_CFG_GRP_MASK) { + case PIN_CFG_GRP_1_8V_2: + drv_strengths = drv_1_8V_group2_uA; + break; + case PIN_CFG_GRP_1_8V_3: + drv_strengths = drv_1_8V_group3_uA; + break; + case PIN_CFG_GRP_SWIO_2: + drv_strengths = drv_SWIO_group2_3_3V_uA; + break; + case PIN_CFG_GRP_SWIO_1: + case PIN_CFG_GRP_3_3V: + drv_strengths = drv_3_3V_group_uA; + break; + default: + return -EINVAL; + } + + for (index = 0; index < 4; index++) { + if (arg == drv_strengths[index]) + break; + } + if (index >= 4) + return -EINVAL; + + /* DRV uses 2-bits per pin */ + bit *= 2; + + rzv2m_rmw_pin_config(pctrl, DRV(port), bit, DRV_MASK, index); + break; + } + + case PIN_CONFIG_SLEW_RATE: { + unsigned int arg = pinconf_to_config_argument(_configs[i]); + + if (!(cfg & PIN_CFG_SLEW)) + return -EINVAL; + + rzv2m_writel_we(pctrl->base + SR(port), bit, !arg); + break; + } + + default: + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int rzv2m_pinctrl_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *configs, + unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int i, npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; i++) { + ret = rzv2m_pinctrl_pinconf_set(pctldev, pins[i], configs, + num_configs); + if (ret) + return ret; + } + + return 0; +}; + +static int rzv2m_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int i, npins, prev_config = 0; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; i++) { + ret = rzv2m_pinctrl_pinconf_get(pctldev, pins[i], config); + if (ret) + return ret; + + /* Check config matches previous pins */ + if (i && prev_config != *config) + return -EOPNOTSUPP; + + prev_config = *config; + } + + return 0; +}; + +static const struct pinctrl_ops rzv2m_pinctrl_pctlops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .dt_node_to_map = rzv2m_dt_node_to_map, + .dt_free_map = rzv2m_dt_free_map, +}; + +static const struct pinmux_ops rzv2m_pinctrl_pmxops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = rzv2m_pinctrl_set_mux, + .strict = true, +}; + +static const struct pinconf_ops rzv2m_pinctrl_confops = { + .is_generic = true, + .pin_config_get = rzv2m_pinctrl_pinconf_get, + .pin_config_set = rzv2m_pinctrl_pinconf_set, + .pin_config_group_set = rzv2m_pinctrl_pinconf_group_set, + .pin_config_group_get = rzv2m_pinctrl_pinconf_group_get, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +static int rzv2m_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + int ret; + + ret = pinctrl_gpio_request(chip->base + offset); + if (ret) + return ret; + + rzv2m_pinctrl_set_pfc_mode(pctrl, port, bit, 0); + + return 0; +} + +static void rzv2m_gpio_set_direction(struct rzv2m_pinctrl *pctrl, u32 port, + u8 bit, bool output) +{ + rzv2m_writel_we(pctrl->base + OE(port), bit, output); + rzv2m_writel_we(pctrl->base + IE(port), bit, !output); +} + +static int rzv2m_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + + if (!(readl(pctrl->base + IE(port)) & BIT(bit))) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int rzv2m_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + + rzv2m_gpio_set_direction(pctrl, port, bit, false); + + return 0; +} + +static void rzv2m_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + + rzv2m_writel_we(pctrl->base + DO(port), bit, !!value); +} + +static int rzv2m_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + + rzv2m_gpio_set(chip, offset, value); + rzv2m_gpio_set_direction(pctrl, port, bit, true); + + return 0; +} + +static int rzv2m_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct rzv2m_pinctrl *pctrl = gpiochip_get_data(chip); + u32 port = RZV2M_PIN_ID_TO_PORT(offset); + u8 bit = RZV2M_PIN_ID_TO_PIN(offset); + int direction = rzv2m_gpio_get_direction(chip, offset); + + if (direction == GPIO_LINE_DIRECTION_IN) + return !!(readl(pctrl->base + DI(port)) & BIT(bit)); + else + return !!(readl(pctrl->base + DO(port)) & BIT(bit)); +} + +static void rzv2m_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + pinctrl_gpio_free(chip->base + offset); + + /* + * Set the GPIO as an input to ensure that the next GPIO request won't + * drive the GPIO pin as an output. + */ + rzv2m_gpio_direction_input(chip, offset); +} + +static const char * const rzv2m_gpio_names[] = { + "P0_0", "P0_1", "P0_2", "P0_3", "P0_4", "P0_5", "P0_6", "P0_7", + "P0_8", "P0_9", "P0_10", "P0_11", "P0_12", "P0_13", "P0_14", "P0_15", + "P1_0", "P1_1", "P1_2", "P1_3", "P1_4", "P1_5", "P1_6", "P1_7", + "P1_8", "P1_9", "P1_10", "P1_11", "P1_12", "P1_13", "P1_14", "P1_15", + "P2_0", "P2_1", "P2_2", "P2_3", "P2_4", "P2_5", "P2_6", "P2_7", + "P2_8", "P2_9", "P2_10", "P2_11", "P2_12", "P2_13", "P2_14", "P2_15", + "P3_0", "P3_1", "P3_2", "P3_3", "P3_4", "P3_5", "P3_6", "P3_7", + "P3_8", "P3_9", "P3_10", "P3_11", "P3_12", "P3_13", "P3_14", "P3_15", + "P4_0", "P4_1", "P4_2", "P4_3", "P4_4", "P4_5", "P4_6", "P4_7", + "P4_8", "P4_9", "P4_10", "P4_11", "P4_12", "P4_13", "P4_14", "P4_15", + "P5_0", "P5_1", "P5_2", "P5_3", "P5_4", "P5_5", "P5_6", "P5_7", + "P5_8", "P5_9", "P5_10", "P5_11", "P5_12", "P5_13", "P5_14", "P5_15", + "P6_0", "P6_1", "P6_2", "P6_3", "P6_4", "P6_5", "P6_6", "P6_7", + "P6_8", "P6_9", "P6_10", "P6_11", "P6_12", "P6_13", "P6_14", "P6_15", + "P7_0", "P7_1", "P7_2", "P7_3", "P7_4", "P7_5", "P7_6", "P7_7", + "P7_8", "P7_9", "P7_10", "P7_11", "P7_12", "P7_13", "P7_14", "P7_15", + "P8_0", "P8_1", "P8_2", "P8_3", "P8_4", "P8_5", "P8_6", "P8_7", + "P8_8", "P8_9", "P8_10", "P8_11", "P8_12", "P8_13", "P8_14", "P8_15", + "P9_0", "P9_1", "P9_2", "P9_3", "P9_4", "P9_5", "P9_6", "P9_7", + "P9_8", "P9_9", "P9_10", "P9_11", "P9_12", "P9_13", "P9_14", "P9_15", + "P10_0", "P10_1", "P10_2", "P10_3", "P10_4", "P10_5", "P10_6", "P10_7", + "P10_8", "P10_9", "P10_10", "P10_11", "P10_12", "P10_13", "P10_14", "P10_15", + "P11_0", "P11_1", "P11_2", "P11_3", "P11_4", "P11_5", "P11_6", "P11_7", + "P11_8", "P11_9", "P11_10", "P11_11", "P11_12", "P11_13", "P11_14", "P11_15", + "P12_0", "P12_1", "P12_2", "P12_3", "P12_4", "P12_5", "P12_6", "P12_7", + "P12_8", "P12_9", "P12_10", "P12_11", "P12_12", "P12_13", "P12_14", "P12_15", + "P13_0", "P13_1", "P13_2", "P13_3", "P13_4", "P13_5", "P13_6", "P13_7", + "P13_8", "P13_9", "P13_10", "P13_11", "P13_12", "P13_13", "P13_14", "P13_15", + "P14_0", "P14_1", "P14_2", "P14_3", "P14_4", "P14_5", "P14_6", "P14_7", + "P14_8", "P14_9", "P14_10", "P14_11", "P14_12", "P14_13", "P14_14", "P14_15", + "P15_0", "P15_1", "P15_2", "P15_3", "P15_4", "P15_5", "P15_6", "P15_7", + "P15_8", "P15_9", "P15_10", "P15_11", "P15_12", "P15_13", "P15_14", "P15_15", + "P16_0", "P16_1", "P16_2", "P16_3", "P16_4", "P16_5", "P16_6", "P16_7", + "P16_8", "P16_9", "P16_10", "P16_11", "P16_12", "P16_13", "P16_14", "P16_15", + "P17_0", "P17_1", "P17_2", "P17_3", "P17_4", "P17_5", "P17_6", "P17_7", + "P17_8", "P17_9", "P17_10", "P17_11", "P17_12", "P17_13", "P17_14", "P17_15", + "P18_0", "P18_1", "P18_2", "P18_3", "P18_4", "P18_5", "P18_6", "P18_7", + "P18_8", "P18_9", "P18_10", "P18_11", "P18_12", "P18_13", "P18_14", "P18_15", + "P19_0", "P19_1", "P19_2", "P19_3", "P19_4", "P19_5", "P19_6", "P19_7", + "P19_8", "P19_9", "P19_10", "P19_11", "P19_12", "P19_13", "P19_14", "P19_15", + "P20_0", "P20_1", "P20_2", "P20_3", "P20_4", "P20_5", "P20_6", "P20_7", + "P20_8", "P20_9", "P20_10", "P20_11", "P20_12", "P20_13", "P20_14", "P20_15", + "P21_0", "P21_1", "P21_2", "P21_3", "P21_4", "P21_5", "P21_6", "P21_7", + "P21_8", "P21_9", "P21_10", "P21_11", "P21_12", "P21_13", "P21_14", "P21_15", +}; + +static const u32 rzv2m_gpio_configs[] = { + RZV2M_GPIO_PORT_PACK(14, 0, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(16, 1, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(8, 2, PIN_CFG_GRP_1_8V_3 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(16, 3, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(8, 4, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(4, 5, PIN_CFG_GRP_1_8V_3 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(12, 6, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(6, 7, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(8, 8, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(8, 9, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(9, 10, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(9, 11, PIN_CFG_GRP_SWIO_1 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(4, 12, PIN_CFG_GRP_3_3V | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(12, 13, PIN_CFG_GRP_3_3V | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(8, 14, PIN_CFG_GRP_3_3V | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(16, 15, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(14, 16, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(1, 17, PIN_CFG_GRP_SWIO_2 | RZV2M_MPXED_PIN_FUNCS), + RZV2M_GPIO_PORT_PACK(0, 18, 0), + RZV2M_GPIO_PORT_PACK(0, 19, 0), + RZV2M_GPIO_PORT_PACK(3, 20, PIN_CFG_GRP_1_8V_2 | PIN_CFG_DRV), + RZV2M_GPIO_PORT_PACK(1, 21, PIN_CFG_GRP_SWIO_1 | PIN_CFG_DRV | PIN_CFG_SLEW), +}; + +static const struct rzv2m_dedicated_configs rzv2m_dedicated_pins[] = { + { "NAWPN", RZV2M_SINGLE_PIN_PACK(0, + (PIN_CFG_GRP_SWIO_2 | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "IM0CLK", RZV2M_SINGLE_PIN_PACK(1, + (PIN_CFG_GRP_SWIO_1 | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "IM1CLK", RZV2M_SINGLE_PIN_PACK(2, + (PIN_CFG_GRP_SWIO_1 | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "DETDO", RZV2M_SINGLE_PIN_PACK(5, + (PIN_CFG_GRP_1_8V_3 | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "DETMS", RZV2M_SINGLE_PIN_PACK(6, + (PIN_CFG_GRP_1_8V_3 | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "PCRSTOUTB", RZV2M_SINGLE_PIN_PACK(12, + (PIN_CFG_GRP_3_3V | PIN_CFG_DRV | PIN_CFG_SLEW)) }, + { "USPWEN", RZV2M_SINGLE_PIN_PACK(14, + (PIN_CFG_GRP_3_3V | PIN_CFG_DRV | PIN_CFG_SLEW)) }, +}; + +static int rzv2m_gpio_register(struct rzv2m_pinctrl *pctrl) +{ + struct device_node *np = pctrl->dev->of_node; + struct gpio_chip *chip = &pctrl->gpio_chip; + const char *name = dev_name(pctrl->dev); + struct of_phandle_args of_args; + int ret; + + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &of_args); + if (ret) { + dev_err(pctrl->dev, "Unable to parse gpio-ranges\n"); + return ret; + } + + if (of_args.args[0] != 0 || of_args.args[1] != 0 || + of_args.args[2] != pctrl->data->n_port_pins) { + dev_err(pctrl->dev, "gpio-ranges does not match selected SOC\n"); + return -EINVAL; + } + + chip->names = pctrl->data->port_pins; + chip->request = rzv2m_gpio_request; + chip->free = rzv2m_gpio_free; + chip->get_direction = rzv2m_gpio_get_direction; + chip->direction_input = rzv2m_gpio_direction_input; + chip->direction_output = rzv2m_gpio_direction_output; + chip->get = rzv2m_gpio_get; + chip->set = rzv2m_gpio_set; + chip->label = name; + chip->parent = pctrl->dev; + chip->owner = THIS_MODULE; + chip->base = -1; + chip->ngpio = of_args.args[2]; + + pctrl->gpio_range.id = 0; + pctrl->gpio_range.pin_base = 0; + pctrl->gpio_range.base = 0; + pctrl->gpio_range.npins = chip->ngpio; + pctrl->gpio_range.name = chip->label; + pctrl->gpio_range.gc = chip; + ret = devm_gpiochip_add_data(pctrl->dev, chip, pctrl); + if (ret) { + dev_err(pctrl->dev, "failed to add GPIO controller\n"); + return ret; + } + + dev_dbg(pctrl->dev, "Registered gpio controller\n"); + + return 0; +} + +static int rzv2m_pinctrl_register(struct rzv2m_pinctrl *pctrl) +{ + struct pinctrl_pin_desc *pins; + unsigned int i, j; + u32 *pin_data; + int ret; + + pctrl->desc.name = DRV_NAME; + pctrl->desc.npins = pctrl->data->n_port_pins + pctrl->data->n_dedicated_pins; + pctrl->desc.pctlops = &rzv2m_pinctrl_pctlops; + pctrl->desc.pmxops = &rzv2m_pinctrl_pmxops; + pctrl->desc.confops = &rzv2m_pinctrl_confops; + pctrl->desc.owner = THIS_MODULE; + + pins = devm_kcalloc(pctrl->dev, pctrl->desc.npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + pin_data = devm_kcalloc(pctrl->dev, pctrl->desc.npins, + sizeof(*pin_data), GFP_KERNEL); + if (!pin_data) + return -ENOMEM; + + pctrl->pins = pins; + pctrl->desc.pins = pins; + + for (i = 0, j = 0; i < pctrl->data->n_port_pins; i++) { + pins[i].number = i; + pins[i].name = pctrl->data->port_pins[i]; + if (i && !(i % RZV2M_PINS_PER_PORT)) + j++; + pin_data[i] = pctrl->data->port_pin_configs[j]; + pins[i].drv_data = &pin_data[i]; + } + + for (i = 0; i < pctrl->data->n_dedicated_pins; i++) { + unsigned int index = pctrl->data->n_port_pins + i; + + pins[index].number = index; + pins[index].name = pctrl->data->dedicated_pins[i].name; + pin_data[index] = pctrl->data->dedicated_pins[i].config; + pins[index].drv_data = &pin_data[index]; + } + + ret = devm_pinctrl_register_and_init(pctrl->dev, &pctrl->desc, pctrl, + &pctrl->pctl); + if (ret) { + dev_err(pctrl->dev, "pinctrl registration failed\n"); + return ret; + } + + ret = pinctrl_enable(pctrl->pctl); + if (ret) { + dev_err(pctrl->dev, "pinctrl enable failed\n"); + return ret; + } + + ret = rzv2m_gpio_register(pctrl); + if (ret) { + dev_err(pctrl->dev, "failed to add GPIO chip: %i\n", ret); + return ret; + } + + return 0; +} + +static void rzv2m_pinctrl_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + +static int rzv2m_pinctrl_probe(struct platform_device *pdev) +{ + struct rzv2m_pinctrl *pctrl; + int ret; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->dev = &pdev->dev; + + pctrl->data = of_device_get_match_data(&pdev->dev); + if (!pctrl->data) + return -EINVAL; + + pctrl->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pctrl->base)) + return PTR_ERR(pctrl->base); + + pctrl->clk = devm_clk_get(pctrl->dev, NULL); + if (IS_ERR(pctrl->clk)) { + ret = PTR_ERR(pctrl->clk); + dev_err(pctrl->dev, "failed to get GPIO clk : %i\n", ret); + return ret; + } + + spin_lock_init(&pctrl->lock); + + platform_set_drvdata(pdev, pctrl); + + ret = clk_prepare_enable(pctrl->clk); + if (ret) { + dev_err(pctrl->dev, "failed to enable GPIO clk: %i\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(&pdev->dev, rzv2m_pinctrl_clk_disable, + pctrl->clk); + if (ret) { + dev_err(pctrl->dev, + "failed to register GPIO clk disable action, %i\n", + ret); + return ret; + } + + ret = rzv2m_pinctrl_register(pctrl); + if (ret) + return ret; + + dev_info(pctrl->dev, "%s support registered\n", DRV_NAME); + return 0; +} + +static struct rzv2m_pinctrl_data r9a09g011_data = { + .port_pins = rzv2m_gpio_names, + .port_pin_configs = rzv2m_gpio_configs, + .dedicated_pins = rzv2m_dedicated_pins, + .n_port_pins = ARRAY_SIZE(rzv2m_gpio_configs) * RZV2M_PINS_PER_PORT, + .n_dedicated_pins = ARRAY_SIZE(rzv2m_dedicated_pins), +}; + +static const struct of_device_id rzv2m_pinctrl_of_table[] = { + { + .compatible = "renesas,r9a09g011-pinctrl", + .data = &r9a09g011_data, + }, + { /* sentinel */ } +}; + +static struct platform_driver rzv2m_pinctrl_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(rzv2m_pinctrl_of_table), + }, + .probe = rzv2m_pinctrl_probe, +}; + +static int __init rzv2m_pinctrl_init(void) +{ + return platform_driver_register(&rzv2m_pinctrl_driver); +} +core_initcall(rzv2m_pinctrl_init); + +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); +MODULE_DESCRIPTION("Pin and gpio controller driver for RZ/V2M"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/renesas/sh_pfc.h b/drivers/pinctrl/renesas/sh_pfc.h index 12bc279f5733..0fcb29ab0c84 100644 --- a/drivers/pinctrl/renesas/sh_pfc.h +++ b/drivers/pinctrl/renesas/sh_pfc.h @@ -325,6 +325,7 @@ extern const struct sh_pfc_soc_info r8a77990_pinmux_info; extern const struct sh_pfc_soc_info r8a77995_pinmux_info; extern const struct sh_pfc_soc_info r8a779a0_pinmux_info; extern const struct sh_pfc_soc_info r8a779f0_pinmux_info; +extern const struct sh_pfc_soc_info r8a779g0_pinmux_info; extern const struct sh_pfc_soc_info sh7203_pinmux_info; extern const struct sh_pfc_soc_info sh7264_pinmux_info; extern const struct sh_pfc_soc_info sh7269_pinmux_info; @@ -492,9 +493,13 @@ extern const struct sh_pfc_soc_info shx3_pinmux_info; PORT_GP_CFG_1(bank, 11, fn, sfx, cfg) #define PORT_GP_12(bank, fn, sfx) PORT_GP_CFG_12(bank, fn, sfx, 0) -#define PORT_GP_CFG_14(bank, fn, sfx, cfg) \ +#define PORT_GP_CFG_13(bank, fn, sfx, cfg) \ PORT_GP_CFG_12(bank, fn, sfx, cfg), \ - PORT_GP_CFG_1(bank, 12, fn, sfx, cfg), \ + PORT_GP_CFG_1(bank, 12, fn, sfx, cfg) +#define PORT_GP_13(bank, fn, sfx) PORT_GP_CFG_13(bank, fn, sfx, 0) + +#define PORT_GP_CFG_14(bank, fn, sfx, cfg) \ + PORT_GP_CFG_13(bank, fn, sfx, cfg), \ PORT_GP_CFG_1(bank, 13, fn, sfx, cfg) #define PORT_GP_14(bank, fn, sfx) PORT_GP_CFG_14(bank, fn, sfx, 0) diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index 6d7ca1758292..a8212fc126bf 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -27,8 +27,6 @@ #include <linux/soc/samsung/exynos-pmu.h> #include <linux/soc/samsung/exynos-regs-pmu.h> -#include <dt-bindings/pinctrl/samsung.h> - #include "pinctrl-samsung.h" #include "pinctrl-exynos.h" @@ -173,7 +171,7 @@ static int exynos_irq_request_resources(struct irq_data *irqd) con = readl(bank->pctl_base + reg_con); con &= ~(mask << shift); - con |= EXYNOS_PIN_FUNC_EINT << shift; + con |= EXYNOS_PIN_CON_FUNC_EINT << shift; writel(con, bank->pctl_base + reg_con); raw_spin_unlock_irqrestore(&bank->slock, flags); @@ -196,7 +194,7 @@ static void exynos_irq_release_resources(struct irq_data *irqd) con = readl(bank->pctl_base + reg_con); con &= ~(mask << shift); - con |= EXYNOS_PIN_FUNC_INPUT << shift; + con |= PIN_CON_FUNC_INPUT << shift; writel(con, bank->pctl_base + reg_con); raw_spin_unlock_irqrestore(&bank->slock, flags); diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.h b/drivers/pinctrl/samsung/pinctrl-exynos.h index bfad1ced8017..7bd6d82c9f36 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.h +++ b/drivers/pinctrl/samsung/pinctrl-exynos.h @@ -16,6 +16,9 @@ #ifndef __PINCTRL_SAMSUNG_EXYNOS_H #define __PINCTRL_SAMSUNG_EXYNOS_H +/* Values for the pin CON register */ +#define EXYNOS_PIN_CON_FUNC_EINT 0xf + /* External GPIO and wakeup interrupt related definitions */ #define EXYNOS_GPIO_ECON_OFFSET 0x700 #define EXYNOS_GPIO_EFLTCON_OFFSET 0x800 diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c index 26d309d2516d..4837bceb767b 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c @@ -26,8 +26,6 @@ #include <linux/of_device.h> #include <linux/spinlock.h> -#include <dt-bindings/pinctrl/samsung.h> - #include "../core.h" #include "pinctrl-samsung.h" @@ -614,7 +612,7 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc, data = readl(reg); data &= ~(mask << shift); if (!input) - data |= EXYNOS_PIN_FUNC_OUTPUT << shift; + data |= PIN_CON_FUNC_OUTPUT << shift; writel(data, reg); return 0; diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h index fc6f5199c548..9af93e3d8d9f 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.h +++ b/drivers/pinctrl/samsung/pinctrl-samsung.h @@ -53,6 +53,14 @@ enum pincfg_type { #define PINCFG_UNPACK_TYPE(cfg) ((cfg) & PINCFG_TYPE_MASK) #define PINCFG_UNPACK_VALUE(cfg) (((cfg) & PINCFG_VALUE_MASK) >> \ PINCFG_VALUE_SHIFT) +/* + * Values for the pin CON register, choosing pin function. + * The basic set (input and output) are same between: S3C24xx, S3C64xx, S5PV210, + * Exynos ARMv7, Exynos ARMv8, Tesla FSD. + */ +#define PIN_CON_FUNC_INPUT 0x0 +#define PIN_CON_FUNC_OUTPUT 0x1 + /** * enum eint_type - possible external interrupt types. * @EINT_TYPE_NONE: bank does not support external interrupts diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index 33751a6a0757..a78fdbbdfc0c 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -29,7 +29,6 @@ config PINCTRL_SUN6I_A31 config PINCTRL_SUN6I_A31_R bool "Support for the Allwinner A31 R-PIO" default MACH_SUN6I - depends on RESET_CONTROLLER select PINCTRL_SUNXI config PINCTRL_SUN8I_A23 @@ -55,7 +54,6 @@ config PINCTRL_SUN8I_A83T_R config PINCTRL_SUN8I_A23_R bool "Support for the Allwinner A23 and A33 R-PIO" default MACH_SUN8I - depends on RESET_CONTROLLER select PINCTRL_SUNXI config PINCTRL_SUN8I_H3 @@ -81,7 +79,11 @@ config PINCTRL_SUN9I_A80 config PINCTRL_SUN9I_A80_R bool "Support for the Allwinner A80 R-PIO" default MACH_SUN9I - depends on RESET_CONTROLLER + select PINCTRL_SUNXI + +config PINCTRL_SUN20I_D1 + bool "Support for the Allwinner D1 PIO" + default MACH_SUN8I || (RISCV && ARCH_SUNXI) select PINCTRL_SUNXI config PINCTRL_SUN50I_A64 diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile index d3440c42b9d6..2ff5a55927ad 100644 --- a/drivers/pinctrl/sunxi/Makefile +++ b/drivers/pinctrl/sunxi/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PINCTRL_SUN8I_A83T_R) += pinctrl-sun8i-a83t-r.o obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o obj-$(CONFIG_PINCTRL_SUN8I_H3_R) += pinctrl-sun8i-h3-r.o obj-$(CONFIG_PINCTRL_SUN8I_V3S) += pinctrl-sun8i-v3s.o +obj-$(CONFIG_PINCTRL_SUN20I_D1) += pinctrl-sun20i-d1.o obj-$(CONFIG_PINCTRL_SUN50I_H5) += pinctrl-sun50i-h5.o obj-$(CONFIG_PINCTRL_SUN50I_H6) += pinctrl-sun50i-h6.o obj-$(CONFIG_PINCTRL_SUN50I_H6_R) += pinctrl-sun50i-h6-r.o diff --git a/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c b/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c new file mode 100644 index 000000000000..40858b881298 --- /dev/null +++ b/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c @@ -0,0 +1,840 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner D1 SoC pinctrl driver. + * + * Copyright (c) 2020 wuyan@allwinnertech.com + * Copyright (c) 2021-2022 Samuel Holland <samuel@sholland.org> + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-sunxi.h" + +static const struct sunxi_desc_pin d1_pins[] = { + /* PB */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm3"), + SUNXI_FUNCTION(0x3, "ir"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* WP */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x7, "uart2"), /* TX */ + SUNXI_FUNCTION(0x8, "spdif"), /* OUT */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm4"), + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT3 */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN3 */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x7, "uart2"), /* RX */ + SUNXI_FUNCTION(0x8, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT2 */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN2 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x7, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN0 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x7, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x7, "uart5"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* BCLK */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x5, "pwm0"), + SUNXI_FUNCTION(0x6, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x7, "uart5"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* LRCK */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x5, "pwm1"), + SUNXI_FUNCTION(0x6, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x7, "uart3"), /* TX */ + SUNXI_FUNCTION(0x8, "bist0"), /* BIST_RESULT0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* MCLK */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x7, "uart3"), /* RX */ + SUNXI_FUNCTION(0x8, "bist1"), /* BIST_RESULT1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x3, "pwm5"), + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* HOLD */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x7, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x3, "pwm6"), + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x7, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x3, "pwm7"), + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x7, "uart1"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA0 */ + SUNXI_FUNCTION(0x3, "pwm2"), + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x7, "uart1"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* CLK */ + SUNXI_FUNCTION(0x3, "pwm0"), + SUNXI_FUNCTION(0x4, "spdif"), /* IN */ + SUNXI_FUNCTION(0x5, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x7, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 12)), + /* PC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "ledc"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CLK */ + SUNXI_FUNCTION(0x3, "mmc2"), /* CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */ + SUNXI_FUNCTION(0x3, "mmc2"), /* CMD */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D2 */ + SUNXI_FUNCTION(0x4, "boot"), /* SEL0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MISO */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D1 */ + SUNXI_FUNCTION(0x4, "boot"), /* SEL1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* WP */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D0 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x5, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x6, "pll"), /* DBG-CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* HOLD */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x5, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 7)), + /* PD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V0P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D0P */ + SUNXI_FUNCTION(0x5, "i2c0"), /* SCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V0N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D0N */ + SUNXI_FUNCTION(0x5, "uart2"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V1P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D1P */ + SUNXI_FUNCTION(0x5, "uart2"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V1N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D1N */ + SUNXI_FUNCTION(0x5, "uart2"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V2P */ + SUNXI_FUNCTION(0x4, "dsi"), /* CKP */ + SUNXI_FUNCTION(0x5, "uart2"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V2N */ + SUNXI_FUNCTION(0x4, "dsi"), /* CKN */ + SUNXI_FUNCTION(0x5, "uart5"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* CKP */ + SUNXI_FUNCTION(0x4, "dsi"), /* D2P */ + SUNXI_FUNCTION(0x5, "uart5"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* CKN */ + SUNXI_FUNCTION(0x4, "dsi"), /* D2N */ + SUNXI_FUNCTION(0x5, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V3P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D3P */ + SUNXI_FUNCTION(0x5, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V3N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D3N */ + SUNXI_FUNCTION(0x5, "pwm6"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V0P */ + SUNXI_FUNCTION(0x4, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V0N */ + SUNXI_FUNCTION(0x4, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V1P */ + SUNXI_FUNCTION(0x4, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x5, "i2c0"), /* SDA */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V1N */ + SUNXI_FUNCTION(0x4, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x5, "uart3"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V2P */ + SUNXI_FUNCTION(0x4, "spi1"), /* HOLD */ + SUNXI_FUNCTION(0x5, "uart3"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V2N */ + SUNXI_FUNCTION(0x4, "spi1"), /* WP */ + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* CKP */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x5, "pwm0"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* CKN */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x5, "pwm1"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 17)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V3P */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x5, "pwm2"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 18)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V3N */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA0 */ + SUNXI_FUNCTION(0x5, "pwm3"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 19)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "dmic"), /* CLK */ + SUNXI_FUNCTION(0x5, "pwm4"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 20)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "uart1"), /* TX */ + SUNXI_FUNCTION(0x5, "pwm5"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 21)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x3, "ir"), /* RX */ + SUNXI_FUNCTION(0x4, "uart1"), /* RX */ + SUNXI_FUNCTION(0x5, "pwm7"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 22)), + /* PE */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "uart2"), /* RTS */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x5, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x8, "emac"), /* RXCTL/CRS_DV */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "uart2"), /* CTS */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x5, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* PCLK */ + SUNXI_FUNCTION(0x3, "uart2"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* MCLK */ + SUNXI_FUNCTION(0x3, "uart2"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x8, "emac"), /* TXCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D0 */ + SUNXI_FUNCTION(0x3, "uart4"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* MS */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* MS */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D1 */ + SUNXI_FUNCTION(0x3, "uart4"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "ledc"), + SUNXI_FUNCTION(0x6, "d_jtag"), /* DI */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* DI */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D2 */ + SUNXI_FUNCTION(0x3, "uart5"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x5, "spdif"), /* IN */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* DO */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* DO */ + SUNXI_FUNCTION(0x8, "emac"), /* TXCTL/TXEN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart5"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x5, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* CK */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* CK */ + SUNXI_FUNCTION(0x8, "emac"), /* CK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D4 */ + SUNXI_FUNCTION(0x3, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x4, "pwm2"), + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION(0x6, "jtag"), /* MS */ + SUNXI_FUNCTION(0x8, "emac"), /* MDC */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D5 */ + SUNXI_FUNCTION(0x3, "uart1"), /* CTS */ + SUNXI_FUNCTION(0x4, "pwm3"), + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION(0x6, "jtag"), /* DI */ + SUNXI_FUNCTION(0x8, "emac"), /* MDIO */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D6 */ + SUNXI_FUNCTION(0x3, "uart1"), /* TX */ + SUNXI_FUNCTION(0x4, "pwm4"), + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION(0x6, "jtag"), /* DO */ + SUNXI_FUNCTION(0x8, "emac"), /* EPHY-25M */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D7 */ + SUNXI_FUNCTION(0x3, "uart1"), /* RX */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT3 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN3 */ + SUNXI_FUNCTION(0x6, "jtag"), /* CK */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD2 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x3, "ncsi0"), /* FIELD */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT2 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN2 */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD3 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x3, "pwm5"), + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD2 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* MS */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN0 */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD3 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* DI */ + SUNXI_FUNCTION(0x4, "pwm6"), + SUNXI_FUNCTION(0x5, "i2s0"), /* LRCK */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* DO */ + SUNXI_FUNCTION(0x4, "pwm7"), + SUNXI_FUNCTION(0x5, "i2s0"), /* BCLK */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* CK */ + SUNXI_FUNCTION(0x4, "ir"), /* TX */ + SUNXI_FUNCTION(0x5, "i2s0"), /* MCLK */ + SUNXI_FUNCTION(0x6, "dmic"), /* CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 17)), + /* PF */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ + SUNXI_FUNCTION(0x3, "jtag"), /* MS */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* MS */ + SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ + SUNXI_FUNCTION(0x3, "jtag"), /* DI */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* DI */ + SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart0"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "ledc"), + SUNXI_FUNCTION(0x6, "spdif"), /* IN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ + SUNXI_FUNCTION(0x3, "jtag"), /* DO */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* DO */ + SUNXI_FUNCTION(0x5, "i2s2"), /* BCLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart0"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "pwm6"), + SUNXI_FUNCTION(0x6, "ir"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ + SUNXI_FUNCTION(0x3, "jtag"), /* CK */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* CK */ + SUNXI_FUNCTION(0x5, "i2s2"), /* LRCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x4, "ir"), /* RX */ + SUNXI_FUNCTION(0x5, "i2s2"), /* MCLK */ + SUNXI_FUNCTION(0x6, "pwm5"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 6)), + /* PG */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart3"), /* TX */ + SUNXI_FUNCTION(0x4, "emac"), /* RXCTRL/CRS_DV */ + SUNXI_FUNCTION(0x5, "pwm7"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x3, "uart3"), /* RX */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD0 */ + SUNXI_FUNCTION(0x5, "pwm6"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */ + SUNXI_FUNCTION(0x3, "uart3"), /* RTS */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD1 */ + SUNXI_FUNCTION(0x5, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */ + SUNXI_FUNCTION(0x3, "uart3"), /* CTS */ + SUNXI_FUNCTION(0x4, "emac"), /* TXCK */ + SUNXI_FUNCTION(0x5, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x3, "uart5"), /* TX */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD0 */ + SUNXI_FUNCTION(0x5, "pwm5"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart5"), /* RX */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD1 */ + SUNXI_FUNCTION(0x5, "pwm4"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD2 */ + SUNXI_FUNCTION(0x5, "pwm1"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD3 */ + SUNXI_FUNCTION(0x5, "spdif"), /* IN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x3, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD2 */ + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* CTS */ + SUNXI_FUNCTION(0x3, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD3 */ + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm3"), + SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* RXCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* MCLK */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* EPHY-25M */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* LRCK */ + SUNXI_FUNCTION(0x3, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* TXCTL/TXEN */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "pwm0"), + SUNXI_FUNCTION(0x7, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* BCLK */ + SUNXI_FUNCTION(0x3, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* CLKIN/RXER */ + SUNXI_FUNCTION(0x5, "pwm2"), + SUNXI_FUNCTION(0x6, "ledc"), + SUNXI_FUNCTION(0x7, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1_din"), /* DIN0 */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* MDC */ + SUNXI_FUNCTION(0x5, "i2s1_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x6, "spi0"), /* WP */ + SUNXI_FUNCTION(0x7, "uart1"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* MDIO */ + SUNXI_FUNCTION(0x5, "i2s1_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "spi0"), /* HOLD */ + SUNXI_FUNCTION(0x7, "uart1"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir"), /* RX */ + SUNXI_FUNCTION(0x3, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION(0x4, "pwm5"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "spdif"), /* IN */ + SUNXI_FUNCTION(0x7, "ledc"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x4, "pwm7"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "ir"), /* TX */ + SUNXI_FUNCTION(0x7, "uart0"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 17)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 18), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x4, "pwm6"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x7, "uart0"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 18)), +}; + +static const unsigned int d1_irq_bank_map[] = { 1, 2, 3, 4, 5, 6 }; + +static const struct sunxi_pinctrl_desc d1_pinctrl_data = { + .pins = d1_pins, + .npins = ARRAY_SIZE(d1_pins), + .irq_banks = ARRAY_SIZE(d1_irq_bank_map), + .irq_bank_map = d1_irq_bank_map, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, +}; + +static int d1_pinctrl_probe(struct platform_device *pdev) +{ + unsigned long variant = (unsigned long)of_device_get_match_data(&pdev->dev); + + return sunxi_pinctrl_init_with_variant(pdev, &d1_pinctrl_data, variant); +} + +static const struct of_device_id d1_pinctrl_match[] = { + { + .compatible = "allwinner,sun20i-d1-pinctrl", + .data = (void *)PINCTRL_SUN20I_D1 + }, + {} +}; + +static struct platform_driver d1_pinctrl_driver = { + .probe = d1_pinctrl_probe, + .driver = { + .name = "sun20i-d1-pinctrl", + .of_match_table = d1_pinctrl_match, + }, +}; +builtin_platform_driver(d1_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c index 21054fcacd34..afc1f5df7545 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c @@ -82,6 +82,7 @@ static const struct sunxi_pinctrl_desc a100_r_pinctrl_data = { .npins = ARRAY_SIZE(a100_r_pins), .pin_base = PL_BASE, .irq_banks = 1, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, }; static int a100_r_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c index e69f6da40dc0..f682e0e4244d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c @@ -684,7 +684,7 @@ static const struct sunxi_pinctrl_desc a100_pinctrl_data = { .npins = ARRAY_SIZE(a100_pins), .irq_banks = 7, .irq_bank_map = a100_irq_bank_map, - .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, }; static int a100_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c index e69c8dae121a..ef261eccda56 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c @@ -24,7 +24,6 @@ #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/platform_device.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h6-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h6-r.c index c7d90c44e87a..3aba0aec3d78 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-h6-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h6-r.c @@ -16,7 +16,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" @@ -107,6 +106,7 @@ static const struct sunxi_pinctrl_desc sun50i_h6_r_pinctrl_data = { .npins = ARRAY_SIZE(sun50i_h6_r_pins), .pin_base = PL_BASE, .irq_banks = 2, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, }; static int sun50i_h6_r_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c index 8e4f10ab96ce..c39ea46046c2 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c @@ -12,7 +12,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c index 152b71226a80..d6ca720ee8d8 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c @@ -525,7 +525,7 @@ static const struct sunxi_pinctrl_desc h616_pinctrl_data = { .irq_banks = ARRAY_SIZE(h616_irq_bank_map), .irq_bank_map = h616_irq_bank_map, .irq_read_needs_mux = true, - .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, }; static int h616_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c index a00246d3dd49..2486cdf345e1 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c @@ -17,7 +17,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" @@ -111,26 +110,7 @@ static const struct sunxi_pinctrl_desc sun6i_a31_r_pinctrl_data = { static int sun6i_a31_r_pinctrl_probe(struct platform_device *pdev) { - struct reset_control *rstc; - int ret; - - rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(rstc)) { - dev_err(&pdev->dev, "Reset controller missing\n"); - return PTR_ERR(rstc); - } - - ret = reset_control_deassert(rstc); - if (ret) - return ret; - - ret = sunxi_pinctrl_init(pdev, - &sun6i_a31_r_pinctrl_data); - - if (ret) - reset_control_assert(rstc); - - return ret; + return sunxi_pinctrl_init(pdev, &sun6i_a31_r_pinctrl_data); } static const struct of_device_id sun6i_a31_r_pinctrl_match[] = { diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c index 9e5b61449999..4fae12c905b7 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c @@ -20,7 +20,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" @@ -98,29 +97,7 @@ static const struct sunxi_pinctrl_desc sun8i_a23_r_pinctrl_data = { static int sun8i_a23_r_pinctrl_probe(struct platform_device *pdev) { - struct reset_control *rstc; - int ret; - - rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - if (ret == -EPROBE_DEFER) - return ret; - dev_err(&pdev->dev, "Reset controller missing err=%d\n", ret); - return ret; - } - - ret = reset_control_deassert(rstc); - if (ret) - return ret; - - ret = sunxi_pinctrl_init(pdev, - &sun8i_a23_r_pinctrl_data); - - if (ret) - reset_control_assert(rstc); - - return ret; + return sunxi_pinctrl_init(pdev, &sun8i_a23_r_pinctrl_data); } static const struct of_device_id sun8i_a23_r_pinctrl_match[] = { diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t-r.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t-r.c index 6531cf67958e..0cb6c1a970c9 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t-r.c @@ -27,7 +27,6 @@ #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/platform_device.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c index a191a65217ac..f11cb5bba0f7 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c @@ -14,7 +14,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/reset.h> #include "pinctrl-sunxi.h" diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index dd928402af99..6c04027d0dd9 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -46,6 +46,67 @@ static struct lock_class_key sunxi_pinctrl_irq_request_class; static struct irq_chip sunxi_pinctrl_edge_irq_chip; static struct irq_chip sunxi_pinctrl_level_irq_chip; +/* + * The sunXi PIO registers are organized as a series of banks, with registers + * for each bank in the following order: + * - Mux config + * - Data value + * - Drive level + * - Pull direction + * + * Multiple consecutive registers are used for fields wider than one bit. + * + * The following functions calculate the register and the bit offset to access. + * They take a pin number which is relative to the start of the current device. + */ +static void sunxi_mux_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 bank = pin / PINS_PER_BANK; + u32 offset = pin % PINS_PER_BANK * MUX_FIELD_WIDTH; + + *reg = bank * pctl->bank_mem_size + MUX_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(MUX_FIELD_WIDTH) - 1) << *shift; +} + +static void sunxi_data_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 bank = pin / PINS_PER_BANK; + u32 offset = pin % PINS_PER_BANK * DATA_FIELD_WIDTH; + + *reg = bank * pctl->bank_mem_size + DATA_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(DATA_FIELD_WIDTH) - 1) << *shift; +} + +static void sunxi_dlevel_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 bank = pin / PINS_PER_BANK; + u32 offset = pin % PINS_PER_BANK * pctl->dlevel_field_width; + + *reg = bank * pctl->bank_mem_size + DLEVEL_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(pctl->dlevel_field_width) - 1) << *shift; +} + +static void sunxi_pull_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 bank = pin / PINS_PER_BANK; + u32 offset = pin % PINS_PER_BANK * PULL_FIELD_WIDTH; + + *reg = bank * pctl->bank_mem_size + pctl->pull_regs_offset + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(PULL_FIELD_WIDTH) - 1) << *shift; +} + static struct sunxi_pinctrl_group * sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) { @@ -451,22 +512,19 @@ static const struct pinctrl_ops sunxi_pctrl_ops = { .get_group_pins = sunxi_pctrl_get_group_pins, }; -static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param, - u32 *offset, u32 *shift, u32 *mask) +static int sunxi_pconf_reg(const struct sunxi_pinctrl *pctl, + u32 pin, enum pin_config_param param, + u32 *reg, u32 *shift, u32 *mask) { switch (param) { case PIN_CONFIG_DRIVE_STRENGTH: - *offset = sunxi_dlevel_reg(pin); - *shift = sunxi_dlevel_offset(pin); - *mask = DLEVEL_PINS_MASK; + sunxi_dlevel_reg(pctl, pin, reg, shift, mask); break; case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_DISABLE: - *offset = sunxi_pull_reg(pin); - *shift = sunxi_pull_offset(pin); - *mask = PULL_PINS_MASK; + sunxi_pull_reg(pctl, pin, reg, shift, mask); break; default: @@ -481,17 +539,17 @@ static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param = pinconf_to_config_param(*config); - u32 offset, shift, mask, val; + u32 reg, shift, mask, val; u16 arg; int ret; pin -= pctl->desc->pin_base; - ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + ret = sunxi_pconf_reg(pctl, pin, param, ®, &shift, &mask); if (ret < 0) return ret; - val = (readl(pctl->membase + offset) >> shift) & mask; + val = (readl(pctl->membase + reg) & mask) >> shift; switch (pinconf_to_config_param(*config)) { case PIN_CONFIG_DRIVE_STRENGTH: @@ -547,16 +605,15 @@ static int sunxi_pconf_set(struct pinctrl_dev *pctldev, unsigned pin, pin -= pctl->desc->pin_base; for (i = 0; i < num_configs; i++) { + u32 arg, reg, shift, mask, val; enum pin_config_param param; unsigned long flags; - u32 offset, shift, mask, reg; - u32 arg, val; int ret; param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); - ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + ret = sunxi_pconf_reg(pctl, pin, param, ®, &shift, &mask); if (ret < 0) return ret; @@ -593,9 +650,8 @@ static int sunxi_pconf_set(struct pinctrl_dev *pctldev, unsigned pin, } raw_spin_lock_irqsave(&pctl->lock, flags); - reg = readl(pctl->membase + offset); - reg &= ~(mask << shift); - writel(reg | val << shift, pctl->membase + offset); + writel((readl(pctl->membase + reg) & ~mask) | val << shift, + pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); } /* for each config */ @@ -624,7 +680,7 @@ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, unsigned pin, struct regulator *supply) { - unsigned short bank = pin / PINS_PER_BANK; + unsigned short bank; unsigned long flags; u32 val, reg; int uV; @@ -640,6 +696,9 @@ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, if (uV == 0) return 0; + pin -= pctl->desc->pin_base; + bank = pin / PINS_PER_BANK; + switch (pctl->desc->io_bias_cfg_variant) { case BIAS_VOLTAGE_GRP_CONFIG: /* @@ -657,12 +716,20 @@ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, else val = 0xD; /* 3.3V */ - pin -= pctl->desc->pin_base; - reg = readl(pctl->membase + sunxi_grp_config_reg(pin)); reg &= ~IO_BIAS_MASK; writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin)); return 0; + case BIAS_VOLTAGE_PIO_POW_MODE_CTL: + val = uV > 1800000 && uV <= 2500000 ? BIT(bank) : 0; + + raw_spin_lock_irqsave(&pctl->lock, flags); + reg = readl(pctl->membase + PIO_POW_MOD_CTL_REG); + reg &= ~BIT(bank); + writel(reg | val, pctl->membase + PIO_POW_MOD_CTL_REG); + raw_spin_unlock_irqrestore(&pctl->lock, flags); + + fallthrough; case BIAS_VOLTAGE_PIO_POW_MODE_SEL: val = uV <= 1800000 ? 1 : 0; @@ -710,16 +777,16 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev, u8 config) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + u32 reg, shift, mask; unsigned long flags; - u32 val, mask; + + pin -= pctl->desc->pin_base; + sunxi_mux_reg(pctl, pin, ®, &shift, &mask); raw_spin_lock_irqsave(&pctl->lock, flags); - pin -= pctl->desc->pin_base; - val = readl(pctl->membase + sunxi_mux_reg(pin)); - mask = MUX_PINS_MASK << sunxi_mux_offset(pin); - writel((val & ~mask) | config << sunxi_mux_offset(pin), - pctl->membase + sunxi_mux_reg(pin)); + writel((readl(pctl->membase + reg) & ~mask) | config << shift, + pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); } @@ -852,43 +919,43 @@ static int sunxi_pinctrl_gpio_direction_input(struct gpio_chip *chip, static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) { struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); - u32 reg = sunxi_data_reg(offset); - u8 index = sunxi_data_offset(offset); bool set_mux = pctl->desc->irq_read_needs_mux && gpiochip_line_is_irq(chip, offset); u32 pin = offset + chip->base; - u32 val; + u32 reg, shift, mask, val; + + sunxi_data_reg(pctl, offset, ®, &shift, &mask); if (set_mux) sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_INPUT); - val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK; + val = (readl(pctl->membase + reg) & mask) >> shift; if (set_mux) sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_IRQ); - return !!val; + return val; } static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); - u32 reg = sunxi_data_reg(offset); - u8 index = sunxi_data_offset(offset); + u32 reg, shift, mask, val; unsigned long flags; - u32 regval; + + sunxi_data_reg(pctl, offset, ®, &shift, &mask); raw_spin_lock_irqsave(&pctl->lock, flags); - regval = readl(pctl->membase + reg); + val = readl(pctl->membase + reg); if (value) - regval |= BIT(index); + val |= mask; else - regval &= ~(BIT(index)); + val &= ~mask; - writel(regval, pctl->membase + reg); + writel(val, pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); } @@ -1232,11 +1299,11 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) /* * Find an upper bound for the maximum number of functions: in - * the worst case we have gpio_in, gpio_out, irq and up to four + * the worst case we have gpio_in, gpio_out, irq and up to seven * special functions per pin, plus one entry for the sentinel. * We'll reallocate that later anyway. */ - pctl->functions = kcalloc(4 * pctl->ngroups + 4, + pctl->functions = kcalloc(7 * pctl->ngroups + 4, sizeof(*pctl->functions), GFP_KERNEL); if (!pctl->functions) @@ -1429,6 +1496,15 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, pctl->dev = &pdev->dev; pctl->desc = desc; pctl->variant = variant; + if (pctl->variant >= PINCTRL_SUN20I_D1) { + pctl->bank_mem_size = D1_BANK_MEM_SIZE; + pctl->pull_regs_offset = D1_PULL_REGS_OFFSET; + pctl->dlevel_field_width = D1_DLEVEL_FIELD_WIDTH; + } else { + pctl->bank_mem_size = BANK_MEM_SIZE; + pctl->pull_regs_offset = PULL_REGS_OFFSET; + pctl->dlevel_field_width = DLEVEL_FIELD_WIDTH; + } pctl->irq_array = devm_kcalloc(&pdev->dev, IRQ_PER_BANK * pctl->desc->irq_banks, diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index a32bb5bcb754..a87a2f944d60 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -36,23 +36,19 @@ #define BANK_MEM_SIZE 0x24 #define MUX_REGS_OFFSET 0x0 +#define MUX_FIELD_WIDTH 4 #define DATA_REGS_OFFSET 0x10 +#define DATA_FIELD_WIDTH 1 #define DLEVEL_REGS_OFFSET 0x14 +#define DLEVEL_FIELD_WIDTH 2 #define PULL_REGS_OFFSET 0x1c +#define PULL_FIELD_WIDTH 2 + +#define D1_BANK_MEM_SIZE 0x30 +#define D1_DLEVEL_FIELD_WIDTH 4 +#define D1_PULL_REGS_OFFSET 0x24 #define PINS_PER_BANK 32 -#define MUX_PINS_PER_REG 8 -#define MUX_PINS_BITS 4 -#define MUX_PINS_MASK 0x0f -#define DATA_PINS_PER_REG 32 -#define DATA_PINS_BITS 1 -#define DATA_PINS_MASK 0x01 -#define DLEVEL_PINS_PER_REG 16 -#define DLEVEL_PINS_BITS 2 -#define DLEVEL_PINS_MASK 0x03 -#define PULL_PINS_PER_REG 16 -#define PULL_PINS_BITS 2 -#define PULL_PINS_MASK 0x03 #define IRQ_PER_BANK 32 @@ -96,8 +92,11 @@ #define PINCTRL_SUN8I_R40 BIT(8) #define PINCTRL_SUN8I_V3 BIT(9) #define PINCTRL_SUN8I_V3S BIT(10) +/* Variants below here have an updated register layout. */ +#define PINCTRL_SUN20I_D1 BIT(11) #define PIO_POW_MOD_SEL_REG 0x340 +#define PIO_POW_MOD_CTL_REG 0x344 enum sunxi_desc_bias_voltage { BIAS_VOLTAGE_NONE, @@ -111,6 +110,12 @@ enum sunxi_desc_bias_voltage { * register, as seen on H6 SoC, for example. */ BIAS_VOLTAGE_PIO_POW_MODE_SEL, + /* + * Bias voltage is set through PIO_POW_MOD_SEL_REG + * and PIO_POW_MOD_CTL_REG register, as seen on + * A100 and D1 SoC, for example. + */ + BIAS_VOLTAGE_PIO_POW_MODE_CTL, }; struct sunxi_desc_function { @@ -170,6 +175,9 @@ struct sunxi_pinctrl { raw_spinlock_t lock; struct pinctrl_dev *pctl_dev; unsigned long variant; + u32 bank_mem_size; + u32 pull_regs_offset; + u32 dlevel_field_width; }; #define SUNXI_PIN(_pin, ...) \ @@ -215,83 +223,6 @@ struct sunxi_pinctrl { .irqnum = _irq, \ } -/* - * The sunXi PIO registers are organized as is: - * 0x00 - 0x0c Muxing values. - * 8 pins per register, each pin having a 4bits value - * 0x10 Pin values - * 32 bits per register, each pin corresponding to one bit - * 0x14 - 0x18 Drive level - * 16 pins per register, each pin having a 2bits value - * 0x1c - 0x20 Pull-Up values - * 16 pins per register, each pin having a 2bits value - * - * This is for the first bank. Each bank will have the same layout, - * with an offset being a multiple of 0x24. - * - * The following functions calculate from the pin number the register - * and the bit offset that we should access. - */ -static inline u32 sunxi_mux_reg(u16 pin) -{ - u8 bank = pin / PINS_PER_BANK; - u32 offset = bank * BANK_MEM_SIZE; - offset += MUX_REGS_OFFSET; - offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04; - return round_down(offset, 4); -} - -static inline u32 sunxi_mux_offset(u16 pin) -{ - u32 pin_num = pin % MUX_PINS_PER_REG; - return pin_num * MUX_PINS_BITS; -} - -static inline u32 sunxi_data_reg(u16 pin) -{ - u8 bank = pin / PINS_PER_BANK; - u32 offset = bank * BANK_MEM_SIZE; - offset += DATA_REGS_OFFSET; - offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04; - return round_down(offset, 4); -} - -static inline u32 sunxi_data_offset(u16 pin) -{ - u32 pin_num = pin % DATA_PINS_PER_REG; - return pin_num * DATA_PINS_BITS; -} - -static inline u32 sunxi_dlevel_reg(u16 pin) -{ - u8 bank = pin / PINS_PER_BANK; - u32 offset = bank * BANK_MEM_SIZE; - offset += DLEVEL_REGS_OFFSET; - offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04; - return round_down(offset, 4); -} - -static inline u32 sunxi_dlevel_offset(u16 pin) -{ - u32 pin_num = pin % DLEVEL_PINS_PER_REG; - return pin_num * DLEVEL_PINS_BITS; -} - -static inline u32 sunxi_pull_reg(u16 pin) -{ - u8 bank = pin / PINS_PER_BANK; - u32 offset = bank * BANK_MEM_SIZE; - offset += PULL_REGS_OFFSET; - offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04; - return round_down(offset, 4); -} - -static inline u32 sunxi_pull_offset(u16 pin) -{ - u32 pin_num = pin % PULL_PINS_PER_REG; - return pin_num * PULL_PINS_BITS; -} - static inline u32 sunxi_irq_hw_bank_num(const struct sunxi_pinctrl_desc *desc, u8 bank) { if (!desc->irq_bank_map) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 4a3352821b1d..38383e7de3c1 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -594,16 +594,17 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, node = of_parse_phandle(np, "memory-region", a); /* Not map vdevbuffer, vdevring region */ - if (!strncmp(node->name, "vdev", strlen("vdev"))) + if (!strncmp(node->name, "vdev", strlen("vdev"))) { + of_node_put(node); continue; + } err = of_address_to_resource(node, 0, &res); + of_node_put(node); if (err) { dev_err(dev, "unable to resolve memory region\n"); return err; } - of_node_put(node); - if (b >= IMX_RPROC_MEM_MAX) break; diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 54781f553f4e..594a9b43b7ae 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -410,10 +410,9 @@ static int keystone_rproc_probe(struct platform_device *pdev) /* enable clock for accessing DSP internal memories */ pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { dev_err(dev, "failed to enable clock, status = %d\n", ret); - pm_runtime_put_noidle(dev); goto disable_rpm; } diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 47b2a40e1b4a..d421a2ccaa1e 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -401,6 +401,14 @@ static int mt8186_scp_before_load(struct mtk_scp *scp) writel(0x0, scp->reg_base + MT8186_SCP_L1_SRAM_PD_P1); writel(0x0, scp->reg_base + MT8186_SCP_L1_SRAM_PD_p2); + /* + * Set I-cache and D-cache size before loading SCP FW. + * SCP SRAM logical address may change when cache size setting differs. + */ + writel(MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB, + scp->reg_base + MT8183_SCP_CACHE_CON); + writel(MT8183_SCP_CACHESIZE_8KB, scp->reg_base + MT8183_SCP_DCACHE_CON); + return 0; } @@ -943,7 +951,19 @@ static const struct mtk_scp_of_data mt8186_of_data = { .scp_da_to_va = mt8183_scp_da_to_va, .host_to_scp_reg = MT8183_HOST_TO_SCP, .host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT, - .ipi_buf_offset = 0x7bdb0, + .ipi_buf_offset = 0x3bdb0, +}; + +static const struct mtk_scp_of_data mt8188_of_data = { + .scp_clk_get = mt8195_scp_clk_get, + .scp_before_load = mt8192_scp_before_load, + .scp_irq_handler = mt8192_scp_irq_handler, + .scp_reset_assert = mt8192_scp_reset_assert, + .scp_reset_deassert = mt8192_scp_reset_deassert, + .scp_stop = mt8192_scp_stop, + .scp_da_to_va = mt8192_scp_da_to_va, + .host_to_scp_reg = MT8192_GIPC_IN_SET, + .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, }; static const struct mtk_scp_of_data mt8192_of_data = { @@ -973,6 +993,7 @@ static const struct mtk_scp_of_data mt8195_of_data = { static const struct of_device_id mtk_scp_of_match[] = { { .compatible = "mediatek,mt8183-scp", .data = &mt8183_of_data }, { .compatible = "mediatek,mt8186-scp", .data = &mt8186_of_data }, + { .compatible = "mediatek,mt8188-scp", .data = &mt8188_of_data }, { .compatible = "mediatek,mt8192-scp", .data = &mt8192_of_data }, { .compatible = "mediatek,mt8195-scp", .data = &mt8195_of_data }, {}, diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 32a588fefbdc..430fab0266ed 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -243,7 +243,7 @@ static inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer) * omap_rproc_ack_timer_irq() - acknowledge a timer irq * @timer: handle to a OMAP rproc timer * - * This function is used to clear the irq associated with a watchdog timer. The + * This function is used to clear the irq associated with a watchdog timer. * The function is called by the OMAP remoteproc upon a watchdog event on the * remote processor to clear the interrupt status of the watchdog timer. */ @@ -303,7 +303,7 @@ static irqreturn_t omap_rproc_watchdog_isr(int irq, void *data) * @configure: boolean flag used to acquire and configure the timer handle * * This function is used primarily to enable the timers associated with - * a remoteproc. The configure flag is provided to allow the driver to + * a remoteproc. The configure flag is provided to allow the driver * to either acquire and start a timer (during device initialization) or * to just start a timer (during a resume operation). * @@ -443,7 +443,7 @@ free_timers: * @configure: boolean flag used to release the timer handle * * This function is used primarily to disable the timers associated with - * a remoteproc. The configure flag is provided to allow the driver to + * a remoteproc. The configure flag is provided to allow the driver * to either stop and release a timer (during device shutdown) or to just * stop a timer (during a suspend operation). * diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 1777a01fa84e..128bf9912f2c 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -897,6 +897,7 @@ static const struct of_device_id pru_rproc_match[] = { { .compatible = "ti,j721e-pru", .data = &k3_pru_data }, { .compatible = "ti,j721e-rtu", .data = &k3_rtu_data }, { .compatible = "ti,j721e-tx-pru", .data = &k3_tx_pru_data }, + { .compatible = "ti,am625-pru", .data = &k3_pru_data }, {}, }; MODULE_DEVICE_TABLE(of, pru_rproc_match); diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 4b91e3c9eafa..020349f8979d 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -50,7 +50,7 @@ struct minidump_region { }; /** - * struct minidump_subsystem_toc: Subsystem's SMEM Table of content + * struct minidump_subsystem - Subsystem's SMEM Table of content * @status : Subsystem toc init status * @enabled : if set to 1, this region would be copied during coredump * @encryption_status: Encryption status for this subsystem @@ -68,7 +68,7 @@ struct minidump_subsystem { }; /** - * struct minidump_global_toc: Global Table of Content + * struct minidump_global_toc - Global Table of Content * @status : Global Minidump init status * @md_revision : Minidump revision * @enabled : Minidump enable status diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c index 5280ec9b5449..497acfb33f8f 100644 --- a/drivers/remoteproc/qcom_q6v5.c +++ b/drivers/remoteproc/qcom_q6v5.c @@ -112,6 +112,7 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) else dev_err(q6v5->dev, "watchdog without message\n"); + q6v5->running = false; rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); return IRQ_HANDLED; @@ -123,6 +124,9 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) size_t len; char *msg; + if (!q6v5->running) + return IRQ_HANDLED; + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); if (!IS_ERR(msg) && len > 0 && msg[0]) dev_err(q6v5->dev, "fatal error received: %s\n", msg); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 2f3b9f54251e..4c9a1b99cd51 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -175,9 +175,8 @@ static int qcom_rproc_pds_enable(struct qcom_adsp *adsp, struct device **pds, for (i = 0; i < pd_count; i++) { dev_pm_genpd_set_performance_state(pds[i], INT_MAX); - ret = pm_runtime_get_sync(pds[i]); + ret = pm_runtime_resume_and_get(pds[i]); if (ret < 0) { - pm_runtime_put_noidle(pds[i]); dev_pm_genpd_set_performance_state(pds[i], 0); goto unroll_pd_votes; } diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index af217de75e4d..fddb63cffee0 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/devcoredump.h> +#include <linux/dma-map-ops.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -932,27 +933,52 @@ static void q6v5proc_halt_axi_port(struct q6v5 *qproc, static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw, const char *fw_name) { - unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_KERNEL_MAPPING; + unsigned long flags = VM_DMA_COHERENT | VM_FLUSH_RESET_PERMS; + struct page **pages; + struct page *page; dma_addr_t phys; void *metadata; int mdata_perm; int xferop_ret; size_t size; - void *ptr; + void *vaddr; + int count; int ret; + int i; metadata = qcom_mdt_read_metadata(fw, &size, fw_name, qproc->dev); if (IS_ERR(metadata)) return PTR_ERR(metadata); - ptr = dma_alloc_attrs(qproc->dev, size, &phys, GFP_KERNEL, dma_attrs); - if (!ptr) { + page = dma_alloc_attrs(qproc->dev, size, &phys, GFP_KERNEL, dma_attrs); + if (!page) { kfree(metadata); dev_err(qproc->dev, "failed to allocate mdt buffer\n"); return -ENOMEM; } - memcpy(ptr, metadata, size); + count = PAGE_ALIGN(size) >> PAGE_SHIFT; + pages = kmalloc_array(count, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + ret = -ENOMEM; + goto free_dma_attrs; + } + + for (i = 0; i < count; i++) + pages[i] = nth_page(page, i); + + vaddr = vmap(pages, count, flags, pgprot_dmacoherent(PAGE_KERNEL)); + kfree(pages); + if (!vaddr) { + dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", &phys, size); + ret = -EBUSY; + goto free_dma_attrs; + } + + memcpy(vaddr, metadata, size); + + vunmap(vaddr); /* Hypervisor mapping to access metadata by modem */ mdata_perm = BIT(QCOM_SCM_VMID_HLOS); @@ -982,7 +1008,7 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw, "mdt buffer not reclaimed system may become unstable\n"); free_dma_attrs: - dma_free_attrs(qproc->dev, size, ptr, phys, dma_attrs); + dma_free_attrs(qproc->dev, size, page, phys, dma_attrs); kfree(metadata); return ret < 0 ? ret : 0; @@ -1102,6 +1128,9 @@ static int q6v5_mba_load(struct q6v5 *qproc) if (ret) goto reclaim_mba; + if (qproc->has_mba_logs) + qcom_pil_info_store("mba", qproc->mba_phys, MBA_LOG_SIZE); + ret = q6v5_rmb_mba_wait(qproc, 0, 5000); if (ret == -ETIMEDOUT) { dev_err(qproc->dev, "MBA boot timed out\n"); @@ -1594,11 +1623,19 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, return ret; } +static unsigned long q6v5_panic(struct rproc *rproc) +{ + struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + + return qcom_q6v5_panic(&qproc->q6v5); +} + static const struct rproc_ops q6v5_ops = { .start = q6v5_start, .stop = q6v5_stop, .parse_fw = qcom_q6v5_register_dump_segments, .load = q6v5_load, + .panic = q6v5_panic, }; static void qcom_msa_handover(struct qcom_q6v5 *q6v5) @@ -2188,6 +2225,11 @@ static const struct rproc_hexagon_res msm8996_mss = { "mnoc_axi", NULL }, + .proxy_pd_names = (char*[]){ + "mx", + "cx", + NULL + }, .need_mem_protection = true, .has_alt_reset = false, .has_mba_logs = false, diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 6ae39c5653b1..6afd0941e552 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -8,6 +8,7 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/firmware.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -29,6 +30,8 @@ #include "qcom_q6v5.h" #include "remoteproc_internal.h" +#define ADSP_DECRYPT_SHUTDOWN_DELAY_MS 100 + struct adsp_data { int crash_reason_smem; const char *firmware_name; @@ -36,6 +39,7 @@ struct adsp_data { unsigned int minidump_id; bool has_aggre2_clk; bool auto_boot; + bool decrypt_shutdown; char **proxy_pd_names; @@ -65,6 +69,7 @@ struct qcom_adsp { unsigned int minidump_id; int crash_reason_smem; bool has_aggre2_clk; + bool decrypt_shutdown; const char *info_name; struct completion start_done; @@ -87,6 +92,9 @@ static void adsp_minidump(struct rproc *rproc) { struct qcom_adsp *adsp = rproc->priv; + if (rproc->dump_conf == RPROC_COREDUMP_DISABLED) + return; + qcom_minidump(rproc, adsp->minidump_id); } @@ -128,6 +136,19 @@ static void adsp_pds_disable(struct qcom_adsp *adsp, struct device **pds, } } +static int adsp_shutdown_poll_decrypt(struct qcom_adsp *adsp) +{ + unsigned int retry_num = 50; + int ret; + + do { + msleep(ADSP_DECRYPT_SHUTDOWN_DELAY_MS); + ret = qcom_scm_pas_shutdown(adsp->pas_id); + } while (ret == -EINVAL && --retry_num); + + return ret; +} + static int adsp_unprepare(struct rproc *rproc) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; @@ -185,13 +206,17 @@ static int adsp_start(struct rproc *rproc) if (ret) goto disable_xo_clk; - ret = regulator_enable(adsp->cx_supply); - if (ret) - goto disable_aggre2_clk; + if (adsp->cx_supply) { + ret = regulator_enable(adsp->cx_supply); + if (ret) + goto disable_aggre2_clk; + } - ret = regulator_enable(adsp->px_supply); - if (ret) - goto disable_cx_supply; + if (adsp->px_supply) { + ret = regulator_enable(adsp->px_supply); + if (ret) + goto disable_cx_supply; + } ret = qcom_scm_pas_auth_and_reset(adsp->pas_id); if (ret) { @@ -212,9 +237,11 @@ static int adsp_start(struct rproc *rproc) return 0; disable_px_supply: - regulator_disable(adsp->px_supply); + if (adsp->px_supply) + regulator_disable(adsp->px_supply); disable_cx_supply: - regulator_disable(adsp->cx_supply); + if (adsp->cx_supply) + regulator_disable(adsp->cx_supply); disable_aggre2_clk: clk_disable_unprepare(adsp->aggre2_clk); disable_xo_clk: @@ -231,8 +258,10 @@ static void qcom_pas_handover(struct qcom_q6v5 *q6v5) { struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); - regulator_disable(adsp->px_supply); - regulator_disable(adsp->cx_supply); + if (adsp->px_supply) + regulator_disable(adsp->px_supply); + if (adsp->cx_supply) + regulator_disable(adsp->cx_supply); clk_disable_unprepare(adsp->aggre2_clk); clk_disable_unprepare(adsp->xo); adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); @@ -249,6 +278,9 @@ static int adsp_stop(struct rproc *rproc) dev_err(adsp->dev, "timed out on wait\n"); ret = qcom_scm_pas_shutdown(adsp->pas_id); + if (ret && adsp->decrypt_shutdown) + ret = adsp_shutdown_poll_decrypt(adsp); + if (ret) dev_err(adsp->dev, "failed to shutdown: %d\n", ret); @@ -268,6 +300,9 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iom if (offset < 0 || offset + len > adsp->mem_size) return NULL; + if (is_iomem) + *is_iomem = true; + return adsp->mem_region + offset; } @@ -326,14 +361,26 @@ static int adsp_init_clock(struct qcom_adsp *adsp) static int adsp_init_regulator(struct qcom_adsp *adsp) { - adsp->cx_supply = devm_regulator_get(adsp->dev, "cx"); - if (IS_ERR(adsp->cx_supply)) - return PTR_ERR(adsp->cx_supply); + adsp->cx_supply = devm_regulator_get_optional(adsp->dev, "cx"); + if (IS_ERR(adsp->cx_supply)) { + if (PTR_ERR(adsp->cx_supply) == -ENODEV) + adsp->cx_supply = NULL; + else + return PTR_ERR(adsp->cx_supply); + } - regulator_set_load(adsp->cx_supply, 100000); + if (adsp->cx_supply) + regulator_set_load(adsp->cx_supply, 100000); - adsp->px_supply = devm_regulator_get(adsp->dev, "px"); - return PTR_ERR_OR_ZERO(adsp->px_supply); + adsp->px_supply = devm_regulator_get_optional(adsp->dev, "px"); + if (IS_ERR(adsp->px_supply)) { + if (PTR_ERR(adsp->px_supply) == -ENODEV) + adsp->px_supply = NULL; + else + return PTR_ERR(adsp->px_supply); + } + + return 0; } static int adsp_pds_attach(struct device *dev, struct device **devs, @@ -459,9 +506,12 @@ static int adsp_probe(struct platform_device *pdev) adsp->pas_id = desc->pas_id; adsp->has_aggre2_clk = desc->has_aggre2_clk; adsp->info_name = desc->sysmon_name; + adsp->decrypt_shutdown = desc->decrypt_shutdown; platform_set_drvdata(pdev, adsp); - device_wakeup_enable(adsp->dev); + ret = device_init_wakeup(adsp->dev, true); + if (ret) + goto free_rproc; ret = adsp_alloc_memory_region(adsp); if (ret) @@ -877,6 +927,25 @@ static const struct adsp_data sdx55_mpss_resource = { .ssctl_id = 0x22, }; +static const struct adsp_data sm8450_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "modem.mdt", + .pas_id = 4, + .minidump_id = 3, + .has_aggre2_clk = false, + .auto_boot = false, + .decrypt_shutdown = true, + .proxy_pd_names = (char*[]){ + "cx", + "mss", + NULL + }, + .load_state = "modem", + .ssr_name = "mpss", + .sysmon_name = "modem", + .ssctl_id = 0x12, +}; + static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,msm8226-adsp-pil", .data = &adsp_resource_init}, { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, @@ -916,7 +985,7 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sm8450-adsp-pas", .data = &sm8350_adsp_resource}, { .compatible = "qcom,sm8450-cdsp-pas", .data = &sm8350_cdsp_resource}, { .compatible = "qcom,sm8450-slpi-pas", .data = &sm8350_slpi_resource}, - { .compatible = "qcom,sm8450-mpss-pas", .data = &mpss_resource_init}, + { .compatible = "qcom,sm8450-mpss-pas", .data = &sm8450_mpss_resource}, { }, }; MODULE_DEVICE_TABLE(of, adsp_of_match); diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 9fca81492863..57dde2a69b9d 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -41,6 +41,7 @@ struct qcom_sysmon { struct completion comp; struct completion ind_comp; struct completion shutdown_comp; + struct completion ssctl_comp; struct mutex lock; bool ssr_ack; @@ -445,6 +446,8 @@ static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc) svc->priv = sysmon; + complete(&sysmon->ssctl_comp); + return 0; } @@ -501,6 +504,7 @@ static int sysmon_start(struct rproc_subdev *subdev) .ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP }; + reinit_completion(&sysmon->ssctl_comp); mutex_lock(&sysmon->state_lock); sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP; blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); @@ -508,10 +512,12 @@ static int sysmon_start(struct rproc_subdev *subdev) mutex_lock(&sysmon_lock); list_for_each_entry(target, &sysmon_list, node) { - if (target == sysmon) + mutex_lock(&target->state_lock); + if (target == sysmon || target->state != SSCTL_SSR_EVENT_AFTER_POWERUP) { + mutex_unlock(&target->state_lock); continue; + } - mutex_lock(&target->state_lock); event.subsys_name = target->name; event.ssr_event = target->state; @@ -545,6 +551,11 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) if (crashed) return; + if (sysmon->ssctl_instance) { + if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2)) + dev_err(sysmon->dev, "timeout waiting for ssctl service\n"); + } + if (sysmon->ssctl_version) sysmon->shutdown_acked = ssctl_request_shutdown(sysmon); else if (sysmon->ept) @@ -631,6 +642,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, init_completion(&sysmon->comp); init_completion(&sysmon->ind_comp); init_completion(&sysmon->shutdown_comp); + init_completion(&sysmon->ssctl_comp); mutex_init(&sysmon->lock); mutex_init(&sysmon->state_lock); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 9a223d394087..68f37296b151 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -467,6 +467,7 @@ static int wcnss_request_irq(struct qcom_wcnss *wcnss, irq_handler_t thread_fn) { int ret; + int irq_number; ret = platform_get_irq_byname(pdev, name); if (ret < 0 && optional) { @@ -477,14 +478,19 @@ static int wcnss_request_irq(struct qcom_wcnss *wcnss, return ret; } + irq_number = ret; + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "wcnss", wcnss); - if (ret) + if (ret) { dev_err(&pdev->dev, "request %s IRQ failed\n", name); + return ret; + } - return ret; + /* Return the IRQ number if the IRQ was successfully acquired */ + return irq_number; } static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 02a04ab34a23..89832399e028 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -59,6 +59,7 @@ static int rproc_release_carveout(struct rproc *rproc, /* Unique indices for remoteproc devices */ static DEFINE_IDA(rproc_dev_index); +static struct workqueue_struct *rproc_recovery_wq; static const char * const rproc_crash_names[] = { [RPROC_MMUFAULT] = "mmufault", @@ -461,6 +462,7 @@ static void rproc_rvdev_release(struct device *dev) struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); of_reserved_mem_device_release(dev); + dma_release_coherent_memory(dev); kfree(rvdev); } @@ -970,7 +972,7 @@ static int rproc_handle_carveout(struct rproc *rproc, return 0; } - /* Register carveout in in list */ + /* Register carveout in list */ carveout = rproc_mem_entry_init(dev, NULL, 0, rsc->len, rsc->da, rproc_alloc_carveout, rproc_release_carveout, rsc->name); @@ -2434,7 +2436,7 @@ static void rproc_type_release(struct device *dev) idr_destroy(&rproc->notifyids); if (rproc->index >= 0) - ida_simple_remove(&rproc_dev_index, rproc->index); + ida_free(&rproc_dev_index, rproc->index); kfree_const(rproc->firmware); kfree_const(rproc->name); @@ -2551,9 +2553,9 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, goto put_device; /* Assign a unique device index and name */ - rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); + rproc->index = ida_alloc(&rproc_dev_index, GFP_KERNEL); if (rproc->index < 0) { - dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); + dev_err(dev, "ida_alloc failed: %d\n", rproc->index); goto put_device; } @@ -2762,8 +2764,7 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) dev_err(&rproc->dev, "crash detected in %s: type %s\n", rproc->name, rproc_crash_to_string(type)); - /* Have a worker handle the error; ensure system is not suspended */ - queue_work(system_freezable_wq, &rproc->crash_handler); + queue_work(rproc_recovery_wq, &rproc->crash_handler); } EXPORT_SYMBOL(rproc_report_crash); @@ -2812,6 +2813,13 @@ static void __exit rproc_exit_panic(void) static int __init remoteproc_init(void) { + rproc_recovery_wq = alloc_workqueue("rproc_recovery_wq", + WQ_UNBOUND | WQ_FREEZABLE, 0); + if (!rproc_recovery_wq) { + pr_err("remoteproc: creation of rproc_recovery_wq failed\n"); + return -ENOMEM; + } + rproc_init_sysfs(); rproc_init_debugfs(); rproc_init_cdev(); @@ -2825,9 +2833,13 @@ static void __exit remoteproc_exit(void) { ida_destroy(&rproc_dev_index); + if (!rproc_recovery_wq) + return; + rproc_exit_panic(); rproc_exit_debugfs(); rproc_exit_sysfs(); + destroy_workqueue(rproc_recovery_wq); } module_exit(remoteproc_exit); diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 4840ad906018..0481926c6975 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1655,6 +1655,7 @@ static int k3_r5_cluster_of_init(struct platform_device *pdev) if (!cpdev) { ret = -ENODEV; dev_err(dev, "could not get R5 core platform device\n"); + of_node_put(child); goto fail; } @@ -1663,6 +1664,7 @@ static int k3_r5_cluster_of_init(struct platform_device *pdev) dev_err(dev, "k3_r5_core_of_init failed, ret = %d\n", ret); put_device(&cpdev->dev); + of_node_put(child); goto fail; } diff --git a/drivers/rpmsg/mtk_rpmsg.c b/drivers/rpmsg/mtk_rpmsg.c index 5b4404b8be4c..d1213c33da20 100644 --- a/drivers/rpmsg/mtk_rpmsg.c +++ b/drivers/rpmsg/mtk_rpmsg.c @@ -234,7 +234,9 @@ static void mtk_register_device_work_function(struct work_struct *register_work) if (info->registered) continue; + mutex_unlock(&subdev->channels_lock); ret = mtk_rpmsg_register_device(subdev, &info->info); + mutex_lock(&subdev->channels_lock); if (ret) { dev_err(&pdev->dev, "Can't create rpmsg_device\n"); continue; diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 07586514991f..115c0a1eddb1 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -98,8 +98,6 @@ struct glink_core_rx_intent { struct qcom_glink { struct device *dev; - const char *name; - struct mbox_client mbox_client; struct mbox_chan *mbox_chan; @@ -1546,7 +1544,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) cancel_work_sync(&channel->intent_work); if (channel->rpdev) { - strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; @@ -1674,7 +1672,7 @@ static ssize_t rpmsg_name_show(struct device *dev, if (ret < 0) name = dev->of_node->name; - return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name); + return sysfs_emit(buf, "%s\n", name); } static DEVICE_ATTR_RO(rpmsg_name); @@ -1755,10 +1753,6 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, if (ret) dev_err(dev, "failed to add groups\n"); - ret = of_property_read_string(dev->of_node, "label", &glink->name); - if (ret < 0) - glink->name = dev->of_node->name; - glink->mbox_client.dev = dev; glink->mbox_client.knows_txdone = true; glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); diff --git a/drivers/rpmsg/qcom_glink_ssr.c b/drivers/rpmsg/qcom_glink_ssr.c index dea929c6045d..776d64446879 100644 --- a/drivers/rpmsg/qcom_glink_ssr.c +++ b/drivers/rpmsg/qcom_glink_ssr.c @@ -39,7 +39,7 @@ struct cleanup_done_msg { __le32 seq_num; }; -/** +/* * G-Link SSR protocol commands */ #define GLINK_SSR_DO_CLEANUP 0 diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index 1957b27c4cf3..1044cf03c542 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -729,11 +729,11 @@ static int qcom_smd_write_fifo(struct qcom_smd_channel *channel, } /** - * qcom_smd_send - write data to smd channel + * __qcom_smd_send - write data to smd channel * @channel: channel handle * @data: buffer of data to write * @len: number of bytes to write - * @wait: flag to indicate if write has ca wait + * @wait: flag to indicate if write can wait * * This is a blocking write of len bytes into the channel's tx ring buffer and * signal the remote end. It will sleep until there is enough space available @@ -1089,7 +1089,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) /* Assign public information to the rpmsg_device */ rpdev = &qsdev->rpdev; - strncpy(rpdev->id.name, channel->name, RPMSG_NAME_SIZE); + strscpy_pad(rpdev->id.name, channel->name, RPMSG_NAME_SIZE); rpdev->src = RPMSG_ADDR_ANY; rpdev->dst = RPMSG_ADDR_ANY; @@ -1323,7 +1323,7 @@ static void qcom_channel_state_worker(struct work_struct *work) spin_unlock_irqrestore(&edge->channels_lock, flags); - strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; rpmsg_unregister_device(&edge->dev, &chinfo); @@ -1383,6 +1383,7 @@ static int qcom_smd_parse_edge(struct device *dev, } edge->ipc_regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); if (IS_ERR(edge->ipc_regmap)) { ret = PTR_ERR(edge->ipc_regmap); goto put_node; diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index b6183d4f62a2..4f2189111494 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -120,8 +120,11 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) struct rpmsg_device *rpdev = eptdev->rpdev; struct device *dev = &eptdev->dev; - if (eptdev->ept) + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + mutex_unlock(&eptdev->ept_lock); return -EBUSY; + } get_device(dev); @@ -137,11 +140,13 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) if (!ept) { dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); put_device(dev); + mutex_unlock(&eptdev->ept_lock); return -EINVAL; } eptdev->ept = ept; filp->private_data = eptdev; + mutex_unlock(&eptdev->ept_lock); return 0; } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 290c1f02da10..d6dde00efdae 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -604,7 +604,7 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, int ret; if (driver_override) - strcpy(rpdev->id.name, driver_override); + strscpy_pad(rpdev->id.name, driver_override, RPMSG_NAME_SIZE); dev_set_name(dev, "%s.%s.%d.%d", dev_name(dev->parent), rpdev->id.name, rpdev->src, rpdev->dst); @@ -618,6 +618,7 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, strlen(driver_override)); if (ret) { dev_err(dev, "device_set_override failed: %d\n", ret); + put_device(dev); return ret; } } diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index a22cd4abe7d1..39b646d0d40d 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -41,8 +41,8 @@ struct rpmsg_device_ops { rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); - int (*announce_create)(struct rpmsg_device *ept); - int (*announce_destroy)(struct rpmsg_device *ept); + int (*announce_create)(struct rpmsg_device *rpdev); + int (*announce_destroy)(struct rpmsg_device *rpdev); }; /** diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 35d4b398c197..8bd9fd51208c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -763,6 +763,49 @@ static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, ipa_name, com, CARD_DEVID(card)); } +static void qeth_default_link_info(struct qeth_card *card) +{ + struct qeth_link_info *link_info = &card->info.link_info; + + QETH_CARD_TEXT(card, 2, "dftlinfo"); + link_info->duplex = DUPLEX_FULL; + + if (IS_IQD(card) || IS_VM_NIC(card)) { + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + link_info->link_mode = QETH_LINK_MODE_FIBRE_SHORT; + } else { + switch (card->info.link_type) { + case QETH_LINK_TYPE_FAST_ETH: + case QETH_LINK_TYPE_LANE_ETH100: + link_info->speed = SPEED_100; + link_info->port = PORT_TP; + break; + case QETH_LINK_TYPE_GBIT_ETH: + case QETH_LINK_TYPE_LANE_ETH1000: + link_info->speed = SPEED_1000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_10GBIT_ETH: + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_25GBIT_ETH: + link_info->speed = SPEED_25000; + link_info->port = PORT_FIBRE; + break; + default: + dev_info(&card->gdev->dev, + "Unknown link type %x\n", + card->info.link_type); + link_info->speed = SPEED_UNKNOWN; + link_info->port = PORT_OTHER; + } + + link_info->link_mode = QETH_LINK_MODE_UNKNOWN; + } +} + static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, struct qeth_ipa_cmd *cmd) { @@ -790,6 +833,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, netdev_name(card->dev), card->info.chpid); qeth_issue_ipa_msg(cmd, cmd->hdr.return_code, card); netif_carrier_off(card->dev); + qeth_default_link_info(card); } return NULL; case IPA_CMD_STARTLAN: @@ -4744,92 +4788,6 @@ out_free: return rc; } -static int qeth_query_card_info_cb(struct qeth_card *card, - struct qeth_reply *reply, unsigned long data) -{ - struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; - struct qeth_link_info *link_info = reply->param; - struct qeth_query_card_info *card_info; - - QETH_CARD_TEXT(card, 2, "qcrdincb"); - if (qeth_setadpparms_inspect_rc(cmd)) - return -EIO; - - card_info = &cmd->data.setadapterparms.data.card_info; - netdev_dbg(card->dev, - "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", - card_info->card_type, card_info->port_mode, - card_info->port_speed); - - switch (card_info->port_mode) { - case CARD_INFO_PORTM_FULLDUPLEX: - link_info->duplex = DUPLEX_FULL; - break; - case CARD_INFO_PORTM_HALFDUPLEX: - link_info->duplex = DUPLEX_HALF; - break; - default: - link_info->duplex = DUPLEX_UNKNOWN; - } - - switch (card_info->card_type) { - case CARD_INFO_TYPE_1G_COPPER_A: - case CARD_INFO_TYPE_1G_COPPER_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_TP; - break; - case CARD_INFO_TYPE_1G_FIBRE_A: - case CARD_INFO_TYPE_1G_FIBRE_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_FIBRE; - break; - case CARD_INFO_TYPE_10G_FIBRE_A: - case CARD_INFO_TYPE_10G_FIBRE_B: - link_info->speed = SPEED_10000; - link_info->port = PORT_FIBRE; - break; - default: - switch (card_info->port_speed) { - case CARD_INFO_PORTS_10M: - link_info->speed = SPEED_10; - break; - case CARD_INFO_PORTS_100M: - link_info->speed = SPEED_100; - break; - case CARD_INFO_PORTS_1G: - link_info->speed = SPEED_1000; - break; - case CARD_INFO_PORTS_10G: - link_info->speed = SPEED_10000; - break; - case CARD_INFO_PORTS_25G: - link_info->speed = SPEED_25000; - break; - default: - link_info->speed = SPEED_UNKNOWN; - } - - link_info->port = PORT_OTHER; - } - - return 0; -} - -int qeth_query_card_info(struct qeth_card *card, - struct qeth_link_info *link_info) -{ - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 2, "qcrdinfo"); - if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) - return -EOPNOTSUPP; - iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 0); - if (!iob) - return -ENOMEM; - - return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, link_info); -} - static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_reply *reply_priv, unsigned long data) @@ -4839,6 +4797,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_query_oat_physical_if *phys_if; struct qeth_query_oat_reply *reply; + QETH_CARD_TEXT(card, 2, "qoatincb"); if (qeth_setadpparms_inspect_rc(cmd)) return -EIO; @@ -4918,41 +4877,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, static void qeth_init_link_info(struct qeth_card *card) { - card->info.link_info.duplex = DUPLEX_FULL; - - if (IS_IQD(card) || IS_VM_NIC(card)) { - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - card->info.link_info.link_mode = QETH_LINK_MODE_FIBRE_SHORT; - } else { - switch (card->info.link_type) { - case QETH_LINK_TYPE_FAST_ETH: - case QETH_LINK_TYPE_LANE_ETH100: - card->info.link_info.speed = SPEED_100; - card->info.link_info.port = PORT_TP; - break; - case QETH_LINK_TYPE_GBIT_ETH: - case QETH_LINK_TYPE_LANE_ETH1000: - card->info.link_info.speed = SPEED_1000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_10GBIT_ETH: - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_25GBIT_ETH: - card->info.link_info.speed = SPEED_25000; - card->info.link_info.port = PORT_FIBRE; - break; - default: - dev_info(&card->gdev->dev, "Unknown link type %x\n", - card->info.link_type); - card->info.link_info.speed = SPEED_UNKNOWN; - card->info.link_info.port = PORT_OTHER; - } - - card->info.link_info.link_mode = QETH_LINK_MODE_UNKNOWN; - } + qeth_default_link_info(card); /* Get more accurate data via QUERY OAT: */ if (qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { @@ -5461,6 +5386,7 @@ int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc, qeth_clear_working_pool_list(card); qeth_flush_local_addrs(card); card->info.promisc_mode = 0; + qeth_default_link_info(card); rc = qeth_stop_channel(&card->data); rc2 = qeth_stop_channel(&card->write); diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index b0b36b2132fe..9eba0a32e9f9 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -428,8 +428,8 @@ static int qeth_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct qeth_card *card = netdev->ml_priv; - struct qeth_link_info link_info; + QETH_CARD_TEXT(card, 4, "ethtglks"); cmd->base.speed = card->info.link_info.speed; cmd->base.duplex = card->info.link_info.duplex; cmd->base.port = card->info.link_info.port; @@ -439,16 +439,6 @@ static int qeth_get_link_ksettings(struct net_device *netdev, cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; - /* Check if we can obtain more accurate information. */ - if (!qeth_query_card_info(card, &link_info)) { - if (link_info.speed != SPEED_UNKNOWN) - cmd->base.speed = link_info.speed; - if (link_info.duplex != DUPLEX_UNKNOWN) - cmd->base.duplex = link_info.duplex; - if (link_info.port != PORT_OTHER) - cmd->base.port = link_info.port; - } - qeth_set_ethtool_link_modes(cmd, card->info.link_info.link_mode); return 0; diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index 32c346b72635..dff6d5ef4e46 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -107,30 +107,47 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev, { u32 hw_version; int err; - - err = devm_pm_opp_set_clkname(dev, NULL); - if (err) { - dev_err(dev, "failed to set OPP clk: %d\n", err); - return err; - } - - /* Tegra114+ doesn't support OPP yet */ - if (!of_machine_is_compatible("nvidia,tegra20") && - !of_machine_is_compatible("nvidia,tegra30")) - return -ENODEV; - - if (of_machine_is_compatible("nvidia,tegra20")) + /* + * The clk's connection id to set is NULL and this is a NULL terminated + * array, hence two NULL entries. + */ + const char *clk_names[] = { NULL, NULL }; + struct dev_pm_opp_config config = { + /* + * For some devices we don't have any OPP table in the DT, and + * in order to use the same code path for all the devices, we + * create a dummy OPP table for them via this. The dummy OPP + * table is only capable of doing clk_set_rate() on invocation + * of dev_pm_opp_set_rate() and doesn't provide any other + * functionality. + */ + .clk_names = clk_names, + }; + + if (of_machine_is_compatible("nvidia,tegra20")) { hw_version = BIT(tegra_sku_info.soc_process_id); - else + config.supported_hw = &hw_version; + config.supported_hw_count = 1; + } else if (of_machine_is_compatible("nvidia,tegra30")) { hw_version = BIT(tegra_sku_info.soc_speedo_id); + config.supported_hw = &hw_version; + config.supported_hw_count = 1; + } - err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1); + err = devm_pm_opp_set_config(dev, &config); if (err) { - dev_err(dev, "failed to set OPP supported HW: %d\n", err); + dev_err(dev, "failed to set OPP config: %d\n", err); return err; } /* + * Tegra114+ doesn't support OPP yet, return early for non tegra20/30 + * case. + */ + if (!config.supported_hw) + return -ENODEV; + + /* * Older device-trees have an empty OPP table, we will get * -ENODEV from devm_pm_opp_of_add_table() in this case. */ diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 5611d14d3ba2..6a4b8f7e7948 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1384,7 +1384,7 @@ tegra_pmc_core_pd_opp_to_performance_state(struct generic_pm_domain *genpd, static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) { struct generic_pm_domain *genpd; - const char *rname = "core"; + const char *rname[] = { "core", NULL}; int err; genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL); @@ -1395,7 +1395,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state; - err = devm_pm_opp_set_regulators(pmc->dev, &rname, 1); + err = devm_pm_opp_set_regulators(pmc->dev, rname); if (err) return dev_err_probe(pmc->dev, err, "failed to set core OPP regulator\n"); diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 0e5cc948373c..e052dae614eb 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -221,7 +221,7 @@ config THERMAL_EMULATION config THERMAL_MMIO tristate "Generic Thermal MMIO driver" - depends on OF || COMPILE_TEST + depends on OF depends on HAS_IOMEM help This option enables the generic thermal MMIO driver that will use @@ -496,7 +496,7 @@ config SPRD_THERMAL config KHADAS_MCU_FAN_THERMAL tristate "Khadas MCU controller FAN cooling support" - depends on OF || COMPILE_TEST + depends on OF depends on MFD_KHADAS_MCU select MFD_CORE select REGMAP diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index a9596e7562ea..95adac427b6f 100644 --- a/drivers/thermal/intel/intel_tcc_cooling.c +++ b/drivers/thermal/intel/intel_tcc_cooling.c @@ -81,7 +81,9 @@ static const struct x86_cpu_id tcc_ids[] __initconst = { X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, NULL), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), {} }; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 5018459e8dd9..3a8d6e747c25 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -813,12 +813,13 @@ static const struct attribute_group cooling_device_stats_attr_group = { static void cooling_device_stats_setup(struct thermal_cooling_device *cdev) { + const struct attribute_group *stats_attr_group = NULL; struct cooling_dev_stats *stats; unsigned long states; int var; if (cdev->ops->get_max_state(cdev, &states)) - return; + goto out; states++; /* Total number of states is highest state + 1 */ @@ -828,7 +829,7 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev) stats = kzalloc(var, GFP_KERNEL); if (!stats) - return; + goto out; stats->time_in_state = (ktime_t *)(stats + 1); stats->trans_table = (unsigned int *)(stats->time_in_state + states); @@ -838,9 +839,12 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev) spin_lock_init(&stats->lock); + stats_attr_group = &cooling_device_stats_attr_group; + +out: /* Fill the empty slot left in cooling_device_attr_groups */ var = ARRAY_SIZE(cooling_device_attr_groups) - 2; - cooling_device_attr_groups[var] = &cooling_device_stats_attr_group; + cooling_device_attr_groups[var] = stats_attr_group; } static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index ffd9e6c2ffc1..9b65509424dc 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -643,14 +643,12 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd, size_t offset; unsigned int npages = 0; - bytes = iov_iter_get_pages(iter, pages, LONG_MAX, + bytes = iov_iter_get_pages2(iter, pages, LONG_MAX, VHOST_SCSI_PREALLOC_UPAGES, &offset); /* No pages were pinned */ if (bytes <= 0) return bytes < 0 ? bytes : -EFAULT; - iov_iter_advance(iter, bytes); - while (bytes) { unsigned n = min_t(unsigned, PAGE_SIZE - offset, bytes); sg_set_page(sg++, pages[npages++], n, offset); diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c index 1635f421ef2c..854b1cc723cb 100644 --- a/drivers/watchdog/armada_37xx_wdt.c +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -274,6 +274,8 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev) if (!res) return -ENODEV; dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!dev->reg) + return -ENOMEM; /* init clock */ dev->clk = devm_clk_get(&pdev->dev, NULL); diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 1ffcf6aca6ae..9388838899ac 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -192,7 +192,6 @@ static int bcm7038_wdt_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int bcm7038_wdt_suspend(struct device *dev) { struct bcm7038_watchdog *wdt = dev_get_drvdata(dev); @@ -212,10 +211,9 @@ static int bcm7038_wdt_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, bcm7038_wdt_suspend, - bcm7038_wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, + bcm7038_wdt_suspend, bcm7038_wdt_resume); static const struct of_device_id bcm7038_wdt_match[] = { { .compatible = "brcm,bcm6345-wdt" }, @@ -236,7 +234,7 @@ static struct platform_driver bcm7038_wdt_driver = { .driver = { .name = "bcm7038-wdt", .of_match_table = bcm7038_wdt_match, - .pm = &bcm7038_wdt_pm_ops, + .pm = pm_sleep_ptr(&bcm7038_wdt_pm_ops), } }; module_platform_driver(bcm7038_wdt_driver); diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 5e4dc1a0f2c6..75da5cd02615 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -74,7 +74,7 @@ static unsigned long long period_to_sec(unsigned int period) /* * This procedure will find the highest period which will give a timeout * greater than the one required. e.g. for a bus speed of 66666666 and - * and a parameter of 2 secs, then this procedure will return a value of 38. + * a parameter of 2 secs, then this procedure will return a value of 38. */ static unsigned int sec_to_period(unsigned int secs) { diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index cd578843277e..52962e8d11a6 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -218,7 +218,7 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) /* * Set the new value in the watchdog. Some versions of dw_wdt - * have have TOPINIT in the TIMEOUT_RANGE register (as per + * have TOPINIT in the TIMEOUT_RANGE register (as per * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1). On those we * effectively get a pat of the watchdog right here. */ @@ -375,7 +375,6 @@ static irqreturn_t dw_wdt_irq(int irq, void *devid) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP static int dw_wdt_suspend(struct device *dev) { struct dw_wdt *dw_wdt = dev_get_drvdata(dev); @@ -410,9 +409,8 @@ static int dw_wdt_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); /* * In case if DW WDT IP core is synthesized with fixed TOP feature disabled the @@ -710,7 +708,7 @@ static struct platform_driver dw_wdt_driver = { .driver = { .name = "dw_wdt", .of_match_table = of_match_ptr(dw_wdt_of_match), - .pm = &dw_wdt_pm_ops, + .pm = pm_sleep_ptr(&dw_wdt_pm_ops), }, }; diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 7f59c680de25..6a16d3d0bb1e 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -634,7 +634,9 @@ static int __init fintek_wdt_init(void) pdata.type = ret; - platform_driver_register(&fintek_wdt_driver); + ret = platform_driver_register(&fintek_wdt_driver); + if (ret) + return ret; wdt_res.name = "superio port"; wdt_res.flags = IORESOURCE_IO; diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c index b76ad6ba0915..33835c0b06de 100644 --- a/drivers/watchdog/max77620_wdt.c +++ b/drivers/watchdog/max77620_wdt.c @@ -6,7 +6,7 @@ * Copyright (C) 2022 Luca Ceresoli * * Author: Laxman Dewangan <ldewangan@nvidia.com> - * Author: Luca Ceresoli <luca@lucaceresoli.net> + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> */ #include <linux/err.h> @@ -260,5 +260,5 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index f0d4e3cc7459..e97787536792 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -401,7 +401,6 @@ static int mtk_wdt_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int mtk_wdt_suspend(struct device *dev) { struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); @@ -423,7 +422,6 @@ static int mtk_wdt_resume(struct device *dev) return 0; } -#endif static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, @@ -437,16 +435,14 @@ static const struct of_device_id mtk_wdt_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); -static const struct dev_pm_ops mtk_wdt_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend, - mtk_wdt_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(mtk_wdt_pm_ops, + mtk_wdt_suspend, mtk_wdt_resume); static struct platform_driver mtk_wdt_driver = { .probe = mtk_wdt_probe, .driver = { .name = DRV_NAME, - .pm = &mtk_wdt_pm_ops, + .pm = pm_sleep_ptr(&mtk_wdt_pm_ops), .of_match_table = mtk_wdt_dt_ids, }, }; diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 9f9a340427fc..c7f745caf203 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -442,7 +442,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd, } } -/* -- Notifier funtions -----------------------------------------*/ +/* -- Notifier functions -----------------------------------------*/ /** * pc87413_notify_sys: diff --git a/drivers/watchdog/pm8916_wdt.c b/drivers/watchdog/pm8916_wdt.c index 0937b8d33104..f4bfbffaf49c 100644 --- a/drivers/watchdog/pm8916_wdt.c +++ b/drivers/watchdog/pm8916_wdt.c @@ -9,6 +9,12 @@ #include <linux/regmap.h> #include <linux/watchdog.h> +#define PON_POFF_REASON1 0x0c +#define PON_POFF_REASON1_PMIC_WD BIT(2) +#define PON_POFF_REASON2 0x0d +#define PON_POFF_REASON2_UVLO BIT(5) +#define PON_POFF_REASON2_OTST3 BIT(6) + #define PON_INT_RT_STS 0x10 #define PMIC_WD_BARK_STS_BIT BIT(6) @@ -58,9 +64,8 @@ static int pm8916_wdt_ping(struct watchdog_device *wdev) { struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); - return regmap_update_bits(wdt->regmap, - wdt->baseaddr + PON_PMIC_WD_RESET_PET, - WATCHDOG_PET_BIT, WATCHDOG_PET_BIT); + return regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_PET, + WATCHDOG_PET_BIT); } static int pm8916_wdt_configure_timers(struct watchdog_device *wdev) @@ -111,12 +116,14 @@ static irqreturn_t pm8916_wdt_isr(int irq, void *arg) } static const struct watchdog_info pm8916_wdt_ident = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_OVERHEAT | WDIOF_CARDRESET | WDIOF_POWERUNDER, .identity = "QCOM PM8916 PON WDT", }; static const struct watchdog_info pm8916_wdt_pt_ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_OVERHEAT | WDIOF_CARDRESET | WDIOF_POWERUNDER | WDIOF_PRETIMEOUT, .identity = "QCOM PM8916 PON WDT", }; @@ -135,7 +142,9 @@ static int pm8916_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pm8916_wdt *wdt; struct device *parent; + unsigned int val; int err, irq; + u8 poff[2]; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -176,6 +185,30 @@ static int pm8916_wdt_probe(struct platform_device *pdev) wdt->wdev.info = &pm8916_wdt_ident; } + err = regmap_bulk_read(wdt->regmap, wdt->baseaddr + PON_POFF_REASON1, + &poff, ARRAY_SIZE(poff)); + if (err) { + dev_err(dev, "failed to read POFF reason: %d\n", err); + return err; + } + + dev_dbg(dev, "POFF reason: %#x %#x\n", poff[0], poff[1]); + if (poff[0] & PON_POFF_REASON1_PMIC_WD) + wdt->wdev.bootstatus |= WDIOF_CARDRESET; + if (poff[1] & PON_POFF_REASON2_UVLO) + wdt->wdev.bootstatus |= WDIOF_POWERUNDER; + if (poff[1] & PON_POFF_REASON2_OTST3) + wdt->wdev.bootstatus |= WDIOF_OVERHEAT; + + err = regmap_read(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2, + &val); + if (err) { + dev_err(dev, "failed to check if watchdog is active: %d\n", err); + return err; + } + if (val & S2_RESET_EN_BIT) + set_bit(WDOG_HW_RUNNING, &wdt->wdev.status); + /* Configure watchdog to hard-reset mode */ err = regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL, diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 60058a0c3ec4..2a5298c5e8e4 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -366,6 +366,7 @@ static const struct of_device_id otto_wdt_ids[] = { { .compatible = "realtek,rtl8380-wdt" }, { .compatible = "realtek,rtl8390-wdt" }, { .compatible = "realtek,rtl9300-wdt" }, + { .compatible = "realtek,rtl9310-wdt" }, { } }; MODULE_DEVICE_TABLE(of, otto_wdt_ids); diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 6db22f2e3a4f..95919392927f 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -845,8 +845,6 @@ static void s3c2410wdt_shutdown(struct platform_device *dev) s3c2410wdt_stop(&wdt->wdt_device); } -#ifdef CONFIG_PM_SLEEP - static int s3c2410wdt_suspend(struct device *dev) { int ret; @@ -885,10 +883,9 @@ static int s3c2410wdt_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend, - s3c2410wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, + s3c2410wdt_suspend, s3c2410wdt_resume); static struct platform_driver s3c2410wdt_driver = { .probe = s3c2410wdt_probe, @@ -897,7 +894,7 @@ static struct platform_driver s3c2410wdt_driver = { .id_table = s3c2410_wdt_ids, .driver = { .name = "s3c2410-wdt", - .pm = &s3c2410wdt_pm_ops, + .pm = pm_sleep_ptr(&s3c2410wdt_pm_ops), .of_match_table = of_match_ptr(s3c2410_wdt_match), }, }; diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index ec20ad4e534f..aeee934ca51b 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -339,7 +339,6 @@ static const struct of_device_id sama5d4_wdt_of_match[] = { }; MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); -#ifdef CONFIG_PM_SLEEP static int sama5d4_wdt_suspend_late(struct device *dev) { struct sama5d4_wdt *wdt = dev_get_drvdata(dev); @@ -366,18 +365,17 @@ static int sama5d4_wdt_resume_early(struct device *dev) return 0; } -#endif static const struct dev_pm_ops sama5d4_wdt_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late, - sama5d4_wdt_resume_early) + LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late, + sama5d4_wdt_resume_early) }; static struct platform_driver sama5d4_wdt_driver = { .probe = sama5d4_wdt_probe, .driver = { .name = "sama5d4_wdt", - .pm = &sama5d4_wdt_pm_ops, + .pm = pm_sleep_ptr(&sama5d4_wdt_pm_ops), .of_match_table = sama5d4_wdt_of_match, } }; diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 86ffb58fbc85..ae54dd33e233 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -402,6 +402,7 @@ out: iounmap(addr); release_resource(res); + kfree(res); return ret; } diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index f9479a3fe2a6..78ba36689eec 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * drivers/char/watchdog/sp805-wdt.c * @@ -341,6 +342,10 @@ static const struct amba_id sp805_wdt_ids[] = { .id = 0x00141805, .mask = 0x00ffffff, }, + { + .id = 0x001bb824, + .mask = 0x00ffffff, + }, { 0, 0 }, }; diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index 14ab6559c748..39abecdb9dd1 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -248,7 +248,6 @@ static int st_wdog_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int st_wdog_suspend(struct device *dev) { struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); @@ -285,16 +284,14 @@ static int st_wdog_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops, - st_wdog_suspend, - st_wdog_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(st_wdog_pm_ops, + st_wdog_suspend, st_wdog_resume); static struct platform_driver st_wdog_driver = { .driver = { .name = "st-lpc-wdt", - .pm = &st_wdog_pm_ops, + .pm = pm_sleep_ptr(&st_wdog_pm_ops), .of_match_table = st_wdog_match, }, .probe = st_wdog_probe, diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index dfe06e506cad..d5de6c0657a5 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -230,8 +230,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int tegra_wdt_runtime_suspend(struct device *dev) +static int tegra_wdt_suspend(struct device *dev) { struct tegra_wdt *wdt = dev_get_drvdata(dev); @@ -241,7 +240,7 @@ static int tegra_wdt_runtime_suspend(struct device *dev) return 0; } -static int tegra_wdt_runtime_resume(struct device *dev) +static int tegra_wdt_resume(struct device *dev) { struct tegra_wdt *wdt = dev_get_drvdata(dev); @@ -250,7 +249,6 @@ static int tegra_wdt_runtime_resume(struct device *dev) return 0; } -#endif static const struct of_device_id tegra_wdt_of_match[] = { { .compatible = "nvidia,tegra30-timer", }, @@ -258,16 +256,14 @@ static const struct of_device_id tegra_wdt_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_wdt_of_match); -static const struct dev_pm_ops tegra_wdt_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend, - tegra_wdt_runtime_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(tegra_wdt_pm_ops, + tegra_wdt_suspend, tegra_wdt_resume); static struct platform_driver tegra_wdt_driver = { .probe = tegra_wdt_probe, .driver = { .name = "tegra-wdt", - .pm = &tegra_wdt_pm_ops, + .pm = pm_sleep_ptr(&tegra_wdt_pm_ops), .of_match_table = tegra_wdt_of_match, }, }; diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index e6f95e99156d..aeadaa07c891 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -467,7 +467,6 @@ static int wdat_wdt_probe(struct platform_device *pdev) return devm_watchdog_register_device(dev, &wdat->wdd); } -#ifdef CONFIG_PM_SLEEP static int wdat_wdt_suspend_noirq(struct device *dev) { struct wdat_wdt *wdat = dev_get_drvdata(dev); @@ -528,18 +527,16 @@ static int wdat_wdt_resume_noirq(struct device *dev) return wdat_wdt_start(&wdat->wdd); } -#endif static const struct dev_pm_ops wdat_wdt_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq, - wdat_wdt_resume_noirq) + NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq, wdat_wdt_resume_noirq) }; static struct platform_driver wdat_wdt_driver = { .probe = wdat_wdt_probe, .driver = { .name = "wdat_wdt", - .pm = &wdat_wdt_pm_ops, + .pm = pm_sleep_ptr(&wdat_wdt_pm_ops), }, }; diff --git a/fs/Kconfig b/fs/Kconfig index 5976eb33535f..a547307c1ae8 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -247,8 +247,7 @@ config HUGETLB_PAGE # # Select this config option from the architecture Kconfig, if it is preferred -# to enable the feature of minimizing overhead of struct page associated with -# each HugeTLB page. +# to enable the feature of HugeTLB Vmemmap Optimization (HVO). # config ARCH_WANT_HUGETLB_PAGE_OPTIMIZE_VMEMMAP bool @@ -259,14 +258,13 @@ config HUGETLB_PAGE_OPTIMIZE_VMEMMAP depends on SPARSEMEM_VMEMMAP config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON - bool "Default optimizing vmemmap pages of HugeTLB to on" + bool "HugeTLB Vmemmap Optimization (HVO) defaults to on" default n depends on HUGETLB_PAGE_OPTIMIZE_VMEMMAP help - When using HUGETLB_PAGE_OPTIMIZE_VMEMMAP, the optimizing unused vmemmap - pages associated with each HugeTLB page is default off. Say Y here - to enable optimizing vmemmap pages of HugeTLB by default. It can then - be disabled on the command line via hugetlb_free_vmemmap=off. + The HugeTLB VmemmapvOptimization (HVO) defaults to off. Say Y here to + enable HVO by default. It can be disabled via hugetlb_free_vmemmap=off + (boot command line) or hugetlb_optimize_vmemmap (sysctl). config MEMFD_CREATE def_bool TMPFS || HUGETLBFS diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 07ad744eef77..988c2ac7cece 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -158,7 +158,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->name[i] = tolower(name[i]); cell->name[i] = 0; - atomic_set(&cell->ref, 1); + refcount_set(&cell->ref, 1); atomic_set(&cell->active, 0); INIT_WORK(&cell->manager, afs_manage_cell_work); cell->volumes = RB_ROOT; @@ -287,7 +287,7 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, cell = candidate; candidate = NULL; atomic_set(&cell->active, 2); - trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 2, afs_cell_trace_insert); + trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 2, afs_cell_trace_insert); rb_link_node_rcu(&cell->net_node, parent, pp); rb_insert_color(&cell->net_node, &net->cells); up_write(&net->cells_lock); @@ -295,7 +295,7 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, afs_queue_cell(cell, afs_cell_trace_get_queue_new); wait_for_cell: - trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), atomic_read(&cell->active), + trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), atomic_read(&cell->active), afs_cell_trace_wait); _debug("wait_for_cell"); wait_var_event(&cell->state, @@ -490,13 +490,13 @@ static void afs_cell_destroy(struct rcu_head *rcu) { struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu); struct afs_net *net = cell->net; - int u; + int r; _enter("%p{%s}", cell, cell->name); - u = atomic_read(&cell->ref); - ASSERTCMP(u, ==, 0); - trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), afs_cell_trace_free); + r = refcount_read(&cell->ref); + ASSERTCMP(r, ==, 0); + trace_afs_cell(cell->debug_id, r, atomic_read(&cell->active), afs_cell_trace_free); afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias); @@ -539,13 +539,10 @@ void afs_cells_timer(struct timer_list *timer) */ struct afs_cell *afs_get_cell(struct afs_cell *cell, enum afs_cell_trace reason) { - int u; + int r; - if (atomic_read(&cell->ref) <= 0) - BUG(); - - u = atomic_inc_return(&cell->ref); - trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), reason); + __refcount_inc(&cell->ref, &r); + trace_afs_cell(cell->debug_id, r + 1, atomic_read(&cell->active), reason); return cell; } @@ -556,12 +553,14 @@ void afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason) { if (cell) { unsigned int debug_id = cell->debug_id; - unsigned int u, a; + unsigned int a; + bool zero; + int r; a = atomic_read(&cell->active); - u = atomic_dec_return(&cell->ref); - trace_afs_cell(debug_id, u, a, reason); - if (u == 0) { + zero = __refcount_dec_and_test(&cell->ref, &r); + trace_afs_cell(debug_id, r - 1, a, reason); + if (zero) { a = atomic_read(&cell->active); WARN(a != 0, "Cell active count %u > 0\n", a); call_rcu(&cell->rcu, afs_cell_destroy); @@ -574,14 +573,12 @@ void afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason) */ struct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason) { - int u, a; - - if (atomic_read(&cell->ref) <= 0) - BUG(); + int r, a; - u = atomic_read(&cell->ref); + r = refcount_read(&cell->ref); + WARN_ON(r == 0); a = atomic_inc_return(&cell->active); - trace_afs_cell(cell->debug_id, u, a, reason); + trace_afs_cell(cell->debug_id, r, a, reason); return cell; } @@ -593,7 +590,7 @@ void afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_tr { unsigned int debug_id; time64_t now, expire_delay; - int u, a; + int r, a; if (!cell) return; @@ -607,9 +604,9 @@ void afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_tr expire_delay = afs_cell_gc_delay; debug_id = cell->debug_id; - u = atomic_read(&cell->ref); + r = refcount_read(&cell->ref); a = atomic_dec_return(&cell->active); - trace_afs_cell(debug_id, u, a, reason); + trace_afs_cell(debug_id, r, a, reason); WARN_ON(a == 0); if (a == 1) /* 'cell' may now be garbage collected. */ @@ -621,11 +618,11 @@ void afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_tr */ void afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason) { - int u, a; + int r, a; - u = atomic_read(&cell->ref); + r = refcount_read(&cell->ref); a = atomic_read(&cell->active); - trace_afs_cell(cell->debug_id, u, a, reason); + trace_afs_cell(cell->debug_id, r, a, reason); } /* @@ -739,7 +736,7 @@ again: active = 1; if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) { rb_erase(&cell->net_node, &net->cells); - trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 0, + trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 0, afs_cell_trace_unuse_delete); smp_store_release(&cell->state, AFS_CELL_REMOVED); } @@ -866,7 +863,7 @@ void afs_manage_cells(struct work_struct *work) bool sched_cell = false; active = atomic_read(&cell->active); - trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), + trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), active, afs_cell_trace_manage); ASSERTCMP(active, >=, 1); @@ -874,7 +871,7 @@ void afs_manage_cells(struct work_struct *work) if (purging) { if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) { active = atomic_dec_return(&cell->active); - trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), + trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), active, afs_cell_trace_unuse_pin); } } diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index a3f5de28be79..0a090d614e76 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -212,8 +212,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work) * to maintain cache coherency. */ if (call->server) { - trace_afs_server(call->server, - atomic_read(&call->server->ref), + trace_afs_server(call->server->debug_id, + refcount_read(&call->server->ref), atomic_read(&call->server->active), afs_server_trace_callback); afs_break_callbacks(call->server, call->count, call->request); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index a6f25d9e75b5..64ad55494349 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -122,7 +122,7 @@ struct afs_call { }; struct afs_operation *op; unsigned int server_index; - atomic_t usage; + refcount_t ref; enum afs_call_state state; spinlock_t state_lock; int error; /* error code */ @@ -365,7 +365,7 @@ struct afs_cell { struct hlist_node proc_link; /* /proc cell list link */ time64_t dns_expiry; /* Time AFSDB/SRV record expires */ time64_t last_inactive; /* Time of last drop of usage count */ - atomic_t ref; /* Struct refcount */ + refcount_t ref; /* Struct refcount */ atomic_t active; /* Active usage counter */ unsigned long flags; #define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */ @@ -410,7 +410,7 @@ struct afs_vlserver { #define AFS_VLSERVER_FL_IS_YFS 2 /* Server is YFS not AFS */ #define AFS_VLSERVER_FL_RESPONDING 3 /* VL server is responding */ rwlock_t lock; /* Lock on addresses */ - atomic_t usage; + refcount_t ref; unsigned int rtt; /* Server's current RTT in uS */ /* Probe state */ @@ -446,7 +446,7 @@ struct afs_vlserver_entry { struct afs_vlserver_list { struct rcu_head rcu; - atomic_t usage; + refcount_t ref; u8 nr_servers; u8 index; /* Server currently in use */ u8 preferred; /* Preferred server */ @@ -517,7 +517,7 @@ struct afs_server { #define AFS_SERVER_FL_NO_IBULK 17 /* Fileserver doesn't support FS.InlineBulkStatus */ #define AFS_SERVER_FL_NO_RM2 18 /* Fileserver doesn't support YFS.RemoveFile2 */ #define AFS_SERVER_FL_HAS_FS64 19 /* Fileserver supports FS.{Fetch,Store}Data64 */ - atomic_t ref; /* Object refcount */ + refcount_t ref; /* Object refcount */ atomic_t active; /* Active user count */ u32 addr_version; /* Address list version */ unsigned int rtt; /* Server's current RTT in uS */ @@ -571,7 +571,7 @@ struct afs_volume { struct rcu_head rcu; afs_volid_t vid; /* volume ID */ }; - atomic_t usage; + refcount_t ref; time64_t update_at; /* Time at which to next update */ struct afs_cell *cell; /* Cell to which belongs (pins ref) */ struct rb_node cell_node; /* Link in cell->volumes */ @@ -1493,14 +1493,14 @@ extern int afs_end_vlserver_operation(struct afs_vl_cursor *); */ static inline struct afs_vlserver *afs_get_vlserver(struct afs_vlserver *vlserver) { - atomic_inc(&vlserver->usage); + refcount_inc(&vlserver->ref); return vlserver; } static inline struct afs_vlserver_list *afs_get_vlserverlist(struct afs_vlserver_list *vllist) { if (vllist) - atomic_inc(&vllist->usage); + refcount_inc(&vllist->ref); return vllist; } diff --git a/fs/afs/proc.c b/fs/afs/proc.c index e1b863449296..2a0c83d71565 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -47,7 +47,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v) /* display one cell per line on subsequent lines */ seq_printf(m, "%3u %3u %6lld %2u %2u %s\n", - atomic_read(&cell->ref), + refcount_read(&cell->ref), atomic_read(&cell->active), cell->dns_expiry - ktime_get_real_seconds(), vllist ? vllist->nr_servers : 0, @@ -217,7 +217,7 @@ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) } seq_printf(m, "%3d %08llx %s %s\n", - atomic_read(&vol->usage), vol->vid, + refcount_read(&vol->ref), vol->vid, afs_vol_types[vol->type], vol->name); @@ -388,7 +388,7 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) alist = rcu_dereference(server->addresses); seq_printf(m, "%pU %3d %3d\n", &server->uuid, - atomic_read(&server->ref), + refcount_read(&server->ref), atomic_read(&server->active)); seq_printf(m, " - info: fl=%lx rtt=%u brk=%x\n", server->flags, server->rtt, server->cb_s_break); diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index a5434f3e57c6..d5c4785c862d 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -145,14 +145,14 @@ static struct afs_call *afs_alloc_call(struct afs_net *net, call->type = type; call->net = net; call->debug_id = atomic_inc_return(&rxrpc_debug_id); - atomic_set(&call->usage, 1); + refcount_set(&call->ref, 1); INIT_WORK(&call->async_work, afs_process_async_call); init_waitqueue_head(&call->waitq); spin_lock_init(&call->state_lock); call->iter = &call->def_iter; o = atomic_inc_return(&net->nr_outstanding_calls); - trace_afs_call(call, afs_call_trace_alloc, 1, o, + trace_afs_call(call->debug_id, afs_call_trace_alloc, 1, o, __builtin_return_address(0)); return call; } @@ -163,14 +163,16 @@ static struct afs_call *afs_alloc_call(struct afs_net *net, void afs_put_call(struct afs_call *call) { struct afs_net *net = call->net; - int n = atomic_dec_return(&call->usage); - int o = atomic_read(&net->nr_outstanding_calls); + unsigned int debug_id = call->debug_id; + bool zero; + int r, o; - trace_afs_call(call, afs_call_trace_put, n, o, + zero = __refcount_dec_and_test(&call->ref, &r); + o = atomic_read(&net->nr_outstanding_calls); + trace_afs_call(debug_id, afs_call_trace_put, r - 1, o, __builtin_return_address(0)); - ASSERTCMP(n, >=, 0); - if (n == 0) { + if (zero) { ASSERT(!work_pending(&call->async_work)); ASSERT(call->type->name != NULL); @@ -185,7 +187,7 @@ void afs_put_call(struct afs_call *call) afs_put_addrlist(call->alist); kfree(call->request); - trace_afs_call(call, afs_call_trace_free, 0, o, + trace_afs_call(call->debug_id, afs_call_trace_free, 0, o, __builtin_return_address(0)); kfree(call); @@ -198,9 +200,11 @@ void afs_put_call(struct afs_call *call) static struct afs_call *afs_get_call(struct afs_call *call, enum afs_call_trace why) { - int u = atomic_inc_return(&call->usage); + int r; - trace_afs_call(call, why, u, + __refcount_inc(&call->ref, &r); + + trace_afs_call(call->debug_id, why, r + 1, atomic_read(&call->net->nr_outstanding_calls), __builtin_return_address(0)); return call; @@ -668,14 +672,13 @@ static void afs_wake_up_async_call(struct sock *sk, struct rxrpc_call *rxcall, unsigned long call_user_ID) { struct afs_call *call = (struct afs_call *)call_user_ID; - int u; + int r; trace_afs_notify_call(rxcall, call); call->need_attention = true; - u = atomic_fetch_add_unless(&call->usage, 1, 0); - if (u != 0) { - trace_afs_call(call, afs_call_trace_wake, u + 1, + if (__refcount_inc_not_zero(&call->ref, &r)) { + trace_afs_call(call->debug_id, afs_call_trace_wake, r + 1, atomic_read(&call->net->nr_outstanding_calls), __builtin_return_address(0)); diff --git a/fs/afs/server.c b/fs/afs/server.c index 6e5b9a19b234..4981baf97835 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, if (!server) goto enomem; - atomic_set(&server->ref, 1); + refcount_set(&server->ref, 1); atomic_set(&server->active, 1); server->debug_id = atomic_inc_return(&afs_server_debug_id); RCU_INIT_POINTER(server->addresses, alist); @@ -243,7 +243,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, server->rtt = UINT_MAX; afs_inc_servers_outstanding(net); - trace_afs_server(server, 1, 1, afs_server_trace_alloc); + trace_afs_server(server->debug_id, 1, 1, afs_server_trace_alloc); _leave(" = %p", server); return server; @@ -352,9 +352,12 @@ void afs_servers_timer(struct timer_list *timer) struct afs_server *afs_get_server(struct afs_server *server, enum afs_server_trace reason) { - unsigned int u = atomic_inc_return(&server->ref); + unsigned int a; + int r; - trace_afs_server(server, u, atomic_read(&server->active), reason); + __refcount_inc(&server->ref, &r); + a = atomic_read(&server->active); + trace_afs_server(server->debug_id, r + 1, a, reason); return server; } @@ -364,14 +367,14 @@ struct afs_server *afs_get_server(struct afs_server *server, static struct afs_server *afs_maybe_use_server(struct afs_server *server, enum afs_server_trace reason) { - unsigned int r = atomic_fetch_add_unless(&server->ref, 1, 0); unsigned int a; + int r; - if (r == 0) + if (!__refcount_inc_not_zero(&server->ref, &r)) return NULL; a = atomic_inc_return(&server->active); - trace_afs_server(server, r, a, reason); + trace_afs_server(server->debug_id, r + 1, a, reason); return server; } @@ -380,10 +383,13 @@ static struct afs_server *afs_maybe_use_server(struct afs_server *server, */ struct afs_server *afs_use_server(struct afs_server *server, enum afs_server_trace reason) { - unsigned int r = atomic_inc_return(&server->ref); - unsigned int a = atomic_inc_return(&server->active); + unsigned int a; + int r; - trace_afs_server(server, r, a, reason); + __refcount_inc(&server->ref, &r); + a = atomic_inc_return(&server->active); + + trace_afs_server(server->debug_id, r + 1, a, reason); return server; } @@ -393,14 +399,17 @@ struct afs_server *afs_use_server(struct afs_server *server, enum afs_server_tra void afs_put_server(struct afs_net *net, struct afs_server *server, enum afs_server_trace reason) { - unsigned int usage; + unsigned int a, debug_id = server->debug_id; + bool zero; + int r; if (!server) return; - usage = atomic_dec_return(&server->ref); - trace_afs_server(server, usage, atomic_read(&server->active), reason); - if (unlikely(usage == 0)) + a = atomic_inc_return(&server->active); + zero = __refcount_dec_and_test(&server->ref, &r); + trace_afs_server(debug_id, r - 1, a, reason); + if (unlikely(zero)) __afs_put_server(net, server); } @@ -436,7 +445,7 @@ static void afs_server_rcu(struct rcu_head *rcu) { struct afs_server *server = container_of(rcu, struct afs_server, rcu); - trace_afs_server(server, atomic_read(&server->ref), + trace_afs_server(server->debug_id, refcount_read(&server->ref), atomic_read(&server->active), afs_server_trace_free); afs_put_addrlist(rcu_access_pointer(server->addresses)); kfree(server); @@ -487,7 +496,7 @@ static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) active = atomic_read(&server->active); if (active == 0) { - trace_afs_server(server, atomic_read(&server->ref), + trace_afs_server(server->debug_id, refcount_read(&server->ref), active, afs_server_trace_gc); next = rcu_dereference_protected( server->uuid_next, lockdep_is_held(&net->fs_lock.lock)); @@ -553,7 +562,7 @@ void afs_manage_servers(struct work_struct *work) _debug("manage %pU %u", &server->uuid, active); if (purging) { - trace_afs_server(server, atomic_read(&server->ref), + trace_afs_server(server->debug_id, refcount_read(&server->ref), active, afs_server_trace_purging); if (active != 0) pr_notice("Can't purge s=%08x\n", server->debug_id); @@ -633,7 +642,8 @@ static noinline bool afs_update_server_record(struct afs_operation *op, _enter(""); - trace_afs_server(server, atomic_read(&server->ref), atomic_read(&server->active), + trace_afs_server(server->debug_id, refcount_read(&server->ref), + atomic_read(&server->active), afs_server_trace_update); alist = afs_vl_lookup_addrs(op->volume->cell, op->key, &server->uuid); diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 38b2ba1d9ec0..acc48216136a 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -17,7 +17,7 @@ struct afs_vlserver *afs_alloc_vlserver(const char *name, size_t name_len, vlserver = kzalloc(struct_size(vlserver, name, name_len + 1), GFP_KERNEL); if (vlserver) { - atomic_set(&vlserver->usage, 1); + refcount_set(&vlserver->ref, 1); rwlock_init(&vlserver->lock); init_waitqueue_head(&vlserver->probe_wq); spin_lock_init(&vlserver->probe_lock); @@ -39,13 +39,9 @@ static void afs_vlserver_rcu(struct rcu_head *rcu) void afs_put_vlserver(struct afs_net *net, struct afs_vlserver *vlserver) { - if (vlserver) { - unsigned int u = atomic_dec_return(&vlserver->usage); - //_debug("VL PUT %p{%u}", vlserver, u); - - if (u == 0) - call_rcu(&vlserver->rcu, afs_vlserver_rcu); - } + if (vlserver && + refcount_dec_and_test(&vlserver->ref)) + call_rcu(&vlserver->rcu, afs_vlserver_rcu); } struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers) @@ -54,7 +50,7 @@ struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers) vllist = kzalloc(struct_size(vllist, servers, nr_servers), GFP_KERNEL); if (vllist) { - atomic_set(&vllist->usage, 1); + refcount_set(&vllist->ref, 1); rwlock_init(&vllist->lock); } @@ -64,10 +60,7 @@ struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers) void afs_put_vlserverlist(struct afs_net *net, struct afs_vlserver_list *vllist) { if (vllist) { - unsigned int u = atomic_dec_return(&vllist->usage); - - //_debug("VLLS PUT %p{%u}", vllist, u); - if (u == 0) { + if (refcount_dec_and_test(&vllist->ref)) { int i; for (i = 0; i < vllist->nr_servers; i++) { diff --git a/fs/afs/volume.c b/fs/afs/volume.c index cc665cef0abe..f4937029dcd7 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -52,7 +52,7 @@ static void afs_remove_volume_from_cell(struct afs_volume *volume) struct afs_cell *cell = volume->cell; if (!hlist_unhashed(&volume->proc_link)) { - trace_afs_volume(volume->vid, atomic_read(&volume->usage), + trace_afs_volume(volume->vid, refcount_read(&cell->ref), afs_volume_trace_remove); write_seqlock(&cell->volume_lock); hlist_del_rcu(&volume->proc_link); @@ -87,7 +87,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params, volume->type_force = params->force; volume->name_len = vldb->name_len; - atomic_set(&volume->usage, 1); + refcount_set(&volume->ref, 1); INIT_HLIST_NODE(&volume->proc_link); rwlock_init(&volume->servers_lock); rwlock_init(&volume->cb_v_break_lock); @@ -228,7 +228,7 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) afs_remove_volume_from_cell(volume); afs_put_serverlist(net, rcu_access_pointer(volume->servers)); afs_put_cell(volume->cell, afs_cell_trace_put_vol); - trace_afs_volume(volume->vid, atomic_read(&volume->usage), + trace_afs_volume(volume->vid, refcount_read(&volume->ref), afs_volume_trace_free); kfree_rcu(volume, rcu); @@ -242,8 +242,10 @@ struct afs_volume *afs_get_volume(struct afs_volume *volume, enum afs_volume_trace reason) { if (volume) { - int u = atomic_inc_return(&volume->usage); - trace_afs_volume(volume->vid, u, reason); + int r; + + __refcount_inc(&volume->ref, &r); + trace_afs_volume(volume->vid, r + 1, reason); } return volume; } @@ -257,9 +259,12 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume, { if (volume) { afs_volid_t vid = volume->vid; - int u = atomic_dec_return(&volume->usage); - trace_afs_volume(vid, u, reason); - if (u == 0) + bool zero; + int r; + + zero = __refcount_dec_and_test(&volume->ref, &r); + trace_afs_volume(vid, r - 1, reason); + if (zero) afs_destroy_volume(net, volume); } } diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index d6e5916138e4..dcf701b05cc1 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -122,7 +122,7 @@ static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio) * Reference snap context in folio->private. Also set * PagePrivate so that we get invalidate_folio callback. */ - VM_BUG_ON_FOLIO(folio_test_private(folio), folio); + VM_WARN_ON_FOLIO(folio->private, folio); folio_attach_private(folio, snapc); return ceph_fscache_dirty_folio(mapping, folio); @@ -237,7 +237,7 @@ static void finish_netfs_read(struct ceph_osd_request *req) if (err >= 0 && err < subreq->len) __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); - netfs_subreq_terminated(subreq, err, true); + netfs_subreq_terminated(subreq, err, false); num_pages = calc_pages_for(osd_data->alignment, osd_data->length); ceph_put_page_vector(osd_data->pages, num_pages, false); @@ -313,8 +313,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) int err = 0; u64 len = subreq->len; - if (ci->i_inline_version != CEPH_INLINE_NONE && - ceph_netfs_issue_op_inline(subreq)) + if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq)) return; req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, subreq->start, &len, @@ -329,7 +328,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) dout("%s: pos=%llu orig_len=%zu len=%llu\n", __func__, subreq->start, subreq->len, len); iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages, subreq->start, len); - err = iov_iter_get_pages_alloc(&iter, &pages, len, &page_off); + err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off); if (err < 0) { dout("%s: iov_ter_get_pages_alloc returned %d\n", __func__, err); goto out; @@ -338,6 +337,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) /* should always give us a page-aligned read */ WARN_ON_ONCE(page_off); len = err; + err = 0; osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, false); req->r_callback = finish_netfs_read; @@ -345,9 +345,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) req->r_inode = inode; ihold(inode); - err = ceph_osdc_start_request(req->r_osdc, req, false); - if (err) - iput(inode); + ceph_osdc_start_request(req->r_osdc, req); out: ceph_osdc_put_request(req); if (err) @@ -621,9 +619,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len); req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(osdc, req, true); - if (!err) - err = ceph_osdc_wait_request(osdc, req); + ceph_osdc_start_request(osdc, req); + err = ceph_osdc_wait_request(osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); @@ -1151,8 +1148,7 @@ new_request: } req->r_mtime = inode->i_mtime; - rc = ceph_osdc_start_request(&fsc->client->osdc, req, true); - BUG_ON(rc); + ceph_osdc_start_request(&fsc->client->osdc, req); req = NULL; wbc->nr_to_write -= i; @@ -1327,16 +1323,13 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, int r; r = netfs_write_begin(&ci->netfs, file, inode->i_mapping, pos, len, &folio, NULL); - if (r == 0) - folio_wait_fscache(folio); - if (r < 0) { - if (folio) - folio_put(folio); - } else { - WARN_ON_ONCE(!folio_test_locked(folio)); - *pagep = &folio->page; - } - return r; + if (r < 0) + return r; + + folio_wait_fscache(folio); + WARN_ON_ONCE(!folio_test_locked(folio)); + *pagep = &folio->page; + return 0; } /* @@ -1439,7 +1432,7 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf) inode, off, ceph_cap_string(got)); if ((got & (CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO)) || - ci->i_inline_version == CEPH_INLINE_NONE) { + !ceph_has_inline_data(ci)) { CEPH_DEFINE_RW_CONTEXT(rw_ctx, got); ceph_add_rw_context(fi, &rw_ctx); ret = filemap_fault(vmf); @@ -1696,9 +1689,8 @@ int ceph_uninline_data(struct file *file) } req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + err = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_osdc_put_request(req); if (err < 0) goto out_unlock; @@ -1739,9 +1731,8 @@ int ceph_uninline_data(struct file *file) } req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + err = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); @@ -1912,15 +1903,13 @@ static int __ceph_pool_perm_get(struct ceph_inode_info *ci, osd_req_op_raw_data_in_pages(rd_req, 0, pages, PAGE_SIZE, 0, false, true); - err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false); + ceph_osdc_start_request(&fsc->client->osdc, rd_req); wr_req->r_mtime = ci->netfs.inode.i_mtime; - err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false); + ceph_osdc_start_request(&fsc->client->osdc, wr_req); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req); - if (!err2) - err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req); + err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req); + err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req); if (err >= 0 || err == -ENOENT) have |= POOL_READ; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index ac8fd5e7f540..53cfe026b3ea 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -602,8 +602,8 @@ static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap, * @ci: inode to be moved * @session: new auth caps session */ -static void change_auth_cap_ses(struct ceph_inode_info *ci, - struct ceph_mds_session *session) +void change_auth_cap_ses(struct ceph_inode_info *ci, + struct ceph_mds_session *session) { lockdep_assert_held(&ci->i_ceph_lock); @@ -1978,14 +1978,15 @@ retry: } dout("check_caps %llx.%llx file_want %s used %s dirty %s flushing %s" - " issued %s revoking %s retain %s %s%s\n", ceph_vinop(inode), + " issued %s revoking %s retain %s %s%s%s\n", ceph_vinop(inode), ceph_cap_string(file_wanted), ceph_cap_string(used), ceph_cap_string(ci->i_dirty_caps), ceph_cap_string(ci->i_flushing_caps), ceph_cap_string(issued), ceph_cap_string(revoking), ceph_cap_string(retain), (flags & CHECK_CAPS_AUTHONLY) ? " AUTHONLY" : "", - (flags & CHECK_CAPS_FLUSH) ? " FLUSH" : ""); + (flags & CHECK_CAPS_FLUSH) ? " FLUSH" : "", + (flags & CHECK_CAPS_NOINVAL) ? " NOINVAL" : ""); /* * If we no longer need to hold onto old our caps, and we may @@ -3005,7 +3006,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got } if (S_ISREG(ci->netfs.inode.i_mode) && - ci->i_inline_version != CEPH_INLINE_NONE && + ceph_has_inline_data(ci) && (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && i_size_read(inode) > 0) { struct page *page = @@ -3578,24 +3579,23 @@ static void handle_cap_grant(struct inode *inode, fill_inline = true; } - if (ci->i_auth_cap == cap && - le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) { - if (newcaps & ~extra_info->issued) - wake = true; + if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) { + if (ci->i_auth_cap == cap) { + if (newcaps & ~extra_info->issued) + wake = true; - if (ci->i_requested_max_size > max_size || - !(le32_to_cpu(grant->wanted) & CEPH_CAP_ANY_FILE_WR)) { - /* re-request max_size if necessary */ - ci->i_requested_max_size = 0; - wake = true; - } + if (ci->i_requested_max_size > max_size || + !(le32_to_cpu(grant->wanted) & CEPH_CAP_ANY_FILE_WR)) { + /* re-request max_size if necessary */ + ci->i_requested_max_size = 0; + wake = true; + } - ceph_kick_flushing_inode_caps(session, ci); - spin_unlock(&ci->i_ceph_lock); + ceph_kick_flushing_inode_caps(session, ci); + } up_read(&session->s_mdsc->snap_rwsem); - } else { - spin_unlock(&ci->i_ceph_lock); } + spin_unlock(&ci->i_ceph_lock); if (fill_inline) ceph_fill_inline_data(inode, NULL, extra_info->inline_data, diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index eae417d71136..e7e2ebac330d 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -856,6 +856,10 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_quota_is_max_files_exceeded(dir)) { err = -EDQUOT; goto out; @@ -918,6 +922,10 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_quota_is_max_files_exceeded(dir)) { err = -EDQUOT; goto out; @@ -968,9 +976,13 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); struct ceph_mds_request *req; struct ceph_acl_sec_ctx as_ctx = {}; - int err = -EROFS; + int err; int op; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_snap(dir) == CEPH_SNAPDIR) { /* mkdir .snap/foo is a MKSNAP */ op = CEPH_MDS_OP_MKSNAP; @@ -980,6 +992,7 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir, dout("mkdir dir %p dn %p mode 0%ho\n", dir, dentry, mode); op = CEPH_MDS_OP_MKDIR; } else { + err = -EROFS; goto out; } @@ -1037,6 +1050,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, struct ceph_mds_request *req; int err; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; @@ -1071,9 +1088,27 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, struct ceph_mds_request *req) { + struct dentry *dentry = req->r_dentry; + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); + struct ceph_dentry_info *di = ceph_dentry(dentry); int result = req->r_err ? req->r_err : le32_to_cpu(req->r_reply_info.head->result); + if (!test_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags)) + pr_warn("%s dentry %p:%pd async unlink bit is not set\n", + __func__, dentry, dentry); + + spin_lock(&fsc->async_unlink_conflict_lock); + hash_del_rcu(&di->hnode); + spin_unlock(&fsc->async_unlink_conflict_lock); + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; + wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT); + spin_unlock(&dentry->d_lock); + + synchronize_rcu(); + if (result == -EJUKEBOX) goto out; @@ -1081,7 +1116,7 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, if (result) { int pathlen = 0; u64 base = 0; - char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, + char *path = ceph_mdsc_build_path(dentry, &pathlen, &base, 0); /* mark error on parent + clear complete */ @@ -1089,13 +1124,13 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, ceph_dir_clear_complete(req->r_parent); /* drop the dentry -- we don't know its status */ - if (!d_unhashed(req->r_dentry)) - d_drop(req->r_dentry); + if (!d_unhashed(dentry)) + d_drop(dentry); /* mark inode itself for an error (since metadata is bogus) */ mapping_set_error(req->r_old_inode->i_mapping, result); - pr_warn("ceph: async unlink failure path=(%llx)%s result=%d!\n", + pr_warn("async unlink failure path=(%llx)%s result=%d!\n", base, IS_ERR(path) ? "<<bad>>" : path, result); ceph_mdsc_free_path(path, pathlen); } @@ -1180,6 +1215,8 @@ retry: if (try_async && op == CEPH_MDS_OP_UNLINK && (req->r_dir_caps = get_caps_for_async_unlink(dir, dentry))) { + struct ceph_dentry_info *di = ceph_dentry(dentry); + dout("async unlink on %llu/%.*s caps=%s", ceph_ino(dir), dentry->d_name.len, dentry->d_name.name, ceph_cap_string(req->r_dir_caps)); @@ -1187,6 +1224,16 @@ retry: req->r_callback = ceph_async_unlink_cb; req->r_old_inode = d_inode(dentry); ihold(req->r_old_inode); + + spin_lock(&dentry->d_lock); + di->flags |= CEPH_DENTRY_ASYNC_UNLINK; + spin_unlock(&dentry->d_lock); + + spin_lock(&fsc->async_unlink_conflict_lock); + hash_add_rcu(fsc->async_unlink_conflict, &di->hnode, + dentry->d_name.hash); + spin_unlock(&fsc->async_unlink_conflict_lock); + err = ceph_mdsc_submit_request(mdsc, dir, req); if (!err) { /* @@ -1195,10 +1242,20 @@ retry: */ drop_nlink(inode); d_delete(dentry); - } else if (err == -EJUKEBOX) { - try_async = false; - ceph_mdsc_put_request(req); - goto retry; + } else { + spin_lock(&fsc->async_unlink_conflict_lock); + hash_del_rcu(&di->hnode); + spin_unlock(&fsc->async_unlink_conflict_lock); + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; + spin_unlock(&dentry->d_lock); + + if (err == -EJUKEBOX) { + try_async = false; + ceph_mdsc_put_request(req); + goto retry; + } } } else { set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); @@ -1237,6 +1294,10 @@ static int ceph_rename(struct user_namespace *mnt_userns, struct inode *old_dir, (!ceph_quota_is_same_realm(old_dir, new_dir))) return -EXDEV; + err = ceph_wait_on_conflict_unlink(new_dentry); + if (err) + return err; + dout("rename dir %p dentry %p to dir %p dentry %p\n", old_dir, old_dentry, new_dir, new_dentry); req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index da59e836a06e..04fd34557de8 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -95,12 +95,11 @@ static ssize_t __iter_get_bvecs(struct iov_iter *iter, size_t maxsize, size_t start; int idx = 0; - bytes = iov_iter_get_pages(iter, pages, maxsize - size, + bytes = iov_iter_get_pages2(iter, pages, maxsize - size, ITER_GET_BVECS_PAGES, &start); if (bytes < 0) return size ?: bytes; - iov_iter_advance(iter, bytes); size += bytes; for ( ; bytes; idx++, bvec_idx++) { @@ -241,8 +240,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file, INIT_LIST_HEAD(&fi->rw_contexts); fi->filp_gen = READ_ONCE(ceph_inode_to_client(inode)->filp_gen); - if ((file->f_mode & FMODE_WRITE) && - ci->i_inline_version != CEPH_INLINE_NONE) { + if ((file->f_mode & FMODE_WRITE) && ceph_has_inline_data(ci)) { ret = ceph_uninline_data(file); if (ret < 0) goto error; @@ -569,7 +567,7 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc, char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, &base, 0); - pr_warn("ceph: async create failure path=(%llx)%s result=%d!\n", + pr_warn("async create failure path=(%llx)%s result=%d!\n", base, IS_ERR(path) ? "<<bad>>" : path, result); ceph_mdsc_free_path(path, pathlen); @@ -612,6 +610,7 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, struct ceph_mds_reply_inode in = { }; struct ceph_mds_reply_info_in iinfo = { .in = &in }; struct ceph_inode_info *ci = ceph_inode(dir); + struct ceph_dentry_info *di = ceph_dentry(dentry); struct inode *inode; struct timespec64 now; struct ceph_string *pool_ns; @@ -657,10 +656,6 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, /* Directories always inherit the setgid bit. */ if (S_ISDIR(mode)) mode |= S_ISGID; - else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && - !in_group_p(dir->i_gid) && - !capable_wrt_inode_uidgid(&init_user_ns, dir, CAP_FSETID)) - mode &= ~S_ISGID; } else { in.gid = cpu_to_le32(from_kgid(&init_user_ns, current_fsgid())); } @@ -714,6 +709,12 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, file->f_mode |= FMODE_CREATED; ret = finish_open(file, dentry, ceph_open); } + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_CREATE; + wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT); + spin_unlock(&dentry->d_lock); + return ret; } @@ -740,6 +741,15 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > NAME_MAX) return -ENAMETOOLONG; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + /* + * Do not truncate the file, since atomic_open is called before the + * permission check. The caller will do the truncation afterward. + */ + flags &= ~O_TRUNC; + if (flags & O_CREAT) { if (ceph_quota_is_max_files_exceeded(dir)) return -EDQUOT; @@ -786,9 +796,16 @@ retry: (req->r_dir_caps = try_prep_async_create(dir, dentry, &lo, &req->r_deleg_ino))) { + struct ceph_dentry_info *di = ceph_dentry(dentry); + set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags); req->r_args.open.flags |= cpu_to_le32(CEPH_O_EXCL); req->r_callback = ceph_async_create_cb; + + spin_lock(&dentry->d_lock); + di->flags |= CEPH_DENTRY_ASYNC_CREATE; + spin_unlock(&dentry->d_lock); + err = ceph_mdsc_submit_request(mdsc, dir, req); if (!err) { err = ceph_finish_async_create(dir, dentry, @@ -807,9 +824,7 @@ retry: } set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); - err = ceph_mdsc_do_request(mdsc, - (flags & (O_CREAT|O_TRUNC)) ? dir : NULL, - req); + err = ceph_mdsc_do_request(mdsc, (flags & O_CREAT) ? dir : NULL, req); if (err == -ENOENT) { dentry = ceph_handle_snapdir(req, dentry); if (IS_ERR(dentry)) { @@ -965,9 +980,8 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_off, false, false); - ret = ceph_osdc_start_request(osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(osdc, req); + ceph_osdc_start_request(osdc, req); + ret = ceph_osdc_wait_request(osdc, req); ceph_update_read_metrics(&fsc->mdsc->metric, req->r_start_latency, @@ -1230,7 +1244,7 @@ static void ceph_aio_retry_work(struct work_struct *work) req->r_inode = inode; req->r_priv = aio_req; - ret = ceph_osdc_start_request(req->r_osdc, req, false); + ceph_osdc_start_request(req->r_osdc, req); out: if (ret < 0) { req->r_result = ret; @@ -1262,7 +1276,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, size_t count = iov_iter_count(iter); loff_t pos = iocb->ki_pos; bool write = iov_iter_rw(iter) == WRITE; - bool should_dirty = !write && iter_is_iovec(iter); + bool should_dirty = !write && user_backed_iter(iter); if (write && ceph_snap(file_inode(file)) != CEPH_NOSNAP) return -EROFS; @@ -1367,9 +1381,8 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, continue; } - ret = ceph_osdc_start_request(req->r_osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(req->r_osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); if (write) ceph_update_write_metrics(metric, req->r_start_latency, @@ -1432,8 +1445,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, r_private_item); list_del_init(&req->r_private_item); if (ret >= 0) - ret = ceph_osdc_start_request(req->r_osdc, - req, false); + ceph_osdc_start_request(req->r_osdc, req); if (ret < 0) { req->r_result = ret; ceph_aio_complete_req(req); @@ -1546,9 +1558,8 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, false, true); req->r_mtime = mtime; - ret = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, ret); @@ -1632,7 +1643,7 @@ again: inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len, ceph_cap_string(got)); - if (ci->i_inline_version == CEPH_INLINE_NONE) { + if (!ceph_has_inline_data(ci)) { if (!retry_op && (iocb->ki_flags & IOCB_DIRECT)) { ret = ceph_direct_read_write(iocb, to, NULL, NULL); @@ -1895,7 +1906,7 @@ retry_snap: if (dirty) __mark_inode_dirty(inode, dirty); if (ceph_quota_is_max_bytes_approaching(inode, iocb->ki_pos)) - ceph_check_caps(ci, 0, NULL); + ceph_check_caps(ci, CHECK_CAPS_FLUSH, NULL); } dout("aio_write %p %llx.%llx %llu~%u dropping cap refs on %s\n", @@ -1935,57 +1946,15 @@ out_unlocked: */ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence) { - struct inode *inode = file->f_mapping->host; - struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - loff_t i_size; - loff_t ret; - - inode_lock(inode); - if (whence == SEEK_END || whence == SEEK_DATA || whence == SEEK_HOLE) { + struct inode *inode = file_inode(file); + int ret; + ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE, false); if (ret < 0) - goto out; - } - - i_size = i_size_read(inode); - switch (whence) { - case SEEK_END: - offset += i_size; - break; - case SEEK_CUR: - /* - * Here we special-case the lseek(fd, 0, SEEK_CUR) - * position-querying operation. Avoid rewriting the "same" - * f_pos value back to the file because a concurrent read(), - * write() or lseek() might have altered it - */ - if (offset == 0) { - ret = file->f_pos; - goto out; - } - offset += file->f_pos; - break; - case SEEK_DATA: - if (offset < 0 || offset >= i_size) { - ret = -ENXIO; - goto out; - } - break; - case SEEK_HOLE: - if (offset < 0 || offset >= i_size) { - ret = -ENXIO; - goto out; - } - offset = i_size; - break; + return ret; } - - ret = vfs_setpos(file, offset, max(i_size, fsc->max_file_size)); - -out: - inode_unlock(inode); - return ret; + return generic_file_llseek(file, offset, whence); } static inline void ceph_zero_partial_page( @@ -2054,12 +2023,10 @@ static int ceph_zero_partial_object(struct inode *inode, } req->r_mtime = inode->i_mtime; - ret = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!ret) { - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); - if (ret == -ENOENT) - ret = 0; - } + ceph_osdc_start_request(&fsc->client->osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + if (ret == -ENOENT) + ret = 0; ceph_osdc_put_request(req); out: @@ -2361,7 +2328,7 @@ static ssize_t ceph_do_objects_copy(struct ceph_inode_info *src_ci, u64 *src_off if (IS_ERR(req)) ret = PTR_ERR(req); else { - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); ceph_update_copyfrom_metrics(&fsc->mdsc->metric, req->r_start_latency, @@ -2554,7 +2521,8 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, /* Let the MDS know about dst file size change */ if (ceph_inode_set_size(dst_inode, dst_off) || ceph_quota_is_max_bytes_approaching(dst_inode, dst_off)) - ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY, NULL); + ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY | CHECK_CAPS_FLUSH, + NULL); } /* Mark Fw dirty */ spin_lock(&dst_ci->i_ceph_lock); diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 56c53ab3618e..42351d7a0dd6 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1049,7 +1049,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, iinfo->inline_version >= ci->i_inline_version) { int cache_caps = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO; ci->i_inline_version = iinfo->inline_version; - if (ci->i_inline_version != CEPH_INLINE_NONE && + if (ceph_has_inline_data(ci) && (locked_page || (info_caps & cache_caps))) fill_inline = true; } @@ -2275,9 +2275,15 @@ int ceph_try_to_choose_auth_mds(struct inode *inode, int mask) * * This cost much when doing the Locker state transition and * usually will need to revoke caps from clients. + * + * And for the 'Xs' caps for getxattr we will also choose the + * auth MDS, because the MDS side code is buggy due to setxattr + * won't notify the replica MDSes when the values changed and + * the replica MDS will return the old values. Though we will + * fix it in MDS code, but this still makes sense for old ceph. */ if (((mask & CEPH_CAP_ANY_SHARED) && (issued & CEPH_CAP_ANY_EXCL)) - || (mask & CEPH_STAT_RSTAT)) + || (mask & (CEPH_STAT_RSTAT | CEPH_STAT_CAP_XATTR))) return USE_AUTH_MDS; else return USE_ANY_MDS; @@ -2321,7 +2327,8 @@ int __ceph_do_getattr(struct inode *inode, struct page *locked_page, if (inline_version == 0) { /* the reply is supposed to contain inline data */ err = -EINVAL; - } else if (inline_version == CEPH_INLINE_NONE) { + } else if (inline_version == CEPH_INLINE_NONE || + inline_version == 1) { err = -ENODATA; } else { err = req->r_reply_info.targeti.inline_len; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 33f517d549ce..80f8b9ec1a31 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -456,7 +456,7 @@ static int ceph_parse_deleg_inos(void **p, void *end, dout("added delegated inode 0x%llx\n", start - 1); } else if (err == -EBUSY) { - pr_warn("ceph: MDS delegated inode 0x%llx more than once.\n", + pr_warn("MDS delegated inode 0x%llx more than once.\n", start - 1); } else { return err; @@ -655,6 +655,79 @@ static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size)); } +/* + * In async unlink case the kclient won't wait for the first reply + * from MDS and just drop all the links and unhash the dentry and then + * succeeds immediately. + * + * For any new create/link/rename,etc requests followed by using the + * same file names we must wait for the first reply of the inflight + * unlink request, or the MDS possibly will fail these following + * requests with -EEXIST if the inflight async unlink request was + * delayed for some reasons. + * + * And the worst case is that for the none async openc request it will + * successfully open the file if the CDentry hasn't been unlinked yet, + * but later the previous delayed async unlink request will remove the + * CDenty. That means the just created file is possiblly deleted later + * by accident. + * + * We need to wait for the inflight async unlink requests to finish + * when creating new files/directories by using the same file names. + */ +int ceph_wait_on_conflict_unlink(struct dentry *dentry) +{ + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); + struct dentry *pdentry = dentry->d_parent; + struct dentry *udentry, *found = NULL; + struct ceph_dentry_info *di; + struct qstr dname; + u32 hash = dentry->d_name.hash; + int err; + + dname.name = dentry->d_name.name; + dname.len = dentry->d_name.len; + + rcu_read_lock(); + hash_for_each_possible_rcu(fsc->async_unlink_conflict, di, + hnode, hash) { + udentry = di->dentry; + + spin_lock(&udentry->d_lock); + if (udentry->d_name.hash != hash) + goto next; + if (unlikely(udentry->d_parent != pdentry)) + goto next; + if (!hash_hashed(&di->hnode)) + goto next; + + if (!test_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags)) + pr_warn("%s dentry %p:%pd async unlink bit is not set\n", + __func__, dentry, dentry); + + if (!d_same_name(udentry, pdentry, &dname)) + goto next; + + spin_unlock(&udentry->d_lock); + found = dget(udentry); + break; +next: + spin_unlock(&udentry->d_lock); + } + rcu_read_unlock(); + + if (likely(!found)) + return 0; + + dout("%s dentry %p:%pd conflict with old %p:%pd\n", __func__, + dentry, dentry, found, found); + + err = wait_on_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT, + TASK_KILLABLE); + dput(found); + return err; +} + /* * sessions @@ -1220,14 +1293,17 @@ static int encode_supported_features(void **p, void *end) if (count > 0) { size_t i; size_t size = FEATURE_BYTES(count); + unsigned long bit; if (WARN_ON_ONCE(*p + 4 + size > end)) return -ERANGE; ceph_encode_32(p, size); memset(*p, 0, size); - for (i = 0; i < count; i++) - ((unsigned char*)(*p))[i / 8] |= BIT(feature_bits[i] % 8); + for (i = 0; i < count; i++) { + bit = feature_bits[i]; + ((unsigned char *)(*p))[bit / 8] |= BIT(bit % 8); + } *p += size; } else { if (WARN_ON_ONCE(*p + 4 > end)) @@ -2884,6 +2960,64 @@ static void __do_request(struct ceph_mds_client *mdsc, if (req->r_request_started == 0) /* note request start time */ req->r_request_started = jiffies; + /* + * For async create we will choose the auth MDS of frag in parent + * directory to send the request and ususally this works fine, but + * if the migrated the dirtory to another MDS before it could handle + * it the request will be forwarded. + * + * And then the auth cap will be changed. + */ + if (test_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags) && req->r_num_fwd) { + struct ceph_dentry_info *di = ceph_dentry(req->r_dentry); + struct ceph_inode_info *ci; + struct ceph_cap *cap; + + /* + * The request maybe handled very fast and the new inode + * hasn't been linked to the dentry yet. We need to wait + * for the ceph_finish_async_create(), which shouldn't be + * stuck too long or fail in thoery, to finish when forwarding + * the request. + */ + if (!d_inode(req->r_dentry)) { + err = wait_on_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT, + TASK_KILLABLE); + if (err) { + mutex_lock(&req->r_fill_mutex); + set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags); + mutex_unlock(&req->r_fill_mutex); + goto out_session; + } + } + + ci = ceph_inode(d_inode(req->r_dentry)); + + spin_lock(&ci->i_ceph_lock); + cap = ci->i_auth_cap; + if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE && mds != cap->mds) { + dout("do_request session changed for auth cap %d -> %d\n", + cap->session->s_mds, session->s_mds); + + /* Remove the auth cap from old session */ + spin_lock(&cap->session->s_cap_lock); + cap->session->s_nr_caps--; + list_del_init(&cap->session_caps); + spin_unlock(&cap->session->s_cap_lock); + + /* Add the auth cap to the new session */ + cap->mds = mds; + cap->session = session; + spin_lock(&session->s_cap_lock); + session->s_nr_caps++; + list_add_tail(&cap->session_caps, &session->s_caps); + spin_unlock(&session->s_cap_lock); + + change_auth_cap_ses(ci, session); + } + spin_unlock(&ci->i_ceph_lock); + } + err = __send_request(session, req, false); out_session: @@ -3464,11 +3598,26 @@ static void handle_session(struct ceph_mds_session *session, case CEPH_SESSION_OPEN: if (session->s_state == CEPH_MDS_SESSION_RECONNECTING) pr_info("mds%d reconnect success\n", session->s_mds); - session->s_state = CEPH_MDS_SESSION_OPEN; - session->s_features = features; - renewed_caps(mdsc, session, 0); - if (test_bit(CEPHFS_FEATURE_METRIC_COLLECT, &session->s_features)) - metric_schedule_delayed(&mdsc->metric); + + if (session->s_state == CEPH_MDS_SESSION_OPEN) { + pr_notice("mds%d is already opened\n", session->s_mds); + } else { + session->s_state = CEPH_MDS_SESSION_OPEN; + session->s_features = features; + renewed_caps(mdsc, session, 0); + if (test_bit(CEPHFS_FEATURE_METRIC_COLLECT, + &session->s_features)) + metric_schedule_delayed(&mdsc->metric); + } + + /* + * The connection maybe broken and the session in client + * side has been reinitialized, need to update the seq + * anyway. + */ + if (!session->s_seq && seq) + session->s_seq = seq; + wake = 1; if (mdsc->stopping) __close_session(mdsc, session); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 1140aecd82ce..256e3eada6c1 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -29,14 +29,12 @@ enum ceph_feature_type { CEPHFS_FEATURE_MULTI_RECONNECT, CEPHFS_FEATURE_DELEG_INO, CEPHFS_FEATURE_METRIC_COLLECT, + CEPHFS_FEATURE_ALTERNATE_NAME, + CEPHFS_FEATURE_NOTIFY_SESSION_STATE, - CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_METRIC_COLLECT, + CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_NOTIFY_SESSION_STATE, }; -/* - * This will always have the highest feature bit value - * as the last element of the array. - */ #define CEPHFS_FEATURES_CLIENT_SUPPORTED { \ 0, 1, 2, 3, 4, 5, 6, 7, \ CEPHFS_FEATURE_MIMIC, \ @@ -45,10 +43,8 @@ enum ceph_feature_type { CEPHFS_FEATURE_MULTI_RECONNECT, \ CEPHFS_FEATURE_DELEG_INO, \ CEPHFS_FEATURE_METRIC_COLLECT, \ - \ - CEPHFS_FEATURE_MAX, \ + CEPHFS_FEATURE_NOTIFY_SESSION_STATE, \ } -#define CEPHFS_FEATURES_CLIENT_REQUIRED {} /* * Some lock dependencies: @@ -582,6 +578,7 @@ static inline int ceph_wait_on_async_create(struct inode *inode) TASK_KILLABLE); } +extern int ceph_wait_on_conflict_unlink(struct dentry *dentry); extern u64 ceph_get_deleg_ino(struct ceph_mds_session *session); extern int ceph_restore_deleg_ino(struct ceph_mds_session *session, u64 ino); #endif diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 30387733765d..8d0a6d2c2da4 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -352,12 +352,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2) __decode_and_drop_type(p, end, u8, bad_ext); } if (mdsmap_ev >= 8) { - u32 name_len; /* enabled */ ceph_decode_8_safe(p, end, m->m_enabled, bad_ext); - ceph_decode_32_safe(p, end, name_len, bad_ext); - ceph_decode_need(p, end, name_len, bad_ext); - *p += name_len; + /* fs_name */ + ceph_decode_skip_string(p, end, bad_ext); } /* damaged */ if (mdsmap_ev >= 9) { @@ -370,6 +368,22 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2) } else { m->m_damaged = false; } + if (mdsmap_ev >= 17) { + /* balancer */ + ceph_decode_skip_string(p, end, bad_ext); + /* standby_count_wanted */ + ceph_decode_skip_32(p, end, bad_ext); + /* old_max_mds */ + ceph_decode_skip_32(p, end, bad_ext); + /* min_compat_client */ + ceph_decode_skip_8(p, end, bad_ext); + /* required_client_features */ + ceph_decode_skip_set(p, end, 64, bad_ext); + ceph_decode_64_safe(p, end, m->m_max_xattr_size, bad_ext); + } else { + /* This forces the usage of the (sync) SETXATTR Op */ + m->m_max_xattr_size = 0; + } bad_ext: dout("mdsmap_decode m_enabled: %d, m_damaged: %d, m_num_laggy: %d\n", !!m->m_enabled, !!m->m_damaged, m->m_num_laggy); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 40140805bdcf..3fc48b43cab0 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -72,15 +72,9 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = CEPH_SUPER_MAGIC; /* ?? */ /* - * express utilization in terms of large blocks to avoid + * Express utilization in terms of large blocks to avoid * overflow on 32-bit machines. - * - * NOTE: for the time being, we make bsize == frsize to humor - * not-yet-ancient versions of glibc that are broken. - * Someday, we will probably want to report a real block - * size... whatever that may mean for a network file system! */ - buf->f_bsize = 1 << CEPH_BLOCK_SHIFT; buf->f_frsize = 1 << CEPH_BLOCK_SHIFT; /* @@ -95,6 +89,14 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); } + /* + * NOTE: for the time being, we make bsize == frsize to humor + * not-yet-ancient versions of glibc that are broken. + * Someday, we will probably want to report a real block + * size... whatever that may mean for a network file system! + */ + buf->f_bsize = buf->f_frsize; + buf->f_files = le64_to_cpu(st.num_objects); buf->f_ffree = -1; buf->f_namelen = NAME_MAX; @@ -816,6 +818,9 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, if (!fsc->cap_wq) goto fail_inode_wq; + hash_init(fsc->async_unlink_conflict); + spin_lock_init(&fsc->async_unlink_conflict_lock); + spin_lock(&ceph_fsc_lock); list_add_tail(&fsc->metric_wakeup, &ceph_fsc_list); spin_unlock(&ceph_fsc_lock); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index f59dac66955b..40630e6f691c 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -19,6 +19,7 @@ #include <linux/security.h> #include <linux/netfs.h> #include <linux/fscache.h> +#include <linux/hashtable.h> #include <linux/ceph/libceph.h> @@ -99,6 +100,8 @@ struct ceph_mount_options { char *mon_addr; }; +#define CEPH_ASYNC_CREATE_CONFLICT_BITS 8 + struct ceph_fs_client { struct super_block *sb; @@ -124,6 +127,9 @@ struct ceph_fs_client { struct workqueue_struct *inode_wq; struct workqueue_struct *cap_wq; + DECLARE_HASHTABLE(async_unlink_conflict, CEPH_ASYNC_CREATE_CONFLICT_BITS); + spinlock_t async_unlink_conflict_lock; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dentry_lru, *debugfs_caps; struct dentry *debugfs_congestion_kb; @@ -280,7 +286,8 @@ struct ceph_dentry_info { struct dentry *dentry; struct ceph_mds_session *lease_session; struct list_head lease_list; - unsigned flags; + struct hlist_node hnode; + unsigned long flags; int lease_shared_gen; u32 lease_gen; u32 lease_seq; @@ -289,10 +296,14 @@ struct ceph_dentry_info { u64 offset; }; -#define CEPH_DENTRY_REFERENCED 1 -#define CEPH_DENTRY_LEASE_LIST 2 -#define CEPH_DENTRY_SHRINK_LIST 4 -#define CEPH_DENTRY_PRIMARY_LINK 8 +#define CEPH_DENTRY_REFERENCED (1 << 0) +#define CEPH_DENTRY_LEASE_LIST (1 << 1) +#define CEPH_DENTRY_SHRINK_LIST (1 << 2) +#define CEPH_DENTRY_PRIMARY_LINK (1 << 3) +#define CEPH_DENTRY_ASYNC_UNLINK_BIT (4) +#define CEPH_DENTRY_ASYNC_UNLINK (1 << CEPH_DENTRY_ASYNC_UNLINK_BIT) +#define CEPH_DENTRY_ASYNC_CREATE_BIT (5) +#define CEPH_DENTRY_ASYNC_CREATE (1 << CEPH_DENTRY_ASYNC_CREATE_BIT) struct ceph_inode_xattrs_info { /* @@ -758,6 +769,8 @@ extern void ceph_unreserve_caps(struct ceph_mds_client *mdsc, extern void ceph_reservation_status(struct ceph_fs_client *client, int *total, int *avail, int *used, int *reserved, int *min); +extern void change_auth_cap_ses(struct ceph_inode_info *ci, + struct ceph_mds_session *session); @@ -1218,6 +1231,14 @@ extern int ceph_pool_perm_check(struct inode *inode, int need); extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc); int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invalidate); +static inline bool ceph_has_inline_data(struct ceph_inode_info *ci) +{ + if (ci->i_inline_version == CEPH_INLINE_NONE || + ci->i_inline_version == 1) /* initial version, no data */ + return false; + return true; +} + /* file.c */ extern const struct file_operations ceph_file_fops; diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index f141f5246163..f31350cda960 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1086,7 +1086,7 @@ static int ceph_sync_setxattr(struct inode *inode, const char *name, flags |= CEPH_XATTR_REMOVE; } - dout("setxattr value=%.*s\n", (int)size, value); + dout("setxattr value size: %zu\n", size); /* do request */ req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); @@ -1184,8 +1184,14 @@ int __ceph_setxattr(struct inode *inode, const char *name, spin_lock(&ci->i_ceph_lock); retry: issued = __ceph_caps_issued(ci, NULL); - if (ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) + required_blob_size = __get_required_blob_size(ci, name_len, val_len); + if ((ci->i_xattrs.version == 0) || !(issued & CEPH_CAP_XATTR_EXCL) || + (required_blob_size > mdsc->mdsmap->m_max_xattr_size)) { + dout("%s do sync setxattr: version: %llu size: %d max: %llu\n", + __func__, ci->i_xattrs.version, required_blob_size, + mdsc->mdsmap->m_max_xattr_size); goto do_sync; + } if (!lock_snap_rwsem && !ci->i_head_snapc) { lock_snap_rwsem = true; @@ -1201,8 +1207,6 @@ retry: ceph_cap_string(issued)); __build_xattrs(inode); - required_blob_size = __get_required_blob_size(ci, name_len, val_len); - if (!ci->i_xattrs.prealloc_blob || required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) { struct ceph_buffer *blob; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 85f2abcb2795..d5a434176ce5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3276,7 +3276,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, if (ctx->direct_io) { ssize_t result; - result = iov_iter_get_pages_alloc( + result = iov_iter_get_pages_alloc2( from, &pagevec, cur_len, &start); if (result < 0) { cifs_dbg(VFS, @@ -3290,7 +3290,6 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, break; } cur_len = (size_t)result; - iov_iter_advance(from, cur_len); nr_pages = (cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE; @@ -4012,7 +4011,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, if (ctx->direct_io) { ssize_t result; - result = iov_iter_get_pages_alloc( + result = iov_iter_get_pages_alloc2( &direct_iov, &pagevec, cur_len, &start); if (result < 0) { @@ -4028,7 +4027,6 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, break; } cur_len = (size_t)result; - iov_iter_advance(&direct_iov, cur_len); rdata = cifs_readdata_direct_alloc( pagevec, cifs_uncached_readv_complete); @@ -4258,7 +4256,7 @@ static ssize_t __cifs_readv( if (!is_sync_kiocb(iocb)) ctx->iocb = iocb; - if (iter_is_iovec(to)) + if (user_backed_iter(to)) ctx->should_dirty = true; if (direct) { diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 7a906067db04..987f47f665d5 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1022,7 +1022,7 @@ setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) saved_len = count; while (count && npages < max_pages) { - rc = iov_iter_get_pages(iter, pages, count, max_pages, &start); + rc = iov_iter_get_pages2(iter, pages, count, max_pages, &start); if (rc < 0) { cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); break; @@ -1034,7 +1034,6 @@ setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) break; } - iov_iter_advance(iter, rc); count -= rc; rc += start; cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 14e0ef5e9a20..12bd61d20f69 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -86,7 +86,8 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) /** * fscrypt_fname_encrypt() - encrypt a filename * @inode: inode of the parent directory (for regular filenames) - * or of the symlink (for symlink targets) + * or of the symlink (for symlink targets). Key must already be + * set up. * @iname: the filename to encrypt * @out: (output) the encrypted filename * @olen: size of the encrypted filename. It must be at least @iname->len. @@ -137,6 +138,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, return 0; } +EXPORT_SYMBOL_GPL(fscrypt_fname_encrypt); /** * fname_decrypt() - decrypt a filename @@ -264,9 +266,9 @@ static int fscrypt_base64url_decode(const char *src, int srclen, u8 *dst) return bp - dst; } -bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, - u32 orig_len, u32 max_len, - u32 *encrypted_len_ret) +bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret) { int padding = 4 << (fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAGS_PAD_MASK); @@ -281,6 +283,29 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, } /** + * fscrypt_fname_encrypted_size() - calculate length of encrypted filename + * @inode: parent inode of dentry name being encrypted. Key must + * already be set up. + * @orig_len: length of the original filename + * @max_len: maximum length to return + * @encrypted_len_ret: where calculated length should be returned (on success) + * + * Filenames that are shorter than the maximum length may have their lengths + * increased slightly by encryption, due to padding that is applied. + * + * Return: false if the orig_len is greater than max_len. Otherwise, true and + * fill out encrypted_len_ret with the length (up to max_len). + */ +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret) +{ + return __fscrypt_fname_encrypted_size(&inode->i_crypt_info->ci_policy, + orig_len, max_len, + encrypted_len_ret); +} +EXPORT_SYMBOL_GPL(fscrypt_fname_encrypted_size); + +/** * fscrypt_fname_alloc_buffer() - allocate a buffer for presented filenames * @max_encrypted_len: maximum length of encrypted filenames the buffer will be * used to present @@ -435,8 +460,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, return ret; if (fscrypt_has_encryption_key(dir)) { - if (!fscrypt_fname_encrypted_size(&dir->i_crypt_info->ci_policy, - iname->len, NAME_MAX, + if (!fscrypt_fname_encrypted_size(dir, iname->len, NAME_MAX, &fname->crypto_buf.len)) return -ENAMETOOLONG; fname->crypto_buf.name = kmalloc(fname->crypto_buf.len, diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index f5be777d8279..3afdaa084773 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -297,14 +297,11 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci); /* fname.c */ -int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, - u8 *out, unsigned int olen); -bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, - u32 orig_len, u32 max_len, - u32 *encrypted_len_ret); +bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret); /* hkdf.c */ - struct fscrypt_hkdf { struct crypto_shash *hmac_tfm; }; diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index af74599ae1cf..7c01025879b3 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -228,9 +228,9 @@ int fscrypt_prepare_symlink(struct inode *dir, const char *target, * counting it (even though it is meaningless for ciphertext) is simpler * for now since filesystems will assume it is there and subtract it. */ - if (!fscrypt_fname_encrypted_size(policy, len, - max_len - sizeof(struct fscrypt_symlink_data), - &disk_link->len)) + if (!__fscrypt_fname_encrypted_size(policy, len, + max_len - sizeof(struct fscrypt_symlink_data), + &disk_link->len)) return -ENAMETOOLONG; disk_link->len += sizeof(struct fscrypt_symlink_data); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 8a054e6d1e68..80b8ca0f340b 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -694,6 +694,32 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir) } /** + * fscrypt_context_for_new_inode() - create an encryption context for a new inode + * @ctx: where context should be written + * @inode: inode from which to fetch policy and nonce + * + * Given an in-core "prepared" (via fscrypt_prepare_new_inode) inode, + * generate a new context and write it to ctx. ctx _must_ be at least + * FSCRYPT_SET_CONTEXT_MAX_SIZE bytes. + * + * Return: size of the resulting context or a negative error code. + */ +int fscrypt_context_for_new_inode(void *ctx, struct inode *inode) +{ + struct fscrypt_info *ci = inode->i_crypt_info; + + BUILD_BUG_ON(sizeof(union fscrypt_context) != + FSCRYPT_SET_CONTEXT_MAX_SIZE); + + /* fscrypt_prepare_new_inode() should have set up the key already. */ + if (WARN_ON_ONCE(!ci)) + return -ENOKEY; + + return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode); + +/** * fscrypt_set_context() - Set the fscrypt context of a new inode * @inode: a new inode * @fs_data: private data given by FS and passed to ->set_context() @@ -709,12 +735,9 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) union fscrypt_context ctx; int ctxsize; - /* fscrypt_prepare_new_inode() should have set up the key already. */ - if (WARN_ON_ONCE(!ci)) - return -ENOKEY; - - BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE); - ctxsize = fscrypt_new_context(&ctx, &ci->ci_policy, ci->ci_nonce); + ctxsize = fscrypt_context_for_new_inode(&ctx, inode); + if (ctxsize < 0) + return ctxsize; /* * This may be the first time the inode number is available, so do any diff --git a/fs/dcache.c b/fs/dcache.c index ea5cdec24ea7..c5dc32a59c76 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2248,10 +2248,16 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, } EXPORT_SYMBOL(d_add_ci); - -static inline bool d_same_name(const struct dentry *dentry, - const struct dentry *parent, - const struct qstr *name) +/** + * d_same_name - compare dentry name with case-exact name + * @parent: parent dentry + * @dentry: the negative dentry that was passed to the parent's lookup func + * @name: the case-exact name to be associated with the returned dentry + * + * Return: true if names are same, or false + */ +bool d_same_name(const struct dentry *dentry, const struct dentry *parent, + const struct qstr *name) { if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) { if (dentry->d_name.len != name->len) @@ -2262,6 +2268,7 @@ static inline bool d_same_name(const struct dentry *dentry, dentry->d_name.len, dentry->d_name.name, name) == 0; } +EXPORT_SYMBOL_GPL(d_same_name); /** * __d_lookup_rcu - search for a dentry (racy, store-free) diff --git a/fs/direct-io.c b/fs/direct-io.c index df5e2d048799..f669163d5860 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -169,7 +169,7 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio) const enum req_op dio_op = dio->opf & REQ_OP_MASK; ssize_t ret; - ret = iov_iter_get_pages(sdio->iter, dio->pages, LONG_MAX, DIO_PAGES, + ret = iov_iter_get_pages2(sdio->iter, dio->pages, LONG_MAX, DIO_PAGES, &sdio->from); if (ret < 0 && sdio->blocks_available && dio_op == REQ_OP_WRITE) { @@ -191,7 +191,6 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio) } if (ret >= 0) { - iov_iter_advance(sdio->iter, ret); ret += sdio->from; sdio->head = 0; sdio->tail = (ret + PAGE_SIZE - 1) / PAGE_SIZE; @@ -1251,7 +1250,7 @@ ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode, spin_lock_init(&dio->bio_lock); dio->refcount = 1; - dio->should_dirty = iter_is_iovec(iter) && iov_iter_rw(iter) == READ; + dio->should_dirty = user_backed_iter(iter) && iov_iter_rw(iter) == READ; sdio.iter = iter; sdio.final_block_in_request = end >> blkbits; diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 74920826d8f6..451d8a077e12 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -263,6 +263,8 @@ void fscache_caching_failed(struct fscache_cookie *cookie) { clear_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags); fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_FAILED); + trace_fscache_cookie(cookie->debug_id, refcount_read(&cookie->ref), + fscache_cookie_failed); } EXPORT_SYMBOL(fscache_caching_failed); @@ -739,6 +741,9 @@ again_locked: fallthrough; case FSCACHE_COOKIE_STATE_FAILED: + if (test_and_clear_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags)) + fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end); + if (atomic_read(&cookie->n_accesses) != 0) break; if (test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) { @@ -1063,8 +1068,8 @@ void __fscache_invalidate(struct fscache_cookie *cookie, return; case FSCACHE_COOKIE_STATE_LOOKING_UP: - __fscache_begin_cookie_access(cookie, fscache_access_invalidate_cookie); - set_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags); + if (!test_and_set_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags)) + __fscache_begin_cookie_access(cookie, fscache_access_invalidate_cookie); fallthrough; case FSCACHE_COOKIE_STATE_CREATING: spin_unlock(&cookie->lock); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0e537e580dc1..51897427a534 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -730,14 +730,13 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) } } else { size_t off; - err = iov_iter_get_pages(cs->iter, &page, PAGE_SIZE, 1, &off); + err = iov_iter_get_pages2(cs->iter, &page, PAGE_SIZE, 1, &off); if (err < 0) return err; BUG_ON(!err); cs->len = err; cs->offset = off; cs->pg = page; - iov_iter_advance(cs->iter, err); } return lock_request(cs->req); @@ -1356,7 +1355,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) if (!fud) return -EPERM; - if (!iter_is_iovec(to)) + if (!user_backed_iter(to)) return -EINVAL; fuse_copy_init(&cs, 1, to); @@ -1949,7 +1948,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) if (!fud) return -EPERM; - if (!iter_is_iovec(from)) + if (!user_backed_iter(from)) return -EINVAL; fuse_copy_init(&cs, 0, from); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7154b9555f39..1a3afd469e3a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1414,14 +1414,13 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, while (nbytes < *nbytesp && ap->num_pages < max_pages) { unsigned npages; size_t start; - ret = iov_iter_get_pages(ii, &ap->pages[ap->num_pages], + ret = iov_iter_get_pages2(ii, &ap->pages[ap->num_pages], *nbytesp - nbytes, max_pages - ap->num_pages, &start); if (ret < 0) break; - iov_iter_advance(ii, ret); nbytes += ret; ret += start; @@ -1478,7 +1477,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, inode_unlock(inode); } - io->should_dirty = !write && iter_is_iovec(iter); + io->should_dirty = !write && user_backed_iter(iter); while (count) { ssize_t nres; fl_owner_t owner = current->files; diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 57ff883d432c..05bee80ac7de 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -82,31 +82,6 @@ static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock, } /** - * gfs2_writepage - Write page for writeback mappings - * @page: The page - * @wbc: The writeback control - */ -static int gfs2_writepage(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct iomap_writepage_ctx wpc = { }; - - if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) - goto out; - if (current->journal_info) - goto redirty; - return iomap_writepage(page, wbc, &wpc, &gfs2_writeback_ops); - -redirty: - redirty_page_for_writepage(wbc, page); -out: - unlock_page(page); - return 0; -} - -/** * gfs2_write_jdata_page - gfs2 jdata-specific version of block_write_full_page * @page: The page to write * @wbc: The writeback control @@ -765,7 +740,6 @@ cannot_release: } static const struct address_space_operations gfs2_aops = { - .writepage = gfs2_writepage, .writepages = gfs2_writepages, .read_folio = gfs2_read_folio, .readahead = gfs2_readahead, diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index d8f1239344c1..892006fbbb09 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -780,7 +780,7 @@ static inline bool should_fault_in_pages(struct iov_iter *i, if (!count) return false; - if (!iter_is_iovec(i)) + if (!user_backed_iter(i)) return false; size = PAGE_SIZE; diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index eec4159b08aa..723639376ae2 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -131,7 +131,7 @@ __acquires(&sdp->sd_ail_lock) if (!mapping) continue; spin_unlock(&sdp->sd_ail_lock); - ret = generic_writepages(mapping, wbc); + ret = filemap_fdatawrite_wbc(mapping, wbc); if (need_resched()) { blk_finish_plug(plug); cond_resched(); @@ -222,8 +222,7 @@ out: spin_unlock(&sdp->sd_ail_lock); blk_finish_plug(&plug); if (ret) { - gfs2_lm(sdp, "gfs2_ail1_start_one (generic_writepages) " - "returned: %d\n", ret); + gfs2_lm(sdp, "gfs2_ail1_start_one returned: %d\n", ret); gfs2_withdraw(sdp); } trace_gfs2_ail_flush(sdp, wbc, 0); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index fe0e374b02a3..f7a5b5124d8a 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -282,35 +282,6 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, } #endif -static size_t -hugetlbfs_read_actor(struct page *page, unsigned long offset, - struct iov_iter *to, unsigned long size) -{ - size_t copied = 0; - int i, chunksize; - - /* Find which 4k chunk and offset with in that chunk */ - i = offset >> PAGE_SHIFT; - offset = offset & ~PAGE_MASK; - - while (size) { - size_t n; - chunksize = PAGE_SIZE; - if (offset) - chunksize -= offset; - if (chunksize > size) - chunksize = size; - n = copy_page_to_iter(&page[i], offset, chunksize, to); - copied += n; - if (n != chunksize) - return copied; - offset = 0; - size -= chunksize; - i++; - } - return copied; -} - /* * Support for read() - Find the page attached to f_mapping and copy out the * data. This provides functionality similar to filemap_read(). @@ -360,7 +331,7 @@ static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to) /* * We have the page, copy it to user space buffer. */ - copied = hugetlbfs_read_actor(page, offset, to, nr); + copied = copy_page_to_iter(page, offset, nr, to); put_page(page); } offset += copied; diff --git a/fs/inode.c b/fs/inode.c index 524ee91f74a6..6462276dfdf0 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -422,6 +422,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_io_list); INIT_LIST_HEAD(&inode->i_wb_list); INIT_LIST_HEAD(&inode->i_lru); + INIT_LIST_HEAD(&inode->i_sb_list); __address_space_init_once(&inode->i_data); i_size_ordered_init(inode); } @@ -1021,7 +1022,6 @@ struct inode *new_inode_pseudo(struct super_block *sb) spin_lock(&inode->i_lock); inode->i_state = 0; spin_unlock(&inode->i_lock); - INIT_LIST_HEAD(&inode->i_sb_list); } return inode; } @@ -1165,7 +1165,6 @@ struct inode *inode_insert5(struct inode *inode, unsigned long hashval, { struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval); struct inode *old; - bool creating = inode->i_state & I_CREATING; again: spin_lock(&inode_hash_lock); @@ -1199,7 +1198,12 @@ again: inode->i_state |= I_NEW; hlist_add_head_rcu(&inode->i_hash, head); spin_unlock(&inode->i_lock); - if (!creating) + + /* + * Add inode to the sb list if it's not already. It has I_NEW at this + * point, so it should be safe to test i_sb_list locklessly. + */ + if (list_empty(&inode->i_sb_list)) inode_sb_list_add(inode); unlock: spin_unlock(&inode_hash_lock); @@ -2326,10 +2330,6 @@ void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode, /* Directories are special, and always inherit S_ISGID */ if (S_ISDIR(mode)) mode |= S_ISGID; - else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && - !in_group_p(i_gid_into_mnt(mnt_userns, dir)) && - !capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID)) - mode &= ~S_ISGID; } else inode_fsgid_set(inode, mnt_userns); inode->i_mode = mode; @@ -2485,3 +2485,33 @@ struct timespec64 current_time(struct inode *inode) return timestamp_truncate(now, inode); } EXPORT_SYMBOL(current_time); + +/** + * mode_strip_sgid - handle the sgid bit for non-directories + * @mnt_userns: User namespace of the mount the inode was created from + * @dir: parent directory inode + * @mode: mode of the file to be created in @dir + * + * If the @mode of the new file has both the S_ISGID and S_IXGRP bit + * raised and @dir has the S_ISGID bit raised ensure that the caller is + * either in the group of the parent directory or they have CAP_FSETID + * in their user namespace and are privileged over the parent directory. + * In all other cases, strip the S_ISGID bit from @mode. + * + * Return: the new mode to use for the file + */ +umode_t mode_strip_sgid(struct user_namespace *mnt_userns, + const struct inode *dir, umode_t mode) +{ + if ((mode & (S_ISGID | S_IXGRP)) != (S_ISGID | S_IXGRP)) + return mode; + if (S_ISDIR(mode) || !dir || !(dir->i_mode & S_ISGID)) + return mode; + if (in_group_p(i_gid_into_mnt(mnt_userns, dir))) + return mode; + if (capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID)) + return mode; + + return mode & ~S_ISGID; +} +EXPORT_SYMBOL(mode_strip_sgid); diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 2b82c7f1de88..ca5c62901541 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1529,21 +1529,6 @@ unlock: } int -iomap_writepage(struct page *page, struct writeback_control *wbc, - struct iomap_writepage_ctx *wpc, - const struct iomap_writeback_ops *ops) -{ - int ret; - - wpc->ops = ops; - ret = iomap_do_writepage(page, wbc, wpc); - if (!wpc->ioend) - return ret; - return iomap_submit_ioend(wpc, wpc->ioend, ret); -} -EXPORT_SYMBOL_GPL(iomap_writepage); - -int iomap_writepages(struct address_space *mapping, struct writeback_control *wbc, struct iomap_writepage_ctx *wpc, const struct iomap_writeback_ops *ops) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index c75d33d5c3ce..4eb559a16c9e 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -533,7 +533,7 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, iomi.flags |= IOMAP_NOWAIT; } - if (iter_is_iovec(iter)) + if (user_backed_iter(iter)) dio->flags |= IOMAP_DIO_DIRTY; } else { iomi.flags |= IOMAP_WRITE; diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c index 911444d21267..c5a5c7b90d72 100644 --- a/fs/ksmbd/auth.c +++ b/fs/ksmbd/auth.c @@ -121,8 +121,8 @@ out: return rc; } -static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, - char *dname) +static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *ntlmv2_hash, char *dname) { int ret, len, conv_len; wchar_t *domain = NULL; @@ -158,7 +158,7 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, } conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, - sess->conn->local_nls); + conn->local_nls); if (conv_len < 0 || conv_len > len) { ret = -EINVAL; goto out; @@ -182,7 +182,7 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, } conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, - sess->conn->local_nls); + conn->local_nls); if (conv_len < 0 || conv_len > len) { ret = -EINVAL; goto out; @@ -215,8 +215,9 @@ out: * * Return: 0 on success, error number on error */ -int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, - int blen, char *domain_name, char *cryptkey) +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey) { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; @@ -230,7 +231,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, return -ENOMEM; } - rc = calc_ntlmv2_hash(sess, ntlmv2_hash, domain_name); + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); if (rc) { ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); goto out; @@ -333,7 +334,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, /* process NTLMv2 authentication */ ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", domain_name); - ret = ksmbd_auth_ntlmv2(sess, (struct ntlmv2_resp *)((char *)authblob + nt_off), + ret = ksmbd_auth_ntlmv2(conn, sess, + (struct ntlmv2_resp *)((char *)authblob + nt_off), nt_len - CIFS_ENCPWD_SIZE, domain_name, conn->ntlmssp.cryptkey); kfree(domain_name); @@ -659,8 +661,9 @@ struct derivation { bool binding; }; -static int generate_key(struct ksmbd_session *sess, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) +static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; @@ -720,8 +723,8 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, goto smb3signkey_ret; } - if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); else rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); @@ -756,17 +759,17 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, if (!chann) return 0; - if (sess->conn->dialect >= SMB30_PROT_ID && signing->binding) + if (conn->dialect >= SMB30_PROT_ID && signing->binding) key = chann->smb3signingkey; else key = sess->smb3signingkey; - rc = generate_key(sess, signing->label, signing->context, key, + rc = generate_key(conn, sess, signing->label, signing->context, key, SMB3_SIGN_KEY_SIZE); if (rc) return rc; - if (!(sess->conn->dialect >= SMB30_PROT_ID && signing->binding)) + if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); @@ -820,30 +823,31 @@ struct derivation_twin { struct derivation decryption; }; -static int generate_smb3encryptionkey(struct ksmbd_session *sess, +static int generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, const struct derivation_twin *ptwin) { int rc; - rc = generate_key(sess, ptwin->encryption.label, + rc = generate_key(conn, sess, ptwin->encryption.label, ptwin->encryption.context, sess->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; - rc = generate_key(sess, ptwin->decryption.label, + rc = generate_key(conn, sess, ptwin->decryption.label, ptwin->decryption.context, sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); - ksmbd_debug(AUTH, "Cipher type %d\n", sess->conn->cipher_type); + ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); ksmbd_debug(AUTH, "Session Key %*ph\n", SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { ksmbd_debug(AUTH, "ServerIn Key %*ph\n", SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); ksmbd_debug(AUTH, "ServerOut Key %*ph\n", @@ -857,7 +861,8 @@ static int generate_smb3encryptionkey(struct ksmbd_session *sess, return 0; } -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess) +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -874,10 +879,11 @@ int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess) d->context.iov_base = "ServerIn "; d->context.iov_len = 10; - return generate_smb3encryptionkey(sess, &twin); + return generate_smb3encryptionkey(conn, sess, &twin); } -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -894,7 +900,7 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) d->context.iov_base = sess->Preauth_HashValue; d->context.iov_len = 64; - return generate_smb3encryptionkey(sess, &twin); + return generate_smb3encryptionkey(conn, sess, &twin); } int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, diff --git a/fs/ksmbd/auth.h b/fs/ksmbd/auth.h index 95629651cf26..25b772653de0 100644 --- a/fs/ksmbd/auth.h +++ b/fs/ksmbd/auth.h @@ -38,8 +38,9 @@ struct kvec; int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, unsigned int nvec, int enc); void ksmbd_copy_gss_neg_header(void *buf); -int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, - int blen, char *domain_name, char *cryptkey); +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey); int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_conn *conn, struct ksmbd_session *sess); @@ -58,8 +59,10 @@ int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess); -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash); int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index e8f476c5f189..756ad631c019 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -36,6 +36,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) list_del(&conn->conns_list); write_unlock(&conn_list_lock); + xa_destroy(&conn->sessions); kvfree(conn->request_buf); kfree(conn->preauth_info); kfree(conn); @@ -65,13 +66,14 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) conn->outstanding_credits = 0; init_waitqueue_head(&conn->req_running_q); + init_waitqueue_head(&conn->r_count_q); INIT_LIST_HEAD(&conn->conns_list); - INIT_LIST_HEAD(&conn->sessions); INIT_LIST_HEAD(&conn->requests); INIT_LIST_HEAD(&conn->async_requests); spin_lock_init(&conn->request_lock); spin_lock_init(&conn->credits_lock); ida_init(&conn->async_ida); + xa_init(&conn->sessions); spin_lock_init(&conn->llist_lock); INIT_LIST_HEAD(&conn->lock_list); @@ -164,7 +166,6 @@ int ksmbd_conn_write(struct ksmbd_work *work) struct kvec iov[3]; int iov_idx = 0; - ksmbd_conn_try_dequeue_request(work); if (!work->response_buf) { pr_err("NULL response header\n"); return -EINVAL; @@ -346,8 +347,8 @@ int ksmbd_conn_handler_loop(void *p) out: /* Wait till all reference dropped to the Server object*/ - while (atomic_read(&conn->r_count) > 0) - schedule_timeout(HZ); + wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); + unload_nls(conn->local_nls); if (default_conn_ops.terminate_fn) diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index 98c1cbe45ec9..e7f7d5707951 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -20,13 +20,6 @@ #define KSMBD_SOCKET_BACKLOG 16 -/* - * WARNING - * - * This is nothing but a HACK. Session status should move to channel - * or to session. As of now we have 1 tcp_conn : 1 ksmbd_session, but - * we need to change it to 1 tcp_conn : N ksmbd_sessions. - */ enum { KSMBD_SESS_NEW = 0, KSMBD_SESS_GOOD, @@ -55,7 +48,7 @@ struct ksmbd_conn { struct nls_table *local_nls; struct list_head conns_list; /* smb session 1 per user */ - struct list_head sessions; + struct xarray sessions; unsigned long last_active; /* How many request are running currently */ atomic_t req_running; @@ -65,6 +58,7 @@ struct ksmbd_conn { unsigned int outstanding_credits; spinlock_t credits_lock; wait_queue_head_t req_running_q; + wait_queue_head_t r_count_q; /* Lock to protect requests list*/ spinlock_t request_lock; struct list_head requests; diff --git a/fs/ksmbd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c index cb72d30f5b71..70655af93b44 100644 --- a/fs/ksmbd/mgmt/share_config.c +++ b/fs/ksmbd/mgmt/share_config.c @@ -222,17 +222,3 @@ bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, } return false; } - -void ksmbd_share_configs_cleanup(void) -{ - struct ksmbd_share_config *share; - struct hlist_node *tmp; - int i; - - down_write(&shares_table_lock); - hash_for_each_safe(shares_table, i, tmp, share, hlist) { - hash_del(&share->hlist); - kill_share(share); - } - up_write(&shares_table_lock); -} diff --git a/fs/ksmbd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h index 953befc94e84..28bf3511763f 100644 --- a/fs/ksmbd/mgmt/share_config.h +++ b/fs/ksmbd/mgmt/share_config.h @@ -76,6 +76,4 @@ static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) struct ksmbd_share_config *ksmbd_share_config_get(char *name); bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, const char *filename); -void ksmbd_share_configs_cleanup(void); - #endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c index 0d28e723a28c..b35ea6a6abc5 100644 --- a/fs/ksmbd/mgmt/tree_connect.c +++ b/fs/ksmbd/mgmt/tree_connect.c @@ -16,7 +16,8 @@ #include "user_session.h" struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *share_name) { struct ksmbd_tree_conn_status status = {-EINVAL, NULL}; struct ksmbd_tree_connect_response *resp = NULL; @@ -41,7 +42,7 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) goto out_error; } - peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn); + peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); resp = ksmbd_ipc_tree_connect_request(sess, sc, tree_conn, diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h index 18e2a996e0aa..71e50271dccf 100644 --- a/fs/ksmbd/mgmt/tree_connect.h +++ b/fs/ksmbd/mgmt/tree_connect.h @@ -12,6 +12,7 @@ struct ksmbd_share_config; struct ksmbd_user; +struct ksmbd_conn; struct ksmbd_tree_connect { int id; @@ -40,7 +41,8 @@ static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, struct ksmbd_session; struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name); +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *share_name); int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn); diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index 8d8ffd8c6f19..3fa2139a0b30 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -32,11 +32,13 @@ static void free_channel_list(struct ksmbd_session *sess) { struct channel *chann, *tmp; + write_lock(&sess->chann_lock); list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, chann_list) { list_del(&chann->chann_list); kfree(chann); } + write_unlock(&sess->chann_lock); } static void __session_rpc_close(struct ksmbd_session *sess, @@ -149,11 +151,6 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) if (!sess) return; - if (!atomic_dec_and_test(&sess->refcnt)) - return; - - list_del(&sess->sessions_entry); - down_write(&sessions_table_lock); hash_del(&sess->hlist); up_write(&sessions_table_lock); @@ -181,53 +178,70 @@ static struct ksmbd_session *__session_lookup(unsigned long long id) return NULL; } -void ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess) +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { - sess->conn = conn; - list_add(&sess->sessions_entry, &conn->sessions); + sess->dialect = conn->dialect; + memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); } -void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { - struct ksmbd_session *sess; - - while (!list_empty(&conn->sessions)) { - sess = list_entry(conn->sessions.next, - struct ksmbd_session, - sessions_entry); + struct channel *chann, *tmp; - ksmbd_session_destroy(sess); + write_lock(&sess->chann_lock); + list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, + chann_list) { + if (chann->conn == conn) { + list_del(&chann->chann_list); + kfree(chann); + write_unlock(&sess->chann_lock); + return 0; + } } -} + write_unlock(&sess->chann_lock); -static bool ksmbd_session_id_match(struct ksmbd_session *sess, - unsigned long long id) -{ - return sess->id == id; + return -ENOENT; } -struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, - unsigned long long id) +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) { - struct ksmbd_session *sess = NULL; + struct ksmbd_session *sess; - list_for_each_entry(sess, &conn->sessions, sessions_entry) { - if (ksmbd_session_id_match(sess, id)) - return sess; + if (conn->binding) { + int bkt; + + down_write(&sessions_table_lock); + hash_for_each(sessions_table, bkt, sess, hlist) { + if (!ksmbd_chann_del(conn, sess)) { + up_write(&sessions_table_lock); + goto sess_destroy; + } + } + up_write(&sessions_table_lock); + } else { + unsigned long id; + + xa_for_each(&conn->sessions, id, sess) { + if (!ksmbd_chann_del(conn, sess)) + goto sess_destroy; + } } - return NULL; -} -int get_session(struct ksmbd_session *sess) -{ - return atomic_inc_not_zero(&sess->refcnt); + return; + +sess_destroy: + if (list_empty(&sess->ksmbd_chann_list)) { + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + } } -void put_session(struct ksmbd_session *sess) +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) { - if (atomic_dec_and_test(&sess->refcnt)) - pr_err("get/%s seems to be mismatched.", __func__); + return xa_load(&conn->sessions, id); } struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) @@ -236,10 +250,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) down_read(&sessions_table_lock); sess = __session_lookup(id); - if (sess) { - if (!get_session(sess)) - sess = NULL; - } up_read(&sessions_table_lock); return sess; @@ -253,6 +263,8 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, sess = ksmbd_session_lookup(conn, id); if (!sess && conn->binding) sess = ksmbd_session_lookup_slowpath(id); + if (sess && sess->state != SMB2_SESSION_VALID) + sess = NULL; return sess; } @@ -314,12 +326,11 @@ static struct ksmbd_session *__session_create(int protocol) goto error; set_session_flag(sess, protocol); - INIT_LIST_HEAD(&sess->sessions_entry); xa_init(&sess->tree_conns); INIT_LIST_HEAD(&sess->ksmbd_chann_list); INIT_LIST_HEAD(&sess->rpc_handle_list); sess->sequence_number = 1; - atomic_set(&sess->refcnt, 1); + rwlock_init(&sess->chann_lock); switch (protocol) { case CIFDS_SESSION_FLAG_SMB2: diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h index e241f16a3851..8934b8ee275b 100644 --- a/fs/ksmbd/mgmt/user_session.h +++ b/fs/ksmbd/mgmt/user_session.h @@ -33,8 +33,10 @@ struct preauth_session { struct ksmbd_session { u64 id; + __u16 dialect; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + struct ksmbd_user *user; - struct ksmbd_conn *conn; unsigned int sequence_number; unsigned int flags; @@ -48,6 +50,7 @@ struct ksmbd_session { char sess_key[CIFS_KEY_SIZE]; struct hlist_node hlist; + rwlock_t chann_lock; struct list_head ksmbd_chann_list; struct xarray tree_conns; struct ida tree_conn_ida; @@ -57,9 +60,7 @@ struct ksmbd_session { __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - struct list_head sessions_entry; struct ksmbd_file_table file_table; - atomic_t refcnt; }; static inline int test_session_flag(struct ksmbd_session *sess, int bit) @@ -84,8 +85,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess); struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, unsigned long long id); -void ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess); +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); void ksmbd_sessions_deregister(struct ksmbd_conn *conn); struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, unsigned long long id); @@ -100,6 +101,4 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); -int get_session(struct ksmbd_session *sess); -void put_session(struct ksmbd_session *sess); #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 8b5560574d4c..9046cff4374b 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -30,6 +30,7 @@ static DEFINE_RWLOCK(lease_list_lock); static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, u64 id, __u16 Tid) { + struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; @@ -38,7 +39,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, return NULL; opinfo->sess = sess; - opinfo->conn = sess->conn; + opinfo->conn = conn; opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; opinfo->pending_break = 0; @@ -615,18 +616,13 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) struct ksmbd_file *fp; fp = ksmbd_lookup_durable_fd(br_info->fid); - if (!fp) { - atomic_dec(&conn->r_count); - ksmbd_free_work_struct(work); - return; - } + if (!fp) + goto out; if (allocate_oplock_break_buf(work)) { pr_err("smb2_allocate_rsp_buf failed! "); - atomic_dec(&conn->r_count); ksmbd_fd_put(work, fp); - ksmbd_free_work_struct(work); - return; + goto out; } rsp_hdr = smb2_get_msg(work->response_buf); @@ -667,8 +663,16 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) ksmbd_fd_put(work, fp); ksmbd_conn_write(work); + +out: ksmbd_free_work_struct(work); - atomic_dec(&conn->r_count); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); } /** @@ -731,9 +735,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) if (allocate_oplock_break_buf(work)) { ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); - ksmbd_free_work_struct(work); - atomic_dec(&conn->r_count); - return; + goto out; } rsp_hdr = smb2_get_msg(work->response_buf); @@ -771,8 +773,16 @@ static void __smb2_lease_break_noti(struct work_struct *wk) inc_rfc1001_len(work->response_buf, 44); ksmbd_conn_write(work); + +out: ksmbd_free_work_struct(work); - atomic_dec(&conn->r_count); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); } /** @@ -972,7 +982,7 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, } list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, sess->conn->ClientGUID, + if (!memcmp(lb->client_guid, sess->ClientGUID, SMB2_CLIENT_GUID_SIZE)) goto found; } @@ -988,7 +998,7 @@ found: rcu_read_unlock(); if (opinfo->o_fp->f_ci == ci) goto op_next; - err = compare_guid_key(opinfo, sess->conn->ClientGUID, + err = compare_guid_key(opinfo, sess->ClientGUID, lctx->lease_key); if (err) { err = -EINVAL; @@ -1122,7 +1132,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct oplock_info *m_opinfo; /* is lease already granted ? */ - m_opinfo = same_client_has_lease(ci, sess->conn->ClientGUID, + m_opinfo = same_client_has_lease(ci, sess->ClientGUID, lctx); if (m_opinfo) { copy_lease(m_opinfo, opinfo); @@ -1240,7 +1250,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; - struct ksmbd_conn *conn = work->sess->conn; + struct ksmbd_conn *conn = work->conn; if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index 4cd03d661df0..ce42bff42ef9 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -261,7 +261,13 @@ static void handle_ksmbd_work(struct work_struct *wk) ksmbd_conn_try_dequeue_request(work); ksmbd_free_work_struct(work); - atomic_dec(&conn->r_count); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); } /** diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index f8f456377a51..6e25ace36568 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -90,11 +90,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, *off = 0; *len = 0; - /* error reqeusts do not have data area */ - if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && - (((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE) - return ret; - /* * Following commands have data areas so we have to get the location * of the data buffer offset and data buffer length for the particular @@ -136,8 +131,11 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); break; case SMB2_WRITE: - if (((struct smb2_write_req *)hdr)->DataOffset) { - *off = le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset); + if (((struct smb2_write_req *)hdr)->DataOffset || + ((struct smb2_write_req *)hdr)->Length) { + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), + offsetof(struct smb2_write_req, Buffer)); *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); break; } diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 353f047e783c..9751cc92c111 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -535,9 +535,10 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) struct smb2_query_info_req *req; req = smb2_get_msg(work->request_buf); - if (req->InfoType == SMB2_O_INFO_FILE && - (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) + if ((req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) || + req->InfoType == SMB2_O_INFO_SECURITY) sz = large_sz; } @@ -588,10 +589,12 @@ int smb2_check_user_session(struct ksmbd_work *work) return -EINVAL; } -static void destroy_previous_session(struct ksmbd_user *user, u64 id) +static void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id) { struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); struct ksmbd_user *prev_user; + struct channel *chann; if (!prev_sess) return; @@ -601,13 +604,14 @@ static void destroy_previous_session(struct ksmbd_user *user, u64 id) if (!prev_user || strcmp(user->name, prev_user->name) || user->passkey_sz != prev_user->passkey_sz || - memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) { - put_session(prev_sess); + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) return; - } - put_session(prev_sess); - ksmbd_session_destroy(prev_sess); + prev_sess->state = SMB2_SESSION_EXPIRED; + write_lock(&prev_sess->chann_lock); + list_for_each_entry(chann, &prev_sess->ksmbd_chann_list, chann_list) + chann->conn->status = KSMBD_SESS_EXITING; + write_unlock(&prev_sess->chann_lock); } /** @@ -1139,12 +1143,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work) status); rsp->hdr.Status = status; rc = -EINVAL; + kfree(conn->preauth_info); + conn->preauth_info = NULL; goto err_out; } rc = init_smb3_11_server(conn); if (rc < 0) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; + kfree(conn->preauth_info); + conn->preauth_info = NULL; goto err_out; } @@ -1439,7 +1447,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) /* Check for previous session */ prev_id = le64_to_cpu(req->PreviousSessionId); if (prev_id && prev_id != sess->id) - destroy_previous_session(user, prev_id); + destroy_previous_session(conn, user, prev_id); if (sess->state == SMB2_SESSION_VALID) { /* @@ -1493,7 +1501,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) if (smb3_encryption_negotiated(conn) && !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(sess); + rc = conn->ops->generate_encryptionkey(conn, sess); if (rc) { ksmbd_debug(SMB, "SMB3 encryption key generation failed\n"); @@ -1510,7 +1518,9 @@ static int ntlm_authenticate(struct ksmbd_work *work) binding_session: if (conn->dialect >= SMB30_PROT_ID) { + read_lock(&sess->chann_lock); chann = lookup_chann_list(sess, conn); + read_unlock(&sess->chann_lock); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) @@ -1518,7 +1528,9 @@ binding_session: chann->conn = conn; INIT_LIST_HEAD(&chann->chann_list); + write_lock(&sess->chann_lock); list_add(&chann->chann_list, &sess->ksmbd_chann_list); + write_unlock(&sess->chann_lock); } } @@ -1561,7 +1573,7 @@ static int krb5_authenticate(struct ksmbd_work *work) /* Check previous session */ prev_sess_id = le64_to_cpu(req->PreviousSessionId); if (prev_sess_id && prev_sess_id != sess->id) - destroy_previous_session(sess->user, prev_sess_id); + destroy_previous_session(conn, sess->user, prev_sess_id); if (sess->state == SMB2_SESSION_VALID) ksmbd_free_user(sess->user); @@ -1580,7 +1592,7 @@ static int krb5_authenticate(struct ksmbd_work *work) sess->sign = true; if (smb3_encryption_negotiated(conn)) { - retval = conn->ops->generate_encryptionkey(sess); + retval = conn->ops->generate_encryptionkey(conn, sess); if (retval) { ksmbd_debug(SMB, "SMB3 encryption key generation failed\n"); @@ -1592,7 +1604,9 @@ static int krb5_authenticate(struct ksmbd_work *work) } if (conn->dialect >= SMB30_PROT_ID) { + read_lock(&sess->chann_lock); chann = lookup_chann_list(sess, conn); + read_unlock(&sess->chann_lock); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) @@ -1600,7 +1614,9 @@ static int krb5_authenticate(struct ksmbd_work *work) chann->conn = conn; INIT_LIST_HEAD(&chann->chann_list); + write_lock(&sess->chann_lock); list_add(&chann->chann_list, &sess->ksmbd_chann_list); + write_unlock(&sess->chann_lock); } } @@ -1650,7 +1666,9 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } rsp->hdr.SessionId = cpu_to_le64(sess->id); - ksmbd_session_register(conn, sess); + rc = ksmbd_session_register(conn, sess); + if (rc) + goto out_err; } else if (conn->dialect >= SMB30_PROT_ID && (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { @@ -1662,7 +1680,7 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } - if (conn->dialect != sess->conn->dialect) { + if (conn->dialect != sess->dialect) { rc = -EINVAL; goto out_err; } @@ -1672,7 +1690,7 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } - if (strncmp(conn->ClientGUID, sess->conn->ClientGUID, + if (strncmp(conn->ClientGUID, sess->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { rc = -ENOENT; goto out_err; @@ -1828,6 +1846,7 @@ out_err: if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) try_delay = true; + xa_erase(&conn->sessions, sess->id); ksmbd_session_destroy(sess); work->sess = NULL; if (try_delay) @@ -1873,7 +1892,7 @@ int smb2_tree_connect(struct ksmbd_work *work) ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", name, treename); - status = ksmbd_tree_conn_connect(sess, name); + status = ksmbd_tree_conn_connect(conn, sess, name); if (status.ret == KSMBD_TREE_CONN_STATUS_OK) rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); else @@ -2039,6 +2058,7 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_close_tree_conn_fds(work); ksmbd_tree_conn_disconnect(sess, tcon); + work->tcon = NULL; return 0; } @@ -2969,7 +2989,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out; rc = build_sec_desc(user_ns, - pntsd, NULL, + pntsd, NULL, 0, OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO, @@ -3814,6 +3834,15 @@ static int verify_info_level(int info_level) return 0; } +static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) +{ + int free_len; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; + return free_len; +} + static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, unsigned short hdr2_len, unsigned int out_buf_len) @@ -3823,9 +3852,7 @@ static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, if (out_buf_len > work->conn->vals->max_trans_size) return -EINVAL; - free_len = (int)(work->response_sz - - (get_rfc1002_len(work->response_buf) + 4)) - - hdr2_len; + free_len = smb2_resp_buf_len(work, hdr2_len); if (free_len < 0) return -EINVAL; @@ -4858,7 +4885,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct smb2_query_info_rsp *rsp) { struct ksmbd_session *sess = work->sess; - struct ksmbd_conn *conn = sess->conn; + struct ksmbd_conn *conn = work->conn; struct ksmbd_share_config *share = work->tcon->share_conf; int fsinfoclass = 0; struct kstatfs stfs; @@ -5088,10 +5115,10 @@ static int smb2_get_info_sec(struct ksmbd_work *work, struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; struct smb_fattr fattr = {{0}}; struct inode *inode; - __u32 secdesclen; + __u32 secdesclen = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; int addition_info = le32_to_cpu(req->AdditionalInformation); - int rc; + int rc = 0, ppntsd_size = 0; if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | PROTECTED_DACL_SECINFO | @@ -5137,11 +5164,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) - ksmbd_vfs_get_sd_xattr(work->conn, user_ns, - fp->filp->f_path.dentry, &ppntsd); - - rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info, - &secdesclen, &fattr); + ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, + fp->filp->f_path.dentry, + &ppntsd); + + /* Check if sd buffer size exceeds response buffer size */ + if (smb2_resp_buf_len(work, 8) > ppntsd_size) + rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); kfree(ppntsd); @@ -5776,7 +5806,7 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, } next: return smb2_rename(work, fp, user_ns, rename_info, - work->sess->conn->local_nls); + work->conn->local_nls); } static int set_file_disposition_info(struct ksmbd_file *fp, @@ -5908,7 +5938,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, return smb2_create_link(work, work->tcon->share_conf, (struct smb2_file_link_info *)req->Buffer, buf_len, fp->filp, - work->sess->conn->local_nls); + work->conn->local_nls); } case FILE_DISPOSITION_INFORMATION: { @@ -6495,14 +6525,12 @@ int smb2_write(struct ksmbd_work *work) writethrough = true; if (is_rdma_channel == false) { - if ((u64)le16_to_cpu(req->DataOffset) + length > - get_rfc1002_len(work->request_buf)) { - pr_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(work->request_buf)); + if (le16_to_cpu(req->DataOffset) < + offsetof(struct smb2_write_req, Buffer)) { err = -EINVAL; goto out; } + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + le16_to_cpu(req->DataOffset)); @@ -8356,10 +8384,14 @@ int smb3_check_sign_req(struct ksmbd_work *work) if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; } else { + read_lock(&work->sess->chann_lock); chann = lookup_chann_list(work->sess, conn); - if (!chann) + if (!chann) { + read_unlock(&work->sess->chann_lock); return 0; + } signing_key = chann->smb3signingkey; + read_unlock(&work->sess->chann_lock); } if (!signing_key) { @@ -8419,10 +8451,14 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; } else { + read_lock(&work->sess->chann_lock); chann = lookup_chann_list(work->sess, work->conn); - if (!chann) + if (!chann) { + read_unlock(&work->sess->chann_lock); return; + } signing_key = chann->smb3signingkey; + read_unlock(&work->sess->chann_lock); } if (!signing_key) diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index e1369b4345a9..318c16fa81da 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -421,7 +421,7 @@ struct smb_version_ops { int (*check_sign_req)(struct ksmbd_work *work); void (*set_sign_rsp)(struct ksmbd_work *work); int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); - int (*generate_encryptionkey)(struct ksmbd_session *sess); + int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); bool (*is_transform_hdr)(void *buf); int (*decrypt_req)(struct ksmbd_work *work); int (*encrypt_resp)(struct ksmbd_work *work); diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 38f23bf981ac..3781bca2c8fc 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -690,6 +690,7 @@ posix_default_acl: static void set_ntacl_dacl(struct user_namespace *user_ns, struct smb_acl *pndacl, struct smb_acl *nt_dacl, + unsigned int aces_size, const struct smb_sid *pownersid, const struct smb_sid *pgrpsid, struct smb_fattr *fattr) @@ -703,9 +704,19 @@ static void set_ntacl_dacl(struct user_namespace *user_ns, if (nt_num_aces) { ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); for (i = 0; i < nt_num_aces; i++) { - memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size)); - size += le16_to_cpu(ntace->size); - ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); + unsigned short nt_ace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + nt_ace_size = le16_to_cpu(ntace->size); + if (nt_ace_size > aces_size) + break; + + memcpy((char *)pndace + size, ntace, nt_ace_size); + size += nt_ace_size; + aces_size -= nt_ace_size; + ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); num_aces++; } } @@ -878,7 +889,7 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, /* Convert permission bits from mode to equivalent CIFS ACL */ int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int addition_info, __u32 *secdesclen, + int ppntsd_size, int addition_info, __u32 *secdesclen, struct smb_fattr *fattr) { int rc = 0; @@ -938,15 +949,25 @@ int build_sec_desc(struct user_namespace *user_ns, if (!ppntsd) { set_mode_dacl(user_ns, dacl_ptr, fattr); - } else if (!ppntsd->dacloffset) { - goto out; } else { struct smb_acl *ppdacl_ptr; + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; + + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) + goto out; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); + ppdacl_size = le16_to_cpu(ppdacl_ptr->size); + if (ppdacl_size > ntacl_size || + ppdacl_size < sizeof(struct smb_acl)) + goto out; - ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + - le32_to_cpu(ppntsd->dacloffset)); set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, - nowner_sid_ptr, ngroup_sid_ptr, fattr); + ntacl_size - sizeof(struct smb_acl), + nowner_sid_ptr, ngroup_sid_ptr, + fattr); } pntsd->dacloffset = cpu_to_le32(offset); offset += le16_to_cpu(dacl_ptr->size); @@ -980,24 +1001,31 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct smb_sid owner_sid, group_sid; struct dentry *parent = path->dentry->d_parent; struct user_namespace *user_ns = mnt_user_ns(path->mnt); - int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; - int rc = 0, num_aces, dacloffset, pntsd_type, acl_len; + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; + int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns, - parent, &parent_pntsd); - if (acl_len <= 0) + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + parent, &parent_pntsd); + if (pntsd_size <= 0) return -ENOENT; dacloffset = le32_to_cpu(parent_pntsd->dacloffset); - if (!dacloffset) { + if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { rc = -EINVAL; goto free_parent_pntsd; } parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + acl_len = pntsd_size - dacloffset; num_aces = le32_to_cpu(parent_pdacl->num_aces); pntsd_type = le16_to_cpu(parent_pntsd->type); + pdacl_size = le16_to_cpu(parent_pdacl->size); + + if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { + rc = -EINVAL; + goto free_parent_pntsd; + } aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); if (!aces_base) { @@ -1008,11 +1036,23 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, aces = (struct smb_ace *)aces_base; parent_aces = (struct smb_ace *)((char *)parent_pdacl + sizeof(struct smb_acl)); + aces_size = acl_len - sizeof(struct smb_acl); if (pntsd_type & DACL_AUTO_INHERITED) inherited_flags = INHERITED_ACE; for (i = 0; i < num_aces; i++) { + int pace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + pace_size = le16_to_cpu(parent_aces->size); + if (pace_size > aces_size) + break; + + aces_size -= pace_size; + flags = parent_aces->flags; if (!smb_inherit_flags(flags, is_dir)) goto pass; @@ -1057,8 +1097,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); ace_cnt++; pass: - parent_aces = - (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size)); + parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); } if (nt_size > 0) { @@ -1153,7 +1192,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, struct smb_ntsd *pntsd = NULL; struct smb_acl *pdacl; struct posix_acl *posix_acls; - int rc = 0, acl_size; + int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; struct smb_sid sid; int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); struct smb_ace *ace; @@ -1162,37 +1201,33 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, struct smb_ace *others_ace = NULL; struct posix_acl_entry *pa_entry; unsigned int sid_type = SIDOWNER; - char *end_of_acl; + unsigned short ace_size; ksmbd_debug(SMB, "check permission using windows acl\n"); - acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, - path->dentry, &pntsd); - if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { - kfree(pntsd); - return 0; - } + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + path->dentry, &pntsd); + if (pntsd_size <= 0 || !pntsd) + goto err_out; + + dacl_offset = le32_to_cpu(pntsd->dacloffset); + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) + goto err_out; pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); - end_of_acl = ((char *)pntsd) + acl_size; - if (end_of_acl <= (char *)pdacl) { - kfree(pntsd); - return 0; - } + acl_size = pntsd_size - dacl_offset; + pdacl_size = le16_to_cpu(pdacl->size); - if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) || - le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) { - kfree(pntsd); - return 0; - } + if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) + goto err_out; if (!pdacl->num_aces) { - if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && + if (!(pdacl_size - sizeof(struct smb_acl)) && *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { rc = -EACCES; goto err_out; } - kfree(pntsd); - return 0; + goto err_out; } if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { @@ -1200,11 +1235,16 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, DELETE; ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; granted |= le32_to_cpu(ace->access_req); ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - if (end_of_acl < (char *)ace) - goto err_out; } if (!pdacl->num_aces) @@ -1216,7 +1256,15 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, id_to_sid(uid, sid_type, &sid); ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + if (!compare_sids(&sid, &ace->sid) || !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { found = 1; @@ -1226,8 +1274,6 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, others_ace = ace; ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - if (end_of_acl < (char *)ace) - goto err_out; } if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h index 811af3309429..fcb2c83f2992 100644 --- a/fs/ksmbd/smbacl.h +++ b/fs/ksmbd/smbacl.h @@ -193,7 +193,7 @@ struct posix_acl_state { int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, int acl_len, struct smb_fattr *fattr); int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - struct smb_ntsd *ppntsd, int addition_info, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, __u32 *secdesclen, struct smb_fattr *fattr); int init_acl_state(struct posix_acl_state *state, int cnt); void free_acl_state(struct posix_acl_state *state); diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 7c849024999f..78d01033604c 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -481,12 +481,11 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written) { - struct ksmbd_session *sess = work->sess; struct file *filp; loff_t offset = *pos; int err = 0; - if (sess->conn->connection_type) { + if (work->conn->connection_type) { if (!(fp->daccess & FILE_WRITE_DATA_LE)) { pr_err("no right to write(%pd)\n", fp->filp->f_path.dentry); @@ -1540,6 +1539,11 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, } *pntsd = acl.sd_buf; + if (acl.sd_size < sizeof(struct smb_ntsd)) { + pr_err("sd size is invalid\n"); + goto out_free; + } + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - NDR_NTSD_OFFSETOF); (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c index c4d59d2735f0..da9163b00350 100644 --- a/fs/ksmbd/vfs_cache.c +++ b/fs/ksmbd/vfs_cache.c @@ -569,7 +569,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) atomic_set(&fp->refcount, 1); fp->filp = filp; - fp->conn = work->sess->conn; + fp->conn = work->conn; fp->tcon = work->tcon; fp->volatile_id = KSMBD_NO_FID; fp->persistent_id = KSMBD_NO_FID; diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 176b468a61c7..bf274f23969b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -32,6 +32,10 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, if (!nlmsvc_ops) return nlm_lck_denied_nolocks; + if (lock->lock_start > OFFSET_MAX || + (lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start)))) + return nlm4_fbig; + /* Obtain host handle */ if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) || (argp->monitor && nsm_monitor(host) < 0)) @@ -50,6 +54,10 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, /* Set up the missing parts of the file_lock structure */ lock->fl.fl_file = file->f_file[mode]; lock->fl.fl_pid = current->tgid; + lock->fl.fl_start = (loff_t)lock->lock_start; + lock->fl.fl_end = lock->lock_len ? + (loff_t)(lock->lock_start + lock->lock_len - 1) : + OFFSET_MAX; lock->fl.fl_lmops = &nlmsvc_lock_operations; nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); if (!lock->fl.fl_owner) { @@ -87,6 +95,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) struct nlm_args *argp = rqstp->rq_argp; struct nlm_host *host; struct nlm_file *file; + struct nlm_lockowner *test_owner; __be32 rc = rpc_success; dprintk("lockd: TEST4 called\n"); @@ -96,6 +105,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + test_owner = argp->lock.fl.fl_owner; /* Now check for conflicting locks */ resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); if (resp->status == nlm_drop_reply) @@ -103,7 +113,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) else dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); - nlmsvc_release_lockowner(&argp->lock); + nlmsvc_put_lockowner(test_owner); nlmsvc_release_host(host); nlm_release_file(file); return rc; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index cb3658ab9b7a..9c1aa75441e1 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -340,7 +340,7 @@ nlmsvc_get_lockowner(struct nlm_lockowner *lockowner) return lockowner; } -static void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner) +void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner) { if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) return; @@ -590,7 +590,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, int error; int mode; __be32 ret; - struct nlm_lockowner *test_owner; dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", nlmsvc_file_inode(file)->i_sb->s_id, @@ -604,9 +603,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } - /* If there's a conflicting lock, remember to clean up the test lock */ - test_owner = (struct nlm_lockowner *)lock->fl.fl_owner; - mode = lock_to_openmode(&lock->fl); error = vfs_test_lock(file->f_file[mode], &lock->fl); if (error) { @@ -635,10 +631,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, conflock->fl.fl_end = lock->fl.fl_end; locks_release_private(&lock->fl); - /* Clean up the test lock */ - lock->fl.fl_owner = NULL; - nlmsvc_put_lockowner(test_owner); - ret = nlm_lck_denied; out: return ret; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 4dc1b40a489a..b09ca35b527c 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -116,6 +116,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) struct nlm_args *argp = rqstp->rq_argp; struct nlm_host *host; struct nlm_file *file; + struct nlm_lockowner *test_owner; __be32 rc = rpc_success; dprintk("lockd: TEST called\n"); @@ -125,6 +126,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + test_owner = argp->lock.fl.fl_owner; + /* Now check for conflicting locks */ resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); if (resp->status == nlm_drop_reply) @@ -133,7 +136,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) dprintk("lockd: TEST status %d vers %d\n", ntohl(resp->status), rqstp->rq_vers); - nlmsvc_release_lockowner(&argp->lock); + nlmsvc_put_lockowner(test_owner); nlmsvc_release_host(host); nlm_release_file(file); return rc; diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 856267c0864b..712fdfeb8ef0 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -20,13 +20,6 @@ #include "svcxdr.h" -static inline loff_t -s64_to_loff_t(__s64 offset) -{ - return (loff_t)offset; -} - - static inline s64 loff_t_to_s64(loff_t offset) { @@ -70,8 +63,6 @@ static bool svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) { struct file_lock *fl = &lock->fl; - u64 len, start; - s64 end; if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) return false; @@ -81,20 +72,14 @@ svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) return false; if (xdr_stream_decode_u32(xdr, &lock->svid) < 0) return false; - if (xdr_stream_decode_u64(xdr, &start) < 0) + if (xdr_stream_decode_u64(xdr, &lock->lock_start) < 0) return false; - if (xdr_stream_decode_u64(xdr, &len) < 0) + if (xdr_stream_decode_u64(xdr, &lock->lock_len) < 0) return false; locks_init_lock(fl); fl->fl_flags = FL_POSIX; fl->fl_type = F_RDLCK; - end = start + len - 1; - fl->fl_start = s64_to_loff_t(start); - if (len == 0 || end < 0) - fl->fl_end = OFFSET_MAX; - else - fl->fl_end = s64_to_loff_t(end); return true; } diff --git a/fs/namei.c b/fs/namei.c index ed3ffd9b22a3..53b4bc094db2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3024,6 +3024,65 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) EXPORT_SYMBOL(unlock_rename); /** + * mode_strip_umask - handle vfs umask stripping + * @dir: parent directory of the new inode + * @mode: mode of the new inode to be created in @dir + * + * Umask stripping depends on whether or not the filesystem supports POSIX + * ACLs. If the filesystem doesn't support it umask stripping is done directly + * in here. If the filesystem does support POSIX ACLs umask stripping is + * deferred until the filesystem calls posix_acl_create(). + * + * Returns: mode + */ +static inline umode_t mode_strip_umask(const struct inode *dir, umode_t mode) +{ + if (!IS_POSIXACL(dir)) + mode &= ~current_umask(); + return mode; +} + +/** + * vfs_prepare_mode - prepare the mode to be used for a new inode + * @mnt_userns: user namespace of the mount the inode was found from + * @dir: parent directory of the new inode + * @mode: mode of the new inode + * @mask_perms: allowed permission by the vfs + * @type: type of file to be created + * + * This helper consolidates and enforces vfs restrictions on the @mode of a new + * object to be created. + * + * Umask stripping depends on whether the filesystem supports POSIX ACLs (see + * the kernel documentation for mode_strip_umask()). Moving umask stripping + * after setgid stripping allows the same ordering for both non-POSIX ACL and + * POSIX ACL supporting filesystems. + * + * Note that it's currently valid for @type to be 0 if a directory is created. + * Filesystems raise that flag individually and we need to check whether each + * filesystem can deal with receiving S_IFDIR from the vfs before we enforce a + * non-zero type. + * + * Returns: mode to be passed to the filesystem + */ +static inline umode_t vfs_prepare_mode(struct user_namespace *mnt_userns, + const struct inode *dir, umode_t mode, + umode_t mask_perms, umode_t type) +{ + mode = mode_strip_sgid(mnt_userns, dir, mode); + mode = mode_strip_umask(dir, mode); + + /* + * Apply the vfs mandated allowed permission mask and set the type of + * file to be created before we call into the filesystem. + */ + mode &= (mask_perms & ~S_IFMT); + mode |= (type & S_IFMT); + + return mode; +} + +/** * vfs_create - create new file * @mnt_userns: user namespace of the mount the inode was found from * @dir: inode of @dentry @@ -3048,8 +3107,8 @@ int vfs_create(struct user_namespace *mnt_userns, struct inode *dir, if (!dir->i_op->create) return -EACCES; /* shouldn't it be ENOSYS? */ - mode &= S_IALLUGO; - mode |= S_IFREG; + + mode = vfs_prepare_mode(mnt_userns, dir, mode, S_IALLUGO, S_IFREG); error = security_inode_create(dir, dentry, mode); if (error) return error; @@ -3312,8 +3371,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (open_flag & O_CREAT) { if (open_flag & O_EXCL) open_flag &= ~O_TRUNC; - if (!IS_POSIXACL(dir->d_inode)) - mode &= ~current_umask(); + mode = vfs_prepare_mode(mnt_userns, dir->d_inode, mode, mode, mode); if (likely(got_write)) create_error = may_o_create(mnt_userns, &nd->path, dentry, mode); @@ -3544,6 +3602,7 @@ struct dentry *vfs_tmpfile(struct user_namespace *mnt_userns, child = d_alloc(dentry, &slash_name); if (unlikely(!child)) goto out_err; + mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode); error = dir->i_op->tmpfile(mnt_userns, dir, child, mode); if (error) goto out_err; @@ -3821,6 +3880,7 @@ int vfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (!dir->i_op->mknod) return -EPERM; + mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode); error = devcgroup_inode_mknod(mode, dev); if (error) return error; @@ -3871,9 +3931,8 @@ retry: if (IS_ERR(dentry)) goto out1; - if (!IS_POSIXACL(path.dentry->d_inode)) - mode &= ~current_umask(); - error = security_path_mknod(&path, dentry, mode, dev); + error = security_path_mknod(&path, dentry, + mode_strip_umask(path.dentry->d_inode, mode), dev); if (error) goto out2; @@ -3943,7 +4002,7 @@ int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, if (!dir->i_op->mkdir) return -EPERM; - mode &= (S_IRWXUGO|S_ISVTX); + mode = vfs_prepare_mode(mnt_userns, dir, mode, S_IRWXUGO | S_ISVTX, 0); error = security_inode_mkdir(dir, dentry, mode); if (error) return error; @@ -3971,9 +4030,8 @@ retry: if (IS_ERR(dentry)) goto out_putname; - if (!IS_POSIXACL(path.dentry->d_inode)) - mode &= ~current_umask(); - error = security_path_mkdir(&path, dentry, mode); + error = security_path_mkdir(&path, dentry, + mode_strip_umask(path.dentry->d_inode, mode)); if (!error) { struct user_namespace *mnt_userns; mnt_userns = mnt_user_ns(path.mnt); diff --git a/fs/nfs/blocklayout/dev.c b/fs/nfs/blocklayout/dev.c index 5e56da748b2a..fea5f8821da5 100644 --- a/fs/nfs/blocklayout/dev.c +++ b/fs/nfs/blocklayout/dev.c @@ -301,18 +301,14 @@ bl_validate_designator(struct pnfs_block_volume *v) } } -/* - * Try to open the udev path for the WWN. At least on Debian the udev - * by-id path will always point to the dm-multipath device if one exists. - */ static struct block_device * -bl_open_udev_path(struct pnfs_block_volume *v) +bl_open_path(struct pnfs_block_volume *v, const char *prefix) { struct block_device *bdev; const char *devname; - devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%*phN", - v->scsi.designator_len, v->scsi.designator); + devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/%s%*phN", + prefix, v->scsi.designator_len, v->scsi.designator); if (!devname) return ERR_PTR(-ENOMEM); @@ -326,28 +322,6 @@ bl_open_udev_path(struct pnfs_block_volume *v) return bdev; } -/* - * Try to open the RH/Fedora specific dm-mpath udev path for this WWN, as the - * wwn- links will only point to the first discovered SCSI device there. - */ -static struct block_device * -bl_open_dm_mpath_udev_path(struct pnfs_block_volume *v) -{ - struct block_device *bdev; - const char *devname; - - devname = kasprintf(GFP_KERNEL, - "/dev/disk/by-id/dm-uuid-mpath-%d%*phN", - v->scsi.designator_type, - v->scsi.designator_len, v->scsi.designator); - if (!devname) - return ERR_PTR(-ENOMEM); - - bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL); - kfree(devname); - return bdev; -} - static int bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) @@ -360,9 +334,15 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, if (!bl_validate_designator(v)) return -EINVAL; - bdev = bl_open_dm_mpath_udev_path(v); + /* + * Try to open the RH/Fedora specific dm-mpath udev path first, as the + * wwn- links will only point to the first discovered SCSI device there. + * On other distributions like Debian, the default SCSI by-id path will + * point to the dm-multipath device if one exists. + */ + bdev = bl_open_path(v, "dm-uuid-mpath-0x"); if (IS_ERR(bdev)) - bdev = bl_open_udev_path(v); + bdev = bl_open_path(v, "wwn-0x"); if (IS_ERR(bdev)) return PTR_ERR(bdev); d->bdev = bdev; diff --git a/fs/nfs/client.c b/fs/nfs/client.c index e828504cc396..da8da5cdbbc1 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -708,9 +708,9 @@ static int nfs_init_server(struct nfs_server *server, } if (ctx->rsize) - server->rsize = nfs_block_size(ctx->rsize, NULL); + server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto); if (ctx->wsize) - server->wsize = nfs_block_size(ctx->wsize, NULL); + server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto); server->acregmin = ctx->acregmin * HZ; server->acregmax = ctx->acregmax * HZ; @@ -755,18 +755,19 @@ error: static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) { + struct nfs_client *clp = server->nfs_client; unsigned long max_rpc_payload, raw_max_rpc_payload; /* Work out a lot of parameters */ if (server->rsize == 0) - server->rsize = nfs_block_size(fsinfo->rtpref, NULL); + server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto); if (server->wsize == 0) - server->wsize = nfs_block_size(fsinfo->wtpref, NULL); + server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto); if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) - server->rsize = nfs_block_size(fsinfo->rtmax, NULL); + server->rsize = nfs_io_size(fsinfo->rtmax, clp->cl_proto); if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) - server->wsize = nfs_block_size(fsinfo->wtmax, NULL); + server->wsize = nfs_io_size(fsinfo->wtmax, clp->cl_proto); raw_max_rpc_payload = rpc_max_payload(server->client); max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0c4e8dd6aa96..dbab3caa15ed 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1084,7 +1084,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc, struct nfs_cache_array *array; unsigned int i; - array = kmap(desc->page); + array = kmap_local_page(desc->page); for (i = desc->cache_entry_index; i < array->size; i++) { struct nfs_cache_array_entry *ent; @@ -1110,7 +1110,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc, if (array->page_is_eof) desc->eof = !desc->eob; - kunmap(desc->page); + kunmap_local(array); dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n", (unsigned long long)desc->dir_cookie); } @@ -1739,6 +1739,10 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, goto out_bad; } + if ((flags & LOOKUP_RENAME_TARGET) && d_count(dentry) < 2 && + nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE)) + goto out_bad; + if (nfs_verifier_is_delegated(dentry)) return nfs_lookup_revalidate_delegated(dir, dentry, inode); @@ -1778,6 +1782,8 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags, int ret; if (flags & LOOKUP_RCU) { + if (dentry->d_fsdata == NFS_FSDATA_BLOCKED) + return -ECHILD; parent = READ_ONCE(dentry->d_parent); dir = d_inode_rcu(parent); if (!dir) @@ -1786,6 +1792,9 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags, if (parent != READ_ONCE(dentry->d_parent)) return -ECHILD; } else { + /* Wait for unlink to complete */ + wait_var_event(&dentry->d_fsdata, + dentry->d_fsdata != NFS_FSDATA_BLOCKED); parent = dget_parent(dentry); ret = reval(d_inode(parent), dentry, flags); dput(parent); @@ -2454,7 +2463,6 @@ out: int nfs_unlink(struct inode *dir, struct dentry *dentry) { int error; - int need_rehash = 0; dfprintk(VFS, "NFS: unlink(%s/%lu, %pd)\n", dir->i_sb->s_id, dir->i_ino, dentry); @@ -2469,15 +2477,25 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry) error = nfs_sillyrename(dir, dentry); goto out; } - if (!d_unhashed(dentry)) { - __d_drop(dentry); - need_rehash = 1; - } + /* We must prevent any concurrent open until the unlink + * completes. ->d_revalidate will wait for ->d_fsdata + * to clear. We set it here to ensure no lookup succeeds until + * the unlink is complete on the server. + */ + error = -ETXTBSY; + if (WARN_ON(dentry->d_flags & DCACHE_NFSFS_RENAMED) || + WARN_ON(dentry->d_fsdata == NFS_FSDATA_BLOCKED)) + goto out; + if (dentry->d_fsdata) + /* old devname */ + kfree(dentry->d_fsdata); + dentry->d_fsdata = NFS_FSDATA_BLOCKED; + spin_unlock(&dentry->d_lock); error = nfs_safe_remove(dentry); nfs_dentry_remove_handle_error(dir, dentry, error); - if (need_rehash) - d_rehash(dentry); + dentry->d_fsdata = NULL; + wake_up_var(&dentry->d_fsdata); out: trace_nfs_unlink_exit(dir, dentry, error); return error; @@ -2584,6 +2602,15 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) } EXPORT_SYMBOL_GPL(nfs_link); +static void +nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data) +{ + struct dentry *new_dentry = data->new_dentry; + + new_dentry->d_fsdata = NULL; + wake_up_var(&new_dentry->d_fsdata); +} + /* * RENAME * FIXME: Some nfsds, like the Linux user space nfsd, may generate a @@ -2614,8 +2641,9 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, { struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); - struct dentry *dentry = NULL, *rehash = NULL; + struct dentry *dentry = NULL; struct rpc_task *task; + bool must_unblock = false; int error = -EBUSY; if (flags) @@ -2633,18 +2661,27 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, * the new target. */ if (new_inode && !S_ISDIR(new_inode->i_mode)) { - /* - * To prevent any new references to the target during the - * rename, we unhash the dentry in advance. + /* We must prevent any concurrent open until the unlink + * completes. ->d_revalidate will wait for ->d_fsdata + * to clear. We set it here to ensure no lookup succeeds until + * the unlink is complete on the server. */ - if (!d_unhashed(new_dentry)) { - d_drop(new_dentry); - rehash = new_dentry; + error = -ETXTBSY; + if (WARN_ON(new_dentry->d_flags & DCACHE_NFSFS_RENAMED) || + WARN_ON(new_dentry->d_fsdata == NFS_FSDATA_BLOCKED)) + goto out; + if (new_dentry->d_fsdata) { + /* old devname */ + kfree(new_dentry->d_fsdata); + new_dentry->d_fsdata = NULL; } + spin_lock(&new_dentry->d_lock); if (d_count(new_dentry) > 2) { int err; + spin_unlock(&new_dentry->d_lock); + /* copy the target dentry's name */ dentry = d_alloc(new_dentry->d_parent, &new_dentry->d_name); @@ -2657,14 +2694,19 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, goto out; new_dentry = dentry; - rehash = NULL; new_inode = NULL; + } else { + new_dentry->d_fsdata = NFS_FSDATA_BLOCKED; + must_unblock = true; + spin_unlock(&new_dentry->d_lock); } + } if (S_ISREG(old_inode->i_mode)) nfs_sync_inode(old_inode); - task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL); + task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, + must_unblock ? nfs_unblock_rename : NULL); if (IS_ERR(task)) { error = PTR_ERR(task); goto out; @@ -2688,8 +2730,6 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, spin_unlock(&old_inode->i_lock); } out: - if (rehash) - d_rehash(rehash); trace_nfs_rename_exit(old_dir, old_dentry, new_dir, new_dentry, error); if (!error) { diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 4eb2a8380a28..1707f46b1335 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -60,44 +60,12 @@ #include "iostat.h" #include "pnfs.h" #include "fscache.h" +#include "nfstrace.h" #define NFSDBG_FACILITY NFSDBG_VFS static struct kmem_cache *nfs_direct_cachep; -struct nfs_direct_req { - struct kref kref; /* release manager */ - - /* I/O parameters */ - struct nfs_open_context *ctx; /* file open context info */ - struct nfs_lock_context *l_ctx; /* Lock context info */ - struct kiocb * iocb; /* controlling i/o request */ - struct inode * inode; /* target file of i/o */ - - /* completion state */ - atomic_t io_count; /* i/os we're waiting for */ - spinlock_t lock; /* protect completion state */ - - loff_t io_start; /* Start offset for I/O */ - ssize_t count, /* bytes actually processed */ - max_count, /* max expected count */ - bytes_left, /* bytes left to be sent */ - error; /* any reported error */ - struct completion completion; /* wait for i/o completion */ - - /* commit state */ - struct nfs_mds_commit_info mds_cinfo; /* Storage for cinfo */ - struct pnfs_ds_commit_info ds_cinfo; /* Storage for cinfo */ - struct work_struct work; - int flags; - /* for write */ -#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ -#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */ - /* for read */ -#define NFS_ODIRECT_SHOULD_DIRTY (3) /* dirty user-space page after read */ -#define NFS_ODIRECT_DONE INT_MAX /* write verification failed */ -}; - static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops; static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops; static void nfs_direct_write_complete(struct nfs_direct_req *dreq); @@ -364,13 +332,12 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, size_t pgbase; unsigned npages, i; - result = iov_iter_get_pages_alloc(iter, &pagevec, + result = iov_iter_get_pages_alloc2(iter, &pagevec, rsize, &pgbase); if (result < 0) break; bytes = result; - iov_iter_advance(iter, bytes); npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE; for (i = 0; i < npages; i++) { struct nfs_page *req; @@ -478,7 +445,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - if (iter_is_iovec(iter)) + if (user_backed_iter(iter)) dreq->flags = NFS_ODIRECT_SHOULD_DIRTY; if (!swap) @@ -595,14 +562,17 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data) struct nfs_page *req; int status = data->task.tk_status; + trace_nfs_direct_commit_complete(dreq); + if (status < 0) { /* Errors in commit are fatal */ dreq->error = status; dreq->max_count = 0; dreq->count = 0; dreq->flags = NFS_ODIRECT_DONE; - } else if (dreq->flags == NFS_ODIRECT_DONE) + } else { status = dreq->error; + } nfs_init_cinfo_from_dreq(&cinfo, dreq); @@ -631,6 +601,8 @@ static void nfs_direct_resched_write(struct nfs_commit_info *cinfo, { struct nfs_direct_req *dreq = cinfo->dreq; + trace_nfs_direct_resched_write(dreq); + spin_lock(&dreq->lock); if (dreq->flags != NFS_ODIRECT_DONE) dreq->flags = NFS_ODIRECT_RESCHED_WRITES; @@ -695,6 +667,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work) static void nfs_direct_write_complete(struct nfs_direct_req *dreq) { + trace_nfs_direct_write_complete(dreq); queue_work(nfsiod_workqueue, &dreq->work); /* Calls nfs_direct_write_schedule_work */ } @@ -705,6 +678,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) struct nfs_page *req = nfs_list_entry(hdr->pages.next); int flags = NFS_ODIRECT_DONE; + trace_nfs_direct_write_completion(dreq); + nfs_init_cinfo_from_dreq(&cinfo, dreq); spin_lock(&dreq->lock); @@ -714,7 +689,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) } nfs_direct_count_bytes(dreq, hdr); - if (hdr->good_bytes != 0 && nfs_write_need_commit(hdr)) { + if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags)) { if (!dreq->flags) dreq->flags = NFS_ODIRECT_DO_COMMIT; flags = dreq->flags; @@ -759,6 +734,8 @@ static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr) { struct nfs_direct_req *dreq = hdr->dreq; + trace_nfs_direct_write_reschedule_io(dreq); + spin_lock(&dreq->lock); if (dreq->error == 0) { dreq->flags = NFS_ODIRECT_RESCHED_WRITES; @@ -799,6 +776,8 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, size_t requested_bytes = 0; size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE); + trace_nfs_direct_write_schedule_iovec(dreq); + nfs_pageio_init_write(&desc, inode, ioflags, false, &nfs_direct_write_completion_ops); desc.pg_dreq = dreq; @@ -812,13 +791,12 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, size_t pgbase; unsigned npages, i; - result = iov_iter_get_pages_alloc(iter, &pagevec, + result = iov_iter_get_pages_alloc2(iter, &pagevec, wsize, &pgbase); if (result < 0) break; bytes = result; - iov_iter_advance(iter, bytes); npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE; for (i = 0; i < npages; i++) { struct nfs_page *req; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 549baed76351..d2bcd4834c0e 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -661,8 +661,6 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from) result = filemap_fdatawait_range(file->f_mapping, iocb->ki_pos - written, iocb->ki_pos - 1); - if (result < 0) - goto out; } result = generic_write_sync(iocb, written); if (result < 0) diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index 2b2661582bbe..ad34a33b0737 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -181,6 +181,8 @@ static int filelayout_async_handle_error(struct rpc_task *task, case -EIO: case -ETIMEDOUT: case -EPIPE: + case -EPROTO: + case -ENODEV: dprintk("%s DS connection error %d\n", __func__, task->tk_status); nfs4_mark_deviceid_unavailable(devid); diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 604be402ae13..7d285561e59f 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -1131,6 +1131,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, case -EIO: case -ETIMEDOUT: case -EPIPE: + case -EPROTO: + case -ENODEV: dprintk("%s DS connection error %d\n", __func__, task->tk_status); nfs4_delete_deviceid(devid->ld, devid->nfs_client, @@ -1236,6 +1238,8 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg, case -ENOBUFS: case -EPIPE: case -EPERM: + case -EPROTO: + case -ENODEV: *op_status = status = NFS4ERR_NXIO; break; case -EACCES: diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c index bfa7202ca7be..e028f5a0ef5f 100644 --- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c +++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c @@ -113,8 +113,10 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, goto out_err_drain_dsaddrs; ds_versions[i].version = be32_to_cpup(p++); ds_versions[i].minor_version = be32_to_cpup(p++); - ds_versions[i].rsize = nfs_block_size(be32_to_cpup(p++), NULL); - ds_versions[i].wsize = nfs_block_size(be32_to_cpup(p++), NULL); + ds_versions[i].rsize = nfs_io_size(be32_to_cpup(p++), + server->nfs_client->cl_proto); + ds_versions[i].wsize = nfs_io_size(be32_to_cpup(p++), + server->nfs_client->cl_proto); ds_versions[i].tightly_coupled = be32_to_cpup(p); if (ds_versions[i].rsize > NFS_MAX_FILE_IO_SIZE) diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index 9a16897e8dc6..4da701fd1424 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -21,6 +21,8 @@ #include "nfs.h" #include "internal.h" +#include "nfstrace.h" + #define NFSDBG_FACILITY NFSDBG_MOUNT #if IS_ENABLED(CONFIG_NFS_V3) @@ -284,7 +286,6 @@ static int nfs_verify_server_address(struct sockaddr *addr) } } - dfprintk(MOUNT, "NFS: Invalid IP address specified\n"); return 0; } @@ -378,7 +379,7 @@ static int nfs_parse_security_flavors(struct fs_context *fc, char *string = param->string, *p; int ret; - dfprintk(MOUNT, "NFS: parsing %s=%s option\n", param->key, param->string); + trace_nfs_mount_assign(param->key, string); while ((p = strsep(&string, ":")) != NULL) { if (!*p) @@ -480,11 +481,11 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, unsigned int len; int ret, opt; - dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", param->key); + trace_nfs_mount_option(param); opt = fs_parse(fc, nfs_fs_parameters, param, &result); if (opt < 0) - return ctx->sloppy ? 1 : opt; + return (opt == -ENOPARAM && ctx->sloppy) ? 1 : opt; if (fc->security) ctx->has_sec_mnt_opts = 1; @@ -683,6 +684,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, return ret; break; case Opt_vers: + trace_nfs_mount_assign(param->key, param->string); ret = nfs_parse_version_string(fc, param->string); if (ret < 0) return ret; @@ -694,6 +696,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, break; case Opt_proto: + trace_nfs_mount_assign(param->key, param->string); protofamily = AF_INET; switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) { case Opt_xprt_udp6: @@ -729,6 +732,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, break; case Opt_mountproto: + trace_nfs_mount_assign(param->key, param->string); mountfamily = AF_INET; switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) { case Opt_xprt_udp6: @@ -751,6 +755,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, break; case Opt_addr: + trace_nfs_mount_assign(param->key, param->string); len = rpc_pton(fc->net_ns, param->string, param->size, &ctx->nfs_server.address, sizeof(ctx->nfs_server._address)); @@ -759,16 +764,19 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, ctx->nfs_server.addrlen = len; break; case Opt_clientaddr: + trace_nfs_mount_assign(param->key, param->string); kfree(ctx->client_address); ctx->client_address = param->string; param->string = NULL; break; case Opt_mounthost: + trace_nfs_mount_assign(param->key, param->string); kfree(ctx->mount_server.hostname); ctx->mount_server.hostname = param->string; param->string = NULL; break; case Opt_mountaddr: + trace_nfs_mount_assign(param->key, param->string); len = rpc_pton(fc->net_ns, param->string, param->size, &ctx->mount_server.address, sizeof(ctx->mount_server._address)); @@ -846,7 +854,6 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, */ case Opt_sloppy: ctx->sloppy = true; - dfprintk(MOUNT, "NFS: relaxing parsing rules\n"); break; } @@ -879,10 +886,8 @@ static int nfs_parse_source(struct fs_context *fc, size_t len; const char *end; - if (unlikely(!dev_name || !*dev_name)) { - dfprintk(MOUNT, "NFS: device name not specified\n"); + if (unlikely(!dev_name || !*dev_name)) return -EINVAL; - } /* Is the host name protected with square brakcets? */ if (*dev_name == '[') { @@ -922,7 +927,7 @@ static int nfs_parse_source(struct fs_context *fc, if (!ctx->nfs_server.export_path) goto out_nomem; - dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", ctx->nfs_server.export_path); + trace_nfs_mount_path(ctx->nfs_server.export_path); return 0; out_bad_devname: @@ -1116,7 +1121,6 @@ out_no_sec: return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS"); out_nomem: - dfprintk(MOUNT, "NFS: not enough memory to handle mount options"); return -ENOMEM; out_no_address: @@ -1248,7 +1252,7 @@ static int nfs4_parse_monolithic(struct fs_context *fc, if (IS_ERR(c)) return PTR_ERR(c); ctx->nfs_server.export_path = c; - dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c); + trace_nfs_mount_path(c); c = strndup_user(data->client_addr.data, 16); if (IS_ERR(c)) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 437ebe544aaf..27c720d71b4e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -707,6 +707,24 @@ unsigned long nfs_block_size(unsigned long bsize, unsigned char *nrbitsp) } /* + * Compute and set NFS server rsize / wsize + */ +static inline +unsigned long nfs_io_size(unsigned long iosize, enum xprt_transports proto) +{ + if (iosize < NFS_MIN_FILE_IO_SIZE) + iosize = NFS_DEF_FILE_IO_SIZE; + else if (iosize >= NFS_MAX_FILE_IO_SIZE) + iosize = NFS_MAX_FILE_IO_SIZE; + else + iosize = iosize & PAGE_MASK; + + if (proto == XPRT_TRANSPORT_UDP) + return nfs_block_bits(iosize, NULL); + return iosize; +} + +/* * Determine the maximum file size for a superblock */ static inline @@ -861,3 +879,36 @@ static inline void nfs_set_port(struct sockaddr *sap, int *port, rpc_set_port(sap, *port); } + +struct nfs_direct_req { + struct kref kref; /* release manager */ + + /* I/O parameters */ + struct nfs_open_context *ctx; /* file open context info */ + struct nfs_lock_context *l_ctx; /* Lock context info */ + struct kiocb * iocb; /* controlling i/o request */ + struct inode * inode; /* target file of i/o */ + + /* completion state */ + atomic_t io_count; /* i/os we're waiting for */ + spinlock_t lock; /* protect completion state */ + + loff_t io_start; /* Start offset for I/O */ + ssize_t count, /* bytes actually processed */ + max_count, /* max expected count */ + bytes_left, /* bytes left to be sent */ + error; /* any reported error */ + struct completion completion; /* wait for i/o completion */ + + /* commit state */ + struct nfs_mds_commit_info mds_cinfo; /* Storage for cinfo */ + struct pnfs_ds_commit_info ds_cinfo; /* Storage for cinfo */ + struct work_struct work; + int flags; + /* for write */ +#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ +#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */ + /* for read */ +#define NFS_ODIRECT_SHOULD_DIRTY (3) /* dirty user-space page after read */ +#define NFS_ODIRECT_DONE INT_MAX /* write verification failed */ +}; diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c index 5601e47360c2..b49359afac88 100644 --- a/fs/nfs/nfs3client.c +++ b/fs/nfs/nfs3client.c @@ -108,7 +108,6 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv, if (mds_srv->flags & NFS_MOUNT_NORESVPORT) __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); - __set_bit(NFS_CS_NOPING, &cl_init.init_flags); __set_bit(NFS_CS_DS, &cl_init.init_flags); /* Use the MDS nfs_client cl_ipaddr. */ diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 271e5f92ed01..b56f05113d36 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -1025,82 +1025,95 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re return decode_op_hdr(xdr, OP_DEALLOCATE); } -static int decode_read_plus_data(struct xdr_stream *xdr, - struct nfs_pgio_args *args, - struct nfs_pgio_res *res) -{ - uint32_t count, recvd; +struct read_plus_segment { + enum data_content4 type; uint64_t offset; - __be32 *p; - - p = xdr_inline_decode(xdr, 8 + 4); - if (!p) - return 1; + union { + struct { + uint64_t length; + } hole; + + struct { + uint32_t length; + unsigned int from; + } data; + }; +}; - p = xdr_decode_hyper(p, &offset); - count = be32_to_cpup(p); - recvd = xdr_align_data(xdr, res->count, xdr_align_size(count)); - if (recvd > count) - recvd = count; - if (res->count + recvd > args->count) { - if (args->count > res->count) - res->count += args->count - res->count; - return 1; - } - res->count += recvd; - if (count > recvd) - return 1; - return 0; +static inline uint64_t read_plus_segment_length(struct read_plus_segment *seg) +{ + return seg->type == NFS4_CONTENT_DATA ? seg->data.length : seg->hole.length; } -static int decode_read_plus_hole(struct xdr_stream *xdr, - struct nfs_pgio_args *args, - struct nfs_pgio_res *res, uint32_t *eof) +static int decode_read_plus_segment(struct xdr_stream *xdr, + struct read_plus_segment *seg) { - uint64_t offset, length, recvd; __be32 *p; - p = xdr_inline_decode(xdr, 8 + 8); + p = xdr_inline_decode(xdr, 4); if (!p) - return 1; - - p = xdr_decode_hyper(p, &offset); - p = xdr_decode_hyper(p, &length); - if (offset != args->offset + res->count) { - /* Server returned an out-of-sequence extent */ - if (offset > args->offset + res->count || - offset + length < args->offset + res->count) { - dprintk("NFS: server returned out of sequence extent: " - "offset/size = %llu/%llu != expected %llu\n", - (unsigned long long)offset, - (unsigned long long)length, - (unsigned long long)(args->offset + - res->count)); - return 1; - } - length -= args->offset + res->count - offset; - } - if (length + res->count > args->count) { - *eof = 0; - if (unlikely(res->count >= args->count)) - return 1; - length = args->count - res->count; - } - recvd = xdr_expand_hole(xdr, res->count, length); - res->count += recvd; + return -EIO; + seg->type = be32_to_cpup(p++); + + p = xdr_inline_decode(xdr, seg->type == NFS4_CONTENT_DATA ? 12 : 16); + if (!p) + return -EIO; + p = xdr_decode_hyper(p, &seg->offset); + + if (seg->type == NFS4_CONTENT_DATA) { + struct xdr_buf buf; + uint32_t len = be32_to_cpup(p); + + seg->data.length = len; + seg->data.from = xdr_stream_pos(xdr); - if (recvd < length) - return 1; + if (!xdr_stream_subsegment(xdr, &buf, xdr_align_size(len))) + return -EIO; + } else if (seg->type == NFS4_CONTENT_HOLE) { + xdr_decode_hyper(p, &seg->hole.length); + } else + return -EINVAL; return 0; } +static int process_read_plus_segment(struct xdr_stream *xdr, + struct nfs_pgio_args *args, + struct nfs_pgio_res *res, + struct read_plus_segment *seg) +{ + unsigned long offset = seg->offset; + unsigned long length = read_plus_segment_length(seg); + unsigned int bufpos; + + if (offset + length < args->offset) + return 0; + else if (offset > args->offset + args->count) { + res->eof = 0; + return 0; + } else if (offset < args->offset) { + length -= (args->offset - offset); + offset = args->offset; + } else if (offset + length > args->offset + args->count) { + length = (args->offset + args->count) - offset; + res->eof = 0; + } + + bufpos = xdr->buf->head[0].iov_len + (offset - args->offset); + if (seg->type == NFS4_CONTENT_HOLE) + return xdr_stream_zero(xdr, bufpos, length); + else + return xdr_stream_move_subsegment(xdr, seg->data.from, bufpos, length); +} + static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res) { struct nfs_pgio_header *hdr = container_of(res, struct nfs_pgio_header, res); struct nfs_pgio_args *args = &hdr->args; - uint32_t eof, segments, type; + uint32_t segments; + struct read_plus_segment *segs; int status, i; + char scratch_buf[16]; __be32 *p; status = decode_op_hdr(xdr, OP_READ_PLUS); @@ -1112,38 +1125,31 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res) return -EIO; res->count = 0; - eof = be32_to_cpup(p++); + res->eof = be32_to_cpup(p++); segments = be32_to_cpup(p++); if (segments == 0) - goto out; - - for (i = 0; i < segments; i++) { - p = xdr_inline_decode(xdr, 4); - if (!p) - goto early_out; + return status; - type = be32_to_cpup(p++); - if (type == NFS4_CONTENT_DATA) - status = decode_read_plus_data(xdr, args, res); - else if (type == NFS4_CONTENT_HOLE) - status = decode_read_plus_hole(xdr, args, res, &eof); - else - return -EINVAL; + segs = kmalloc_array(segments, sizeof(*segs), GFP_KERNEL); + if (!segs) + return -ENOMEM; + xdr_set_scratch_buffer(xdr, &scratch_buf, 32); + status = -EIO; + for (i = 0; i < segments; i++) { + status = decode_read_plus_segment(xdr, &segs[i]); if (status < 0) - return status; - if (status > 0) - goto early_out; + goto out; } + xdr_set_pagelen(xdr, xdr_align_size(args->count)); + for (i = segments; i > 0; i--) + res->count += process_read_plus_segment(xdr, args, res, &segs[i-1]); + status = 0; + out: - res->eof = eof; - return 0; -early_out: - if (unlikely(!i)) - return -EIO; - res->eof = 0; - return 0; + kfree(segs); + return status; } static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 47a6cf892c95..3c5678aec006 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -1161,9 +1161,9 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc) return error; if (ctx->rsize) - server->rsize = nfs_block_size(ctx->rsize, NULL); + server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto); if (ctx->wsize) - server->wsize = nfs_block_size(ctx->wsize, NULL); + server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto); server->acregmin = ctx->acregmin * HZ; server->acregmax = ctx->acregmax * HZ; diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index f331866dd418..ec6afd3c4bca 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -561,22 +561,20 @@ nfs_idmap_prepare_pipe_upcall(struct idmap *idmap, return true; } -static void -nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret) +static void nfs_idmap_complete_pipe_upcall(struct idmap_legacy_upcalldata *data, + int ret) { - struct key *authkey = idmap->idmap_upcall_data->authkey; - - kfree(idmap->idmap_upcall_data); - idmap->idmap_upcall_data = NULL; - complete_request_key(authkey, ret); - key_put(authkey); + complete_request_key(data->authkey, ret); + key_put(data->authkey); + kfree(data); } -static void -nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret) +static void nfs_idmap_abort_pipe_upcall(struct idmap *idmap, + struct idmap_legacy_upcalldata *data, + int ret) { - if (idmap->idmap_upcall_data != NULL) - nfs_idmap_complete_pipe_upcall_locked(idmap, ret); + if (cmpxchg(&idmap->idmap_upcall_data, data, NULL) == data) + nfs_idmap_complete_pipe_upcall(data, ret); } static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux) @@ -613,7 +611,7 @@ static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux) ret = rpc_queue_upcall(idmap->idmap_pipe, msg); if (ret < 0) - nfs_idmap_abort_pipe_upcall(idmap, ret); + nfs_idmap_abort_pipe_upcall(idmap, data, ret); return ret; out2: @@ -669,6 +667,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) struct request_key_auth *rka; struct rpc_inode *rpci = RPC_I(file_inode(filp)); struct idmap *idmap = (struct idmap *)rpci->private; + struct idmap_legacy_upcalldata *data; struct key *authkey; struct idmap_msg im; size_t namelen_in; @@ -678,10 +677,11 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) * will have been woken up and someone else may now have used * idmap_key_cons - so after this point we may no longer touch it. */ - if (idmap->idmap_upcall_data == NULL) + data = xchg(&idmap->idmap_upcall_data, NULL); + if (data == NULL) goto out_noupcall; - authkey = idmap->idmap_upcall_data->authkey; + authkey = data->authkey; rka = get_request_key_auth(authkey); if (mlen != sizeof(im)) { @@ -703,18 +703,17 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { ret = -EINVAL; goto out; -} + } - ret = nfs_idmap_read_and_verify_message(&im, - &idmap->idmap_upcall_data->idmap_msg, - rka->target_key, authkey); + ret = nfs_idmap_read_and_verify_message(&im, &data->idmap_msg, + rka->target_key, authkey); if (ret >= 0) { key_set_timeout(rka->target_key, nfs_idmap_cache_timeout); ret = mlen; } out: - nfs_idmap_complete_pipe_upcall_locked(idmap, ret); + nfs_idmap_complete_pipe_upcall(data, ret); out_noupcall: return ret; } @@ -728,7 +727,7 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) struct idmap *idmap = data->idmap; if (msg->errno) - nfs_idmap_abort_pipe_upcall(idmap, msg->errno); + nfs_idmap_abort_pipe_upcall(idmap, data, msg->errno); } static void @@ -736,8 +735,11 @@ idmap_release_pipe(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); struct idmap *idmap = (struct idmap *)rpci->private; + struct idmap_legacy_upcalldata *data; - nfs_idmap_abort_pipe_upcall(idmap, -EPIPE); + data = xchg(&idmap->idmap_upcall_data, NULL); + if (data) + nfs_idmap_complete_pipe_upcall(data, -EPIPE); } int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index bb0e84a46d61..3ed14a2a84a4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -784,10 +784,9 @@ static void nfs4_slot_sequence_record_sent(struct nfs4_slot *slot, if ((s32)(seqnr - slot->seq_nr_highest_sent) > 0) slot->seq_nr_highest_sent = seqnr; } -static void nfs4_slot_sequence_acked(struct nfs4_slot *slot, - u32 seqnr) +static void nfs4_slot_sequence_acked(struct nfs4_slot *slot, u32 seqnr) { - slot->seq_nr_highest_sent = seqnr; + nfs4_slot_sequence_record_sent(slot, seqnr); slot->seq_nr_last_acked = seqnr; } @@ -854,7 +853,6 @@ static int nfs41_sequence_process(struct rpc_task *task, __func__, slot->slot_nr, slot->seq_nr); - nfs4_slot_sequence_acked(slot, slot->seq_nr); goto out_retry; case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_SEQ_FALSE_RETRY: @@ -3098,12 +3096,13 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, } out: - if (opendata->lgp) { - nfs4_lgopen_release(opendata->lgp); - opendata->lgp = NULL; - } - if (!opendata->cancelled) + if (!opendata->cancelled) { + if (opendata->lgp) { + nfs4_lgopen_release(opendata->lgp); + opendata->lgp = NULL; + } nfs4_sequence_free_slot(&opendata->o_res.seq_res); + } return ret; } @@ -8924,6 +8923,9 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt, if (status == 0) rpc_clnt_xprt_switch_add_xprt(clnt, xprt); + else if (rpc_clnt_xprt_switch_has_addr(clnt, + (struct sockaddr *)&xprt->addr)) + rpc_clnt_xprt_switch_remove_xprt(clnt, xprt); rpc_put_task(task); } @@ -9248,6 +9250,13 @@ int nfs4_proc_create_session(struct nfs_client *clp, const struct cred *cred) int status; unsigned *ptr; struct nfs4_session *session = clp->cl_session; + struct nfs4_add_xprt_data xprtdata = { + .clp = clp, + }; + struct rpc_add_xprt_test rpcdata = { + .add_xprt_test = clp->cl_mvops->session_trunk, + .data = &xprtdata, + }; dprintk("--> %s clp=%p session=%p\n", __func__, clp, session); @@ -9264,6 +9273,7 @@ int nfs4_proc_create_session(struct nfs_client *clp, const struct cred *cred) ptr = (unsigned *)&session->sess_id.data[0]; dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__, clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]); + rpc_clnt_probe_trunked_xprts(clp->cl_rpcclient, &rpcdata); out: return status; } @@ -9293,6 +9303,7 @@ int nfs4_proc_destroy_session(struct nfs4_session *session, if (status) dprintk("NFS: Got error %d from the server on DESTROY_SESSION. " "Session has been destroyed regardless...\n", status); + rpc_clnt_manage_trunked_xprts(session->clp->cl_rpcclient); return status; } @@ -9477,6 +9488,9 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf rpc_delay(task, NFS4_POLL_RETRY_MAX); fallthrough; case -NFS4ERR_RETRY_UNCACHED_REP: + case -EACCES: + dprintk("%s: failed to reclaim complete error %d for server %s, retrying\n", + __func__, task->tk_status, clp->cl_hostname); return -EAGAIN; case -NFS4ERR_BADSESSION: case -NFS4ERR_DEADSESSION: diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index 012bd7339862..8c6cc58679ff 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -1137,7 +1137,7 @@ TRACE_EVENT(nfs_readpage_done, __field(u32, arg_count) __field(u32, res_count) __field(bool, eof) - __field(int, status) + __field(int, error) ), TP_fast_assign( @@ -1146,7 +1146,7 @@ TRACE_EVENT(nfs_readpage_done, const struct nfs_fh *fh = hdr->args.fh ? hdr->args.fh : &nfsi->fh; - __entry->status = task->tk_status; + __entry->error = task->tk_status; __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; __entry->res_count = hdr->res.count; @@ -1157,14 +1157,13 @@ TRACE_EVENT(nfs_readpage_done, ), TP_printk( - "fileid=%02x:%02x:%llu fhandle=0x%08x " - "offset=%lld count=%u res=%u status=%d%s", + "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%u res=%u%s", __entry->error, MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long long)__entry->fileid, __entry->fhandle, (long long)__entry->offset, __entry->arg_count, - __entry->res_count, __entry->status, - __entry->eof ? " eof" : "" + __entry->res_count, __entry->eof ? " eof" : "" ) ); @@ -1184,7 +1183,7 @@ TRACE_EVENT(nfs_readpage_short, __field(u32, arg_count) __field(u32, res_count) __field(bool, eof) - __field(int, status) + __field(int, error) ), TP_fast_assign( @@ -1193,7 +1192,7 @@ TRACE_EVENT(nfs_readpage_short, const struct nfs_fh *fh = hdr->args.fh ? hdr->args.fh : &nfsi->fh; - __entry->status = task->tk_status; + __entry->error = task->tk_status; __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; __entry->res_count = hdr->res.count; @@ -1204,14 +1203,13 @@ TRACE_EVENT(nfs_readpage_short, ), TP_printk( - "fileid=%02x:%02x:%llu fhandle=0x%08x " - "offset=%lld count=%u res=%u status=%d%s", + "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%u res=%u%s", __entry->error, MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long long)__entry->fileid, __entry->fhandle, (long long)__entry->offset, __entry->arg_count, - __entry->res_count, __entry->status, - __entry->eof ? " eof" : "" + __entry->res_count, __entry->eof ? " eof" : "" ) ); @@ -1323,7 +1321,7 @@ TRACE_EVENT(nfs_pgio_error, __field(u32, arg_count) __field(u32, res_count) __field(loff_t, pos) - __field(int, status) + __field(int, error) ), TP_fast_assign( @@ -1332,7 +1330,7 @@ TRACE_EVENT(nfs_pgio_error, const struct nfs_fh *fh = hdr->args.fh ? hdr->args.fh : &nfsi->fh; - __entry->status = error; + __entry->error = error; __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; __entry->res_count = hdr->res.count; @@ -1341,12 +1339,12 @@ TRACE_EVENT(nfs_pgio_error, __entry->fhandle = nfs_fhandle_hash(fh); ), - TP_printk("fileid=%02x:%02x:%llu fhandle=0x%08x " - "offset=%lld count=%u res=%u pos=%llu status=%d", + TP_printk("error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%u res=%u pos=%llu", __entry->error, MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long long)__entry->fileid, __entry->fhandle, (long long)__entry->offset, __entry->arg_count, __entry->res_count, - __entry->pos, __entry->status + __entry->pos ) ); @@ -1406,7 +1404,7 @@ TRACE_EVENT(nfs_writeback_done, __field(loff_t, offset) __field(u32, arg_count) __field(u32, res_count) - __field(int, status) + __field(int, error) __field(unsigned long, stable) __array(char, verifier, NFS4_VERIFIER_SIZE) ), @@ -1418,7 +1416,7 @@ TRACE_EVENT(nfs_writeback_done, hdr->args.fh : &nfsi->fh; const struct nfs_writeverf *verf = hdr->res.verf; - __entry->status = task->tk_status; + __entry->error = task->tk_status; __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; __entry->res_count = hdr->res.count; @@ -1432,14 +1430,14 @@ TRACE_EVENT(nfs_writeback_done, ), TP_printk( - "fileid=%02x:%02x:%llu fhandle=0x%08x " - "offset=%lld count=%u res=%u status=%d stable=%s " - "verifier=%s", + "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%u res=%u stable=%s " + "verifier=%s", __entry->error, MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long long)__entry->fileid, __entry->fhandle, (long long)__entry->offset, __entry->arg_count, - __entry->res_count, __entry->status, + __entry->res_count, show_nfs_stable_how(__entry->stable), show_nfs4_verifier(__entry->verifier) ) @@ -1447,44 +1445,50 @@ TRACE_EVENT(nfs_writeback_done, DECLARE_EVENT_CLASS(nfs_page_error_class, TP_PROTO( + const struct inode *inode, const struct nfs_page *req, int error ), - TP_ARGS(req, error), + TP_ARGS(inode, req, error), TP_STRUCT__entry( - __field(const void *, req) - __field(pgoff_t, index) - __field(unsigned int, offset) - __field(unsigned int, pgbase) - __field(unsigned int, bytes) + __field(dev_t, dev) + __field(u32, fhandle) + __field(u64, fileid) + __field(loff_t, offset) + __field(unsigned int, count) __field(int, error) ), TP_fast_assign( - __entry->req = req; - __entry->index = req->wb_index; - __entry->offset = req->wb_offset; - __entry->pgbase = req->wb_pgbase; - __entry->bytes = req->wb_bytes; + const struct nfs_inode *nfsi = NFS_I(inode); + __entry->dev = inode->i_sb->s_dev; + __entry->fileid = nfsi->fileid; + __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); + __entry->offset = req_offset(req); + __entry->count = req->wb_bytes; __entry->error = error; ), TP_printk( - "req=%p index=%lu offset=%u pgbase=%u bytes=%u error=%d", - __entry->req, __entry->index, __entry->offset, - __entry->pgbase, __entry->bytes, __entry->error + "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%u", __entry->error, + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long long)__entry->fileid, + __entry->fhandle, __entry->offset, + __entry->count ) ); #define DEFINE_NFS_PAGEERR_EVENT(name) \ DEFINE_EVENT(nfs_page_error_class, name, \ TP_PROTO( \ + const struct inode *inode, \ const struct nfs_page *req, \ int error \ ), \ - TP_ARGS(req, error)) + TP_ARGS(inode, req, error)) DEFINE_NFS_PAGEERR_EVENT(nfs_write_error); DEFINE_NFS_PAGEERR_EVENT(nfs_comp_error); @@ -1541,7 +1545,7 @@ TRACE_EVENT(nfs_commit_done, __field(u32, fhandle) __field(u64, fileid) __field(loff_t, offset) - __field(int, status) + __field(int, error) __field(unsigned long, stable) __array(char, verifier, NFS4_VERIFIER_SIZE) ), @@ -1553,7 +1557,7 @@ TRACE_EVENT(nfs_commit_done, data->args.fh : &nfsi->fh; const struct nfs_writeverf *verf = data->res.verf; - __entry->status = task->tk_status; + __entry->error = task->tk_status; __entry->offset = data->args.offset; __entry->stable = verf->committed; memcpy(__entry->verifier, @@ -1565,17 +1569,83 @@ TRACE_EVENT(nfs_commit_done, ), TP_printk( - "fileid=%02x:%02x:%llu fhandle=0x%08x " - "offset=%lld status=%d stable=%s verifier=%s", + "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld stable=%s verifier=%s", __entry->error, MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long long)__entry->fileid, __entry->fhandle, - (long long)__entry->offset, __entry->status, + (long long)__entry->offset, show_nfs_stable_how(__entry->stable), show_nfs4_verifier(__entry->verifier) ) ); +#define nfs_show_direct_req_flags(v) \ + __print_flags(v, "|", \ + { NFS_ODIRECT_DO_COMMIT, "DO_COMMIT" }, \ + { NFS_ODIRECT_RESCHED_WRITES, "RESCHED_WRITES" }, \ + { NFS_ODIRECT_SHOULD_DIRTY, "SHOULD DIRTY" }, \ + { NFS_ODIRECT_DONE, "DONE" } ) + +DECLARE_EVENT_CLASS(nfs_direct_req_class, + TP_PROTO( + const struct nfs_direct_req *dreq + ), + + TP_ARGS(dreq), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(u64, fileid) + __field(u32, fhandle) + __field(loff_t, offset) + __field(ssize_t, count) + __field(ssize_t, bytes_left) + __field(ssize_t, error) + __field(int, flags) + ), + + TP_fast_assign( + const struct inode *inode = dreq->inode; + const struct nfs_inode *nfsi = NFS_I(inode); + const struct nfs_fh *fh = &nfsi->fh; + + __entry->dev = inode->i_sb->s_dev; + __entry->fileid = nfsi->fileid; + __entry->fhandle = nfs_fhandle_hash(fh); + __entry->offset = dreq->io_start; + __entry->count = dreq->count; + __entry->bytes_left = dreq->bytes_left; + __entry->error = dreq->error; + __entry->flags = dreq->flags; + ), + + TP_printk( + "error=%zd fileid=%02x:%02x:%llu fhandle=0x%08x " + "offset=%lld count=%zd bytes_left=%zd flags=%s", + __entry->error, MAJOR(__entry->dev), + MINOR(__entry->dev), + (unsigned long long)__entry->fileid, + __entry->fhandle, __entry->offset, + __entry->count, __entry->bytes_left, + nfs_show_direct_req_flags(__entry->flags) + ) +); + +#define DEFINE_NFS_DIRECT_REQ_EVENT(name) \ + DEFINE_EVENT(nfs_direct_req_class, name, \ + TP_PROTO( \ + const struct nfs_direct_req *dreq \ + ), \ + TP_ARGS(dreq)) + +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_commit_complete); +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_resched_write); +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_complete); +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_completion); +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_schedule_iovec); +DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_reschedule_io); + TRACE_EVENT(nfs_fh_to_dentry, TP_PROTO( const struct super_block *sb, @@ -1609,6 +1679,65 @@ TRACE_EVENT(nfs_fh_to_dentry, ) ); +TRACE_EVENT(nfs_mount_assign, + TP_PROTO( + const char *option, + const char *value + ), + + TP_ARGS(option, value), + + TP_STRUCT__entry( + __string(option, option) + __string(value, value) + ), + + TP_fast_assign( + __assign_str(option, option); + __assign_str(value, value); + ), + + TP_printk("option %s=%s", + __get_str(option), __get_str(value) + ) +); + +TRACE_EVENT(nfs_mount_option, + TP_PROTO( + const struct fs_parameter *param + ), + + TP_ARGS(param), + + TP_STRUCT__entry( + __string(option, param->key) + ), + + TP_fast_assign( + __assign_str(option, param->key); + ), + + TP_printk("option %s", __get_str(option)) +); + +TRACE_EVENT(nfs_mount_path, + TP_PROTO( + const char *path + ), + + TP_ARGS(path), + + TP_STRUCT__entry( + __string(path, path) + ), + + TP_fast_assign( + __assign_str(path, path); + ), + + TP_printk("path='%s'", __get_str(path)) +); + DECLARE_EVENT_CLASS(nfs_xdr_event, TP_PROTO( const struct xdr_stream *xdr, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 69569696dde0..51a7e202d6e5 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -592,7 +592,8 @@ nfs_lock_and_join_requests(struct page *page) static void nfs_write_error(struct nfs_page *req, int error) { - trace_nfs_write_error(req, error); + trace_nfs_write_error(page_file_mapping(req->wb_page)->host, req, + error); nfs_mapping_set_error(req->wb_page, error); nfs_inode_remove_request(req); nfs_end_page_writeback(req); @@ -1000,7 +1001,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) nfs_list_remove_request(req); if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && (hdr->good_bytes < bytes)) { - trace_nfs_comp_error(req, hdr->error); + trace_nfs_comp_error(hdr->inode, req, hdr->error); nfs_mapping_set_error(req->wb_page, hdr->error); goto remove_req; } @@ -1444,8 +1445,6 @@ static void nfs_async_write_error(struct list_head *head, int error) static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr) { nfs_async_write_error(&hdr->pages, 0); - filemap_fdatawrite_range(hdr->inode->i_mapping, hdr->args.offset, - hdr->args.offset + hdr->args.count - 1); } static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = { @@ -1576,25 +1575,37 @@ static int nfs_writeback_done(struct rpc_task *task, nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count); trace_nfs_writeback_done(task, hdr); - if (hdr->res.verf->committed < hdr->args.stable && - task->tk_status >= 0) { - /* We tried a write call, but the server did not - * commit data to stable storage even though we - * requested it. - * Note: There is a known bug in Tru64 < 5.0 in which - * the server reports NFS_DATA_SYNC, but performs - * NFS_FILE_SYNC. We therefore implement this checking - * as a dprintk() in order to avoid filling syslog. - */ - static unsigned long complain; + if (task->tk_status >= 0) { + enum nfs3_stable_how committed = hdr->res.verf->committed; + + if (committed == NFS_UNSTABLE) { + /* + * We have some uncommitted data on the server at + * this point, so ensure that we keep track of that + * fact irrespective of what later writes do. + */ + set_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags); + } - /* Note this will print the MDS for a DS write */ - if (time_before(complain, jiffies)) { - dprintk("NFS: faulty NFS server %s:" - " (committed = %d) != (stable = %d)\n", - NFS_SERVER(inode)->nfs_client->cl_hostname, - hdr->res.verf->committed, hdr->args.stable); - complain = jiffies + 300 * HZ; + if (committed < hdr->args.stable) { + /* We tried a write call, but the server did not + * commit data to stable storage even though we + * requested it. + * Note: There is a known bug in Tru64 < 5.0 in which + * the server reports NFS_DATA_SYNC, but performs + * NFS_FILE_SYNC. We therefore implement this checking + * as a dprintk() in order to avoid filling syslog. + */ + static unsigned long complain; + + /* Note this will print the MDS for a DS write */ + if (time_before(complain, jiffies)) { + dprintk("NFS: faulty NFS server %s:" + " (committed = %d) != (stable = %d)\n", + NFS_SERVER(inode)->nfs_client->cl_hostname, + committed, hdr->args.stable); + complain = jiffies + 300 * HZ; + } } } @@ -1872,7 +1883,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) (long long)req_offset(req)); if (status < 0) { if (req->wb_page) { - trace_nfs_commit_error(req, status); + trace_nfs_commit_error(data->inode, req, + status); nfs_mapping_set_error(req->wb_page, status); nfs_inode_remove_request(req); } diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index ba14d2f4b64f..4b7324458a94 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -38,6 +38,8 @@ struct nfs4_acl; struct svc_fh; struct svc_rqst; +struct nfsd_attrs; +enum nfs_ftype4; int nfs4_acl_bytes(int entries); int nfs4_acl_get_whotype(char *, u32); @@ -45,7 +47,7 @@ __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who); int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl); -__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct nfs4_acl *acl); +__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, + struct nfsd_attrs *attr); #endif /* LINUX_NFS4_ACL_H */ diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index a605c0e39b09..eeed4ae5b4ad 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -13,6 +13,7 @@ #include <linux/fsnotify_backend.h> #include <linux/fsnotify.h> #include <linux/seq_file.h> +#include <linux/rhashtable.h> #include "vfs.h" #include "nfsd.h" @@ -21,28 +22,19 @@ #include "filecache.h" #include "trace.h" -#define NFSDDBG_FACILITY NFSDDBG_FH - -/* FIXME: dynamically size this for the machine somehow? */ -#define NFSD_FILE_HASH_BITS 12 -#define NFSD_FILE_HASH_SIZE (1 << NFSD_FILE_HASH_BITS) #define NFSD_LAUNDRETTE_DELAY (2 * HZ) -#define NFSD_FILE_SHUTDOWN (1) -#define NFSD_FILE_LRU_THRESHOLD (4096UL) -#define NFSD_FILE_LRU_LIMIT (NFSD_FILE_LRU_THRESHOLD << 2) +#define NFSD_FILE_CACHE_UP (0) /* We only care about NFSD_MAY_READ/WRITE for this cache */ #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE) -struct nfsd_fcache_bucket { - struct hlist_head nfb_head; - spinlock_t nfb_lock; - unsigned int nfb_count; - unsigned int nfb_maxcount; -}; - static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits); +static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions); +static DEFINE_PER_CPU(unsigned long, nfsd_file_releases); +static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age); +static DEFINE_PER_CPU(unsigned long, nfsd_file_pages_flushed); +static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions); struct nfsd_fcache_disposal { struct work_struct work; @@ -54,21 +46,146 @@ static struct workqueue_struct *nfsd_filecache_wq __read_mostly; static struct kmem_cache *nfsd_file_slab; static struct kmem_cache *nfsd_file_mark_slab; -static struct nfsd_fcache_bucket *nfsd_file_hashtbl; static struct list_lru nfsd_file_lru; -static long nfsd_file_lru_flags; +static unsigned long nfsd_file_flags; static struct fsnotify_group *nfsd_file_fsnotify_group; -static atomic_long_t nfsd_filecache_count; static struct delayed_work nfsd_filecache_laundrette; +static struct rhashtable nfsd_file_rhash_tbl + ____cacheline_aligned_in_smp; + +enum nfsd_file_lookup_type { + NFSD_FILE_KEY_INODE, + NFSD_FILE_KEY_FULL, +}; + +struct nfsd_file_lookup_key { + struct inode *inode; + struct net *net; + const struct cred *cred; + unsigned char need; + enum nfsd_file_lookup_type type; +}; + +/* + * The returned hash value is based solely on the address of an in-code + * inode, a pointer to a slab-allocated object. The entropy in such a + * pointer is concentrated in its middle bits. + */ +static u32 nfsd_file_inode_hash(const struct inode *inode, u32 seed) +{ + unsigned long ptr = (unsigned long)inode; + u32 k; + + k = ptr >> L1_CACHE_SHIFT; + k &= 0x00ffffff; + return jhash2(&k, 1, seed); +} + +/** + * nfsd_file_key_hashfn - Compute the hash value of a lookup key + * @data: key on which to compute the hash value + * @len: rhash table's key_len parameter (unused) + * @seed: rhash table's random seed of the day + * + * Return value: + * Computed 32-bit hash value + */ +static u32 nfsd_file_key_hashfn(const void *data, u32 len, u32 seed) +{ + const struct nfsd_file_lookup_key *key = data; + + return nfsd_file_inode_hash(key->inode, seed); +} + +/** + * nfsd_file_obj_hashfn - Compute the hash value of an nfsd_file + * @data: object on which to compute the hash value + * @len: rhash table's key_len parameter (unused) + * @seed: rhash table's random seed of the day + * + * Return value: + * Computed 32-bit hash value + */ +static u32 nfsd_file_obj_hashfn(const void *data, u32 len, u32 seed) +{ + const struct nfsd_file *nf = data; + + return nfsd_file_inode_hash(nf->nf_inode, seed); +} -static void nfsd_file_gc(void); +static bool +nfsd_match_cred(const struct cred *c1, const struct cred *c2) +{ + int i; + + if (!uid_eq(c1->fsuid, c2->fsuid)) + return false; + if (!gid_eq(c1->fsgid, c2->fsgid)) + return false; + if (c1->group_info == NULL || c2->group_info == NULL) + return c1->group_info == c2->group_info; + if (c1->group_info->ngroups != c2->group_info->ngroups) + return false; + for (i = 0; i < c1->group_info->ngroups; i++) { + if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i])) + return false; + } + return true; +} + +/** + * nfsd_file_obj_cmpfn - Match a cache item against search criteria + * @arg: search criteria + * @ptr: cache item to check + * + * Return values: + * %0 - Item matches search criteria + * %1 - Item does not match search criteria + */ +static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg, + const void *ptr) +{ + const struct nfsd_file_lookup_key *key = arg->key; + const struct nfsd_file *nf = ptr; + + switch (key->type) { + case NFSD_FILE_KEY_INODE: + if (nf->nf_inode != key->inode) + return 1; + break; + case NFSD_FILE_KEY_FULL: + if (nf->nf_inode != key->inode) + return 1; + if (nf->nf_may != key->need) + return 1; + if (nf->nf_net != key->net) + return 1; + if (!nfsd_match_cred(nf->nf_cred, key->cred)) + return 1; + if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) + return 1; + break; + } + return 0; +} + +static const struct rhashtable_params nfsd_file_rhash_params = { + .key_len = sizeof_field(struct nfsd_file, nf_inode), + .key_offset = offsetof(struct nfsd_file, nf_inode), + .head_offset = offsetof(struct nfsd_file, nf_rhash), + .hashfn = nfsd_file_key_hashfn, + .obj_hashfn = nfsd_file_obj_hashfn, + .obj_cmpfn = nfsd_file_obj_cmpfn, + /* Reduce resizing churn on light workloads */ + .min_size = 512, /* buckets */ + .automatic_shrinking = true, +}; static void nfsd_file_schedule_laundrette(void) { - long count = atomic_long_read(&nfsd_filecache_count); - - if (count == 0 || test_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags)) + if ((atomic_read(&nfsd_file_rhash_tbl.nelems) == 0) || + test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) return; queue_delayed_work(system_wq, &nfsd_filecache_laundrette, @@ -111,12 +228,11 @@ nfsd_file_mark_put(struct nfsd_file_mark *nfm) } static struct nfsd_file_mark * -nfsd_file_mark_find_or_create(struct nfsd_file *nf) +nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode) { int err; struct fsnotify_mark *mark; struct nfsd_file_mark *nfm = NULL, *new; - struct inode *inode = nf->nf_inode; do { fsnotify_group_lock(nfsd_file_fsnotify_group); @@ -167,31 +283,25 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf) } static struct nfsd_file * -nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval, - struct net *net) +nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may) { struct nfsd_file *nf; nf = kmem_cache_alloc(nfsd_file_slab, GFP_KERNEL); if (nf) { - INIT_HLIST_NODE(&nf->nf_node); INIT_LIST_HEAD(&nf->nf_lru); + nf->nf_birthtime = ktime_get(); nf->nf_file = NULL; nf->nf_cred = get_current_cred(); - nf->nf_net = net; + nf->nf_net = key->net; nf->nf_flags = 0; - nf->nf_inode = inode; - nf->nf_hashval = hashval; - refcount_set(&nf->nf_ref, 1); - nf->nf_may = may & NFSD_FILE_MAY_MASK; - if (may & NFSD_MAY_NOT_BREAK_LEASE) { - if (may & NFSD_MAY_WRITE) - __set_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags); - if (may & NFSD_MAY_READ) - __set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags); - } + __set_bit(NFSD_FILE_HASHED, &nf->nf_flags); + __set_bit(NFSD_FILE_PENDING, &nf->nf_flags); + nf->nf_inode = key->inode; + /* nf_ref is pre-incremented for hash table */ + refcount_set(&nf->nf_ref, 2); + nf->nf_may = key->need; nf->nf_mark = NULL; - trace_nfsd_file_alloc(nf); } return nf; } @@ -199,8 +309,12 @@ nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval, static bool nfsd_file_free(struct nfsd_file *nf) { + s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime)); bool flush = false; + this_cpu_inc(nfsd_file_releases); + this_cpu_add(nfsd_file_total_age, age); + trace_nfsd_file_put_final(nf); if (nf->nf_mark) nfsd_file_mark_put(nf->nf_mark); @@ -210,6 +324,14 @@ nfsd_file_free(struct nfsd_file *nf) fput(nf->nf_file); flush = true; } + + /* + * If this item is still linked via nf_lru, that's a bug. + * WARN and leak it to preserve system stability. + */ + if (WARN_ON_ONCE(!list_empty(&nf->nf_lru))) + return flush; + call_rcu(&nf->nf_rcu, nfsd_file_slab_free); return flush; } @@ -240,31 +362,44 @@ nfsd_file_check_write_error(struct nfsd_file *nf) static void nfsd_file_flush(struct nfsd_file *nf) { - if (nf->nf_file && vfs_fsync(nf->nf_file, 1) != 0) + struct file *file = nf->nf_file; + + if (!file || !(file->f_mode & FMODE_WRITE)) + return; + this_cpu_add(nfsd_file_pages_flushed, file->f_mapping->nrpages); + if (vfs_fsync(file, 1) != 0) nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id)); } -static void -nfsd_file_do_unhash(struct nfsd_file *nf) +static void nfsd_file_lru_add(struct nfsd_file *nf) { - lockdep_assert_held(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); + set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); + if (list_lru_add(&nfsd_file_lru, &nf->nf_lru)) + trace_nfsd_file_lru_add(nf); +} +static void nfsd_file_lru_remove(struct nfsd_file *nf) +{ + if (list_lru_del(&nfsd_file_lru, &nf->nf_lru)) + trace_nfsd_file_lru_del(nf); +} + +static void +nfsd_file_hash_remove(struct nfsd_file *nf) +{ trace_nfsd_file_unhash(nf); if (nfsd_file_check_write_error(nf)) nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id)); - --nfsd_file_hashtbl[nf->nf_hashval].nfb_count; - hlist_del_rcu(&nf->nf_node); - atomic_long_dec(&nfsd_filecache_count); + rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash, + nfsd_file_rhash_params); } static bool nfsd_file_unhash(struct nfsd_file *nf) { if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { - nfsd_file_do_unhash(nf); - if (!list_empty(&nf->nf_lru)) - list_lru_del(&nfsd_file_lru, &nf->nf_lru); + nfsd_file_hash_remove(nf); return true; } return false; @@ -274,17 +409,16 @@ nfsd_file_unhash(struct nfsd_file *nf) * Return true if the file was unhashed. */ static bool -nfsd_file_unhash_and_release_locked(struct nfsd_file *nf, struct list_head *dispose) +nfsd_file_unhash_and_dispose(struct nfsd_file *nf, struct list_head *dispose) { - lockdep_assert_held(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); - - trace_nfsd_file_unhash_and_release_locked(nf); + trace_nfsd_file_unhash_and_dispose(nf); if (!nfsd_file_unhash(nf)) return false; /* keep final reference for nfsd_file_lru_dispose */ if (refcount_dec_not_one(&nf->nf_ref)) return true; + nfsd_file_lru_remove(nf); list_add(&nf->nf_lru, dispose); return true; } @@ -296,6 +430,7 @@ nfsd_file_put_noref(struct nfsd_file *nf) if (refcount_dec_and_test(&nf->nf_ref)) { WARN_ON(test_bit(NFSD_FILE_HASHED, &nf->nf_flags)); + nfsd_file_lru_remove(nf); nfsd_file_free(nf); } } @@ -305,7 +440,7 @@ nfsd_file_put(struct nfsd_file *nf) { might_sleep(); - set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); + nfsd_file_lru_add(nf); if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) { nfsd_file_flush(nf); nfsd_file_put_noref(nf); @@ -314,9 +449,24 @@ nfsd_file_put(struct nfsd_file *nf) nfsd_file_schedule_laundrette(); } else nfsd_file_put_noref(nf); +} - if (atomic_long_read(&nfsd_filecache_count) >= NFSD_FILE_LRU_LIMIT) - nfsd_file_gc(); +/** + * nfsd_file_close - Close an nfsd_file + * @nf: nfsd_file to close + * + * If this is the final reference for @nf, free it immediately. + * This reflects an on-the-wire CLOSE or DELEGRETURN into the + * VFS and exported filesystem. + */ +void nfsd_file_close(struct nfsd_file *nf) +{ + nfsd_file_put(nf); + if (refcount_dec_if_one(&nf->nf_ref)) { + nfsd_file_unhash(nf); + nfsd_file_lru_remove(nf); + nfsd_file_free(nf); + } } struct nfsd_file * @@ -334,7 +484,7 @@ nfsd_file_dispose_list(struct list_head *dispose) while(!list_empty(dispose)) { nf = list_first_entry(dispose, struct nfsd_file, nf_lru); - list_del(&nf->nf_lru); + list_del_init(&nf->nf_lru); nfsd_file_flush(nf); nfsd_file_put_noref(nf); } @@ -348,7 +498,7 @@ nfsd_file_dispose_list_sync(struct list_head *dispose) while(!list_empty(dispose)) { nf = list_first_entry(dispose, struct nfsd_file, nf_lru); - list_del(&nf->nf_lru); + list_del_init(&nf->nf_lru); nfsd_file_flush(nf); if (!refcount_dec_and_test(&nf->nf_ref)) continue; @@ -405,8 +555,19 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose) } } -/* +/** + * nfsd_file_lru_cb - Examine an entry on the LRU list + * @item: LRU entry to examine + * @lru: controlling LRU + * @lock: LRU list lock (unused) + * @arg: dispose list + * * Note this can deadlock with nfsd_file_cache_purge. + * + * Return values: + * %LRU_REMOVED: @item was removed from the LRU + * %LRU_ROTATE: @item is to be moved to the LRU tail + * %LRU_SKIP: @item cannot be evicted */ static enum lru_status nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru, @@ -427,55 +588,65 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru, * counter. Here we check the counter and then test and clear the flag. * That order is deliberate to ensure that we can do this locklessly. */ - if (refcount_read(&nf->nf_ref) > 1) - goto out_skip; + if (refcount_read(&nf->nf_ref) > 1) { + list_lru_isolate(lru, &nf->nf_lru); + trace_nfsd_file_gc_in_use(nf); + return LRU_REMOVED; + } /* * Don't throw out files that are still undergoing I/O or * that have uncleared errors pending. */ - if (nfsd_file_check_writeback(nf)) - goto out_skip; + if (nfsd_file_check_writeback(nf)) { + trace_nfsd_file_gc_writeback(nf); + return LRU_SKIP; + } - if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) - goto out_skip; + if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) { + trace_nfsd_file_gc_referenced(nf); + return LRU_ROTATE; + } - if (!test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) - goto out_skip; + if (!test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { + trace_nfsd_file_gc_hashed(nf); + return LRU_SKIP; + } list_lru_isolate_move(lru, &nf->nf_lru, head); + this_cpu_inc(nfsd_file_evictions); + trace_nfsd_file_gc_disposed(nf); return LRU_REMOVED; -out_skip: - return LRU_SKIP; } -static unsigned long -nfsd_file_lru_walk_list(struct shrink_control *sc) +/* + * Unhash items on @dispose immediately, then queue them on the + * disposal workqueue to finish releasing them in the background. + * + * cel: Note that between the time list_lru_shrink_walk runs and + * now, these items are in the hash table but marked unhashed. + * Why release these outside of lru_cb ? There's no lock ordering + * problem since lru_cb currently takes no lock. + */ +static void nfsd_file_gc_dispose_list(struct list_head *dispose) { - LIST_HEAD(head); struct nfsd_file *nf; - unsigned long ret; - if (sc) - ret = list_lru_shrink_walk(&nfsd_file_lru, sc, - nfsd_file_lru_cb, &head); - else - ret = list_lru_walk(&nfsd_file_lru, - nfsd_file_lru_cb, - &head, LONG_MAX); - list_for_each_entry(nf, &head, nf_lru) { - spin_lock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); - nfsd_file_do_unhash(nf); - spin_unlock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); - } - nfsd_file_dispose_list_delayed(&head); - return ret; + list_for_each_entry(nf, dispose, nf_lru) + nfsd_file_hash_remove(nf); + nfsd_file_dispose_list_delayed(dispose); } static void nfsd_file_gc(void) { - nfsd_file_lru_walk_list(NULL); + LIST_HEAD(dispose); + unsigned long ret; + + ret = list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb, + &dispose, list_lru_count(&nfsd_file_lru)); + trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru)); + nfsd_file_gc_dispose_list(&dispose); } static void @@ -494,7 +665,14 @@ nfsd_file_lru_count(struct shrinker *s, struct shrink_control *sc) static unsigned long nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc) { - return nfsd_file_lru_walk_list(sc); + LIST_HEAD(dispose); + unsigned long ret; + + ret = list_lru_shrink_walk(&nfsd_file_lru, sc, + nfsd_file_lru_cb, &dispose); + trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru)); + nfsd_file_gc_dispose_list(&dispose); + return ret; } static struct shrinker nfsd_file_shrinker = { @@ -503,39 +681,47 @@ static struct shrinker nfsd_file_shrinker = { .seeks = 1, }; -static void -__nfsd_file_close_inode(struct inode *inode, unsigned int hashval, - struct list_head *dispose) +/* + * Find all cache items across all net namespaces that match @inode and + * move them to @dispose. The lookup is atomic wrt nfsd_file_acquire(). + */ +static unsigned int +__nfsd_file_close_inode(struct inode *inode, struct list_head *dispose) { - struct nfsd_file *nf; - struct hlist_node *tmp; + struct nfsd_file_lookup_key key = { + .type = NFSD_FILE_KEY_INODE, + .inode = inode, + }; + unsigned int count = 0; + struct nfsd_file *nf; - spin_lock(&nfsd_file_hashtbl[hashval].nfb_lock); - hlist_for_each_entry_safe(nf, tmp, &nfsd_file_hashtbl[hashval].nfb_head, nf_node) { - if (inode == nf->nf_inode) - nfsd_file_unhash_and_release_locked(nf, dispose); - } - spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock); + rcu_read_lock(); + do { + nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key, + nfsd_file_rhash_params); + if (!nf) + break; + nfsd_file_unhash_and_dispose(nf, dispose); + count++; + } while (1); + rcu_read_unlock(); + return count; } /** * nfsd_file_close_inode_sync - attempt to forcibly close a nfsd_file * @inode: inode of the file to attempt to remove * - * Walk the whole hash bucket, looking for any files that correspond to "inode". - * If any do, then unhash them and put the hashtable reference to them and - * destroy any that had their last reference put. Also ensure that any of the - * fputs also have their final __fput done as well. + * Unhash and put, then flush and fput all cache items associated with @inode. */ void nfsd_file_close_inode_sync(struct inode *inode) { - unsigned int hashval = (unsigned int)hash_long(inode->i_ino, - NFSD_FILE_HASH_BITS); LIST_HEAD(dispose); + unsigned int count; - __nfsd_file_close_inode(inode, hashval, &dispose); - trace_nfsd_file_close_inode_sync(inode, hashval, !list_empty(&dispose)); + count = __nfsd_file_close_inode(inode, &dispose); + trace_nfsd_file_close_inode_sync(inode, count); nfsd_file_dispose_list_sync(&dispose); } @@ -543,19 +729,16 @@ nfsd_file_close_inode_sync(struct inode *inode) * nfsd_file_close_inode - attempt a delayed close of a nfsd_file * @inode: inode of the file to attempt to remove * - * Walk the whole hash bucket, looking for any files that correspond to "inode". - * If any do, then unhash them and put the hashtable reference to them and - * destroy any that had their last reference put. + * Unhash and put all cache item associated with @inode. */ static void nfsd_file_close_inode(struct inode *inode) { - unsigned int hashval = (unsigned int)hash_long(inode->i_ino, - NFSD_FILE_HASH_BITS); LIST_HEAD(dispose); + unsigned int count; - __nfsd_file_close_inode(inode, hashval, &dispose); - trace_nfsd_file_close_inode(inode, hashval, !list_empty(&dispose)); + count = __nfsd_file_close_inode(inode, &dispose); + trace_nfsd_file_close_inode(inode, count); nfsd_file_dispose_list_delayed(&dispose); } @@ -630,25 +813,21 @@ static const struct fsnotify_ops nfsd_file_fsnotify_ops = { int nfsd_file_cache_init(void) { - int ret = -ENOMEM; - unsigned int i; + int ret; - clear_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags); - - if (nfsd_file_hashtbl) + lockdep_assert_held(&nfsd_mutex); + if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) return 0; + ret = rhashtable_init(&nfsd_file_rhash_tbl, &nfsd_file_rhash_params); + if (ret) + return ret; + + ret = -ENOMEM; nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0); if (!nfsd_filecache_wq) goto out; - nfsd_file_hashtbl = kvcalloc(NFSD_FILE_HASH_SIZE, - sizeof(*nfsd_file_hashtbl), GFP_KERNEL); - if (!nfsd_file_hashtbl) { - pr_err("nfsd: unable to allocate nfsd_file_hashtbl\n"); - goto out_err; - } - nfsd_file_slab = kmem_cache_create("nfsd_file", sizeof(struct nfsd_file), 0, 0, NULL); if (!nfsd_file_slab) { @@ -692,11 +871,6 @@ nfsd_file_cache_init(void) goto out_notifier; } - for (i = 0; i < NFSD_FILE_HASH_SIZE; i++) { - INIT_HLIST_HEAD(&nfsd_file_hashtbl[i].nfb_head); - spin_lock_init(&nfsd_file_hashtbl[i].nfb_lock); - } - INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker); out: return ret; @@ -711,46 +885,47 @@ out_err: nfsd_file_slab = NULL; kmem_cache_destroy(nfsd_file_mark_slab); nfsd_file_mark_slab = NULL; - kvfree(nfsd_file_hashtbl); - nfsd_file_hashtbl = NULL; destroy_workqueue(nfsd_filecache_wq); nfsd_filecache_wq = NULL; + rhashtable_destroy(&nfsd_file_rhash_tbl); goto out; } /* * Note this can deadlock with nfsd_file_lru_cb. */ -void -nfsd_file_cache_purge(struct net *net) +static void +__nfsd_file_cache_purge(struct net *net) { - unsigned int i; - struct nfsd_file *nf; - struct hlist_node *next; + struct rhashtable_iter iter; + struct nfsd_file *nf; LIST_HEAD(dispose); bool del; - if (!nfsd_file_hashtbl) - return; - - for (i = 0; i < NFSD_FILE_HASH_SIZE; i++) { - struct nfsd_fcache_bucket *nfb = &nfsd_file_hashtbl[i]; + rhashtable_walk_enter(&nfsd_file_rhash_tbl, &iter); + do { + rhashtable_walk_start(&iter); - spin_lock(&nfb->nfb_lock); - hlist_for_each_entry_safe(nf, next, &nfb->nfb_head, nf_node) { + nf = rhashtable_walk_next(&iter); + while (!IS_ERR_OR_NULL(nf)) { if (net && nf->nf_net != net) continue; - del = nfsd_file_unhash_and_release_locked(nf, &dispose); + del = nfsd_file_unhash_and_dispose(nf, &dispose); /* * Deadlock detected! Something marked this entry as * unhased, but hasn't removed it from the hash list. */ WARN_ON_ONCE(!del); + + nf = rhashtable_walk_next(&iter); } - spin_unlock(&nfb->nfb_lock); - nfsd_file_dispose_list(&dispose); - } + + rhashtable_walk_stop(&iter); + } while (nf == ERR_PTR(-EAGAIN)); + rhashtable_walk_exit(&iter); + + nfsd_file_dispose_list(&dispose); } static struct nfsd_fcache_disposal * @@ -793,6 +968,19 @@ nfsd_file_cache_start_net(struct net *net) return nn->fcache_disposal ? 0 : -ENOMEM; } +/** + * nfsd_file_cache_purge - Remove all cache items associated with @net + * @net: target net namespace + * + */ +void +nfsd_file_cache_purge(struct net *net) +{ + lockdep_assert_held(&nfsd_mutex); + if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) + __nfsd_file_cache_purge(net); +} + void nfsd_file_cache_shutdown_net(struct net *net) { @@ -803,7 +991,11 @@ nfsd_file_cache_shutdown_net(struct net *net) void nfsd_file_cache_shutdown(void) { - set_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags); + int i; + + lockdep_assert_held(&nfsd_mutex); + if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) + return; lease_unregister_notifier(&nfsd_file_lease_notifier); unregister_shrinker(&nfsd_file_shrinker); @@ -812,7 +1004,7 @@ nfsd_file_cache_shutdown(void) * calling nfsd_file_cache_purge */ cancel_delayed_work_sync(&nfsd_filecache_laundrette); - nfsd_file_cache_purge(NULL); + __nfsd_file_cache_purge(NULL); list_lru_destroy(&nfsd_file_lru); rcu_barrier(); fsnotify_put_group(nfsd_file_fsnotify_group); @@ -822,124 +1014,96 @@ nfsd_file_cache_shutdown(void) fsnotify_wait_marks_destroyed(); kmem_cache_destroy(nfsd_file_mark_slab); nfsd_file_mark_slab = NULL; - kvfree(nfsd_file_hashtbl); - nfsd_file_hashtbl = NULL; destroy_workqueue(nfsd_filecache_wq); nfsd_filecache_wq = NULL; -} - -static bool -nfsd_match_cred(const struct cred *c1, const struct cred *c2) -{ - int i; - - if (!uid_eq(c1->fsuid, c2->fsuid)) - return false; - if (!gid_eq(c1->fsgid, c2->fsgid)) - return false; - if (c1->group_info == NULL || c2->group_info == NULL) - return c1->group_info == c2->group_info; - if (c1->group_info->ngroups != c2->group_info->ngroups) - return false; - for (i = 0; i < c1->group_info->ngroups; i++) { - if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i])) - return false; - } - return true; -} - -static struct nfsd_file * -nfsd_file_find_locked(struct inode *inode, unsigned int may_flags, - unsigned int hashval, struct net *net) -{ - struct nfsd_file *nf; - unsigned char need = may_flags & NFSD_FILE_MAY_MASK; - - hlist_for_each_entry_rcu(nf, &nfsd_file_hashtbl[hashval].nfb_head, - nf_node, lockdep_is_held(&nfsd_file_hashtbl[hashval].nfb_lock)) { - if (nf->nf_may != need) - continue; - if (nf->nf_inode != inode) - continue; - if (nf->nf_net != net) - continue; - if (!nfsd_match_cred(nf->nf_cred, current_cred())) - continue; - if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) - continue; - if (nfsd_file_get(nf) != NULL) - return nf; + rhashtable_destroy(&nfsd_file_rhash_tbl); + + for_each_possible_cpu(i) { + per_cpu(nfsd_file_cache_hits, i) = 0; + per_cpu(nfsd_file_acquisitions, i) = 0; + per_cpu(nfsd_file_releases, i) = 0; + per_cpu(nfsd_file_total_age, i) = 0; + per_cpu(nfsd_file_pages_flushed, i) = 0; + per_cpu(nfsd_file_evictions, i) = 0; } - return NULL; } /** - * nfsd_file_is_cached - are there any cached open files for this fh? - * @inode: inode of the file to check + * nfsd_file_is_cached - are there any cached open files for this inode? + * @inode: inode to check + * + * The lookup matches inodes in all net namespaces and is atomic wrt + * nfsd_file_acquire(). * - * Scan the hashtable for open files that match this fh. Returns true if there - * are any, and false if not. + * Return values: + * %true: filecache contains at least one file matching this inode + * %false: filecache contains no files matching this inode */ bool nfsd_file_is_cached(struct inode *inode) { - bool ret = false; - struct nfsd_file *nf; - unsigned int hashval; - - hashval = (unsigned int)hash_long(inode->i_ino, NFSD_FILE_HASH_BITS); - - rcu_read_lock(); - hlist_for_each_entry_rcu(nf, &nfsd_file_hashtbl[hashval].nfb_head, - nf_node) { - if (inode == nf->nf_inode) { - ret = true; - break; - } - } - rcu_read_unlock(); - trace_nfsd_file_is_cached(inode, hashval, (int)ret); + struct nfsd_file_lookup_key key = { + .type = NFSD_FILE_KEY_INODE, + .inode = inode, + }; + bool ret = false; + + if (rhashtable_lookup_fast(&nfsd_file_rhash_tbl, &key, + nfsd_file_rhash_params) != NULL) + ret = true; + trace_nfsd_file_is_cached(inode, (int)ret); return ret; } static __be32 -nfsd_do_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, +nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int may_flags, struct nfsd_file **pnf, bool open) { - __be32 status; - struct net *net = SVC_NET(rqstp); + struct nfsd_file_lookup_key key = { + .type = NFSD_FILE_KEY_FULL, + .need = may_flags & NFSD_FILE_MAY_MASK, + .net = SVC_NET(rqstp), + }; struct nfsd_file *nf, *new; - struct inode *inode; - unsigned int hashval; bool retry = true; + __be32 status; - /* FIXME: skip this if fh_dentry is already set? */ status = fh_verify(rqstp, fhp, S_IFREG, may_flags|NFSD_MAY_OWNER_OVERRIDE); if (status != nfs_ok) return status; + key.inode = d_inode(fhp->fh_dentry); + key.cred = get_current_cred(); - inode = d_inode(fhp->fh_dentry); - hashval = (unsigned int)hash_long(inode->i_ino, NFSD_FILE_HASH_BITS); retry: - rcu_read_lock(); - nf = nfsd_file_find_locked(inode, may_flags, hashval, net); - rcu_read_unlock(); + /* Avoid allocation if the item is already in cache */ + nf = rhashtable_lookup_fast(&nfsd_file_rhash_tbl, &key, + nfsd_file_rhash_params); + if (nf) + nf = nfsd_file_get(nf); if (nf) goto wait_for_construction; - new = nfsd_file_alloc(inode, may_flags, hashval, net); + new = nfsd_file_alloc(&key, may_flags); if (!new) { - trace_nfsd_file_acquire(rqstp, hashval, inode, may_flags, - NULL, nfserr_jukebox); - return nfserr_jukebox; + status = nfserr_jukebox; + goto out_status; } - spin_lock(&nfsd_file_hashtbl[hashval].nfb_lock); - nf = nfsd_file_find_locked(inode, may_flags, hashval, net); - if (nf == NULL) + nf = rhashtable_lookup_get_insert_key(&nfsd_file_rhash_tbl, + &key, &new->nf_rhash, + nfsd_file_rhash_params); + if (!nf) { + nf = new; goto open_file; - spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock); + } + if (IS_ERR(nf)) + goto insert_err; + nf = nfsd_file_get(nf); + if (nf == NULL) { + nf = new; + goto open_file; + } nfsd_file_slab_free(&new->nf_rcu); wait_for_construction: @@ -947,6 +1111,7 @@ wait_for_construction: /* Did construction of this file fail? */ if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { + trace_nfsd_file_cons_err(rqstp, key.inode, may_flags, nf); if (!retry) { status = nfserr_jukebox; goto out; @@ -956,49 +1121,29 @@ wait_for_construction: goto retry; } + nfsd_file_lru_remove(nf); this_cpu_inc(nfsd_file_cache_hits); - if (!(may_flags & NFSD_MAY_NOT_BREAK_LEASE)) { - bool write = (may_flags & NFSD_MAY_WRITE); - - if (test_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags) || - (test_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags) && write)) { - status = nfserrno(nfsd_open_break_lease( - file_inode(nf->nf_file), may_flags)); - if (status == nfs_ok) { - clear_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags); - if (write) - clear_bit(NFSD_FILE_BREAK_WRITE, - &nf->nf_flags); - } - } - } + status = nfserrno(nfsd_open_break_lease(file_inode(nf->nf_file), may_flags)); out: if (status == nfs_ok) { + if (open) + this_cpu_inc(nfsd_file_acquisitions); *pnf = nf; } else { nfsd_file_put(nf); nf = NULL; } - trace_nfsd_file_acquire(rqstp, hashval, inode, may_flags, nf, status); +out_status: + put_cred(key.cred); + if (open) + trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status); return status; + open_file: - nf = new; - /* Take reference for the hashtable */ - refcount_inc(&nf->nf_ref); - __set_bit(NFSD_FILE_HASHED, &nf->nf_flags); - __set_bit(NFSD_FILE_PENDING, &nf->nf_flags); - list_lru_add(&nfsd_file_lru, &nf->nf_lru); - hlist_add_head_rcu(&nf->nf_node, &nfsd_file_hashtbl[hashval].nfb_head); - ++nfsd_file_hashtbl[hashval].nfb_count; - nfsd_file_hashtbl[hashval].nfb_maxcount = max(nfsd_file_hashtbl[hashval].nfb_maxcount, - nfsd_file_hashtbl[hashval].nfb_count); - spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock); - if (atomic_long_inc_return(&nfsd_filecache_count) >= NFSD_FILE_LRU_THRESHOLD) - nfsd_file_gc(); - - nf->nf_mark = nfsd_file_mark_find_or_create(nf); + trace_nfsd_file_alloc(nf); + nf->nf_mark = nfsd_file_mark_find_or_create(nf, key.inode); if (nf->nf_mark) { if (open) { status = nfsd_open_verified(rqstp, fhp, may_flags, @@ -1012,18 +1157,20 @@ open_file: * If construction failed, or we raced with a call to unlink() * then unhash. */ - if (status != nfs_ok || inode->i_nlink == 0) { - bool do_free; - spin_lock(&nfsd_file_hashtbl[hashval].nfb_lock); - do_free = nfsd_file_unhash(nf); - spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock); - if (do_free) + if (status != nfs_ok || key.inode->i_nlink == 0) + if (nfsd_file_unhash(nf)) nfsd_file_put_noref(nf); - } clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags); smp_mb__after_atomic(); wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING); goto out; + +insert_err: + nfsd_file_slab_free(&new->nf_rcu); + trace_nfsd_file_insert_err(rqstp, key.inode, may_flags, PTR_ERR(nf)); + nf = NULL; + status = nfserr_jukebox; + goto out_status; } /** @@ -1040,7 +1187,7 @@ __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int may_flags, struct nfsd_file **pnf) { - return nfsd_do_file_acquire(rqstp, fhp, may_flags, pnf, true); + return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true); } /** @@ -1057,7 +1204,7 @@ __be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int may_flags, struct nfsd_file **pnf) { - return nfsd_do_file_acquire(rqstp, fhp, may_flags, pnf, false); + return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false); } /* @@ -1067,29 +1214,49 @@ nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp, */ static int nfsd_file_cache_stats_show(struct seq_file *m, void *v) { - unsigned int i, count = 0, longest = 0; - unsigned long hits = 0; + unsigned long releases = 0, pages_flushed = 0, evictions = 0; + unsigned long hits = 0, acquisitions = 0; + unsigned int i, count = 0, buckets = 0; + unsigned long lru = 0, total_age = 0; - /* - * No need for spinlocks here since we're not terribly interested in - * accuracy. We do take the nfsd_mutex simply to ensure that we - * don't end up racing with server shutdown - */ + /* Serialize with server shutdown */ mutex_lock(&nfsd_mutex); - if (nfsd_file_hashtbl) { - for (i = 0; i < NFSD_FILE_HASH_SIZE; i++) { - count += nfsd_file_hashtbl[i].nfb_count; - longest = max(longest, nfsd_file_hashtbl[i].nfb_count); - } + if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) { + struct bucket_table *tbl; + struct rhashtable *ht; + + lru = list_lru_count(&nfsd_file_lru); + + rcu_read_lock(); + ht = &nfsd_file_rhash_tbl; + count = atomic_read(&ht->nelems); + tbl = rht_dereference_rcu(ht->tbl, ht); + buckets = tbl->size; + rcu_read_unlock(); } mutex_unlock(&nfsd_mutex); - for_each_possible_cpu(i) + for_each_possible_cpu(i) { hits += per_cpu(nfsd_file_cache_hits, i); + acquisitions += per_cpu(nfsd_file_acquisitions, i); + releases += per_cpu(nfsd_file_releases, i); + total_age += per_cpu(nfsd_file_total_age, i); + evictions += per_cpu(nfsd_file_evictions, i); + pages_flushed += per_cpu(nfsd_file_pages_flushed, i); + } seq_printf(m, "total entries: %u\n", count); - seq_printf(m, "longest chain: %u\n", longest); + seq_printf(m, "hash buckets: %u\n", buckets); + seq_printf(m, "lru entries: %lu\n", lru); seq_printf(m, "cache hits: %lu\n", hits); + seq_printf(m, "acquisitions: %lu\n", acquisitions); + seq_printf(m, "releases: %lu\n", releases); + seq_printf(m, "evictions: %lu\n", evictions); + if (releases) + seq_printf(m, "mean age (ms): %ld\n", total_age / releases); + else + seq_printf(m, "mean age (ms): -\n"); + seq_printf(m, "pages flushed: %lu\n", pages_flushed); return 0; } diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index 1da0c79a5580..8e8c0c47d67d 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -29,7 +29,7 @@ struct nfsd_file_mark { * never be dereferenced, only used for comparison. */ struct nfsd_file { - struct hlist_node nf_node; + struct rhash_head nf_rhash; struct list_head nf_lru; struct rcu_head nf_rcu; struct file *nf_file; @@ -37,15 +37,13 @@ struct nfsd_file { struct net *nf_net; #define NFSD_FILE_HASHED (0) #define NFSD_FILE_PENDING (1) -#define NFSD_FILE_BREAK_READ (2) -#define NFSD_FILE_BREAK_WRITE (3) -#define NFSD_FILE_REFERENCED (4) +#define NFSD_FILE_REFERENCED (2) unsigned long nf_flags; - struct inode *nf_inode; - unsigned int nf_hashval; + struct inode *nf_inode; /* don't deref */ refcount_t nf_ref; unsigned char nf_may; struct nfsd_file_mark *nf_mark; + ktime_t nf_birthtime; }; int nfsd_file_cache_init(void); @@ -54,6 +52,7 @@ void nfsd_file_cache_shutdown(void); int nfsd_file_cache_start_net(struct net *net); void nfsd_file_cache_shutdown_net(struct net *net); void nfsd_file_put(struct nfsd_file *nf); +void nfsd_file_close(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); void nfsd_file_close_inode_sync(struct inode *inode); bool nfsd_file_is_cached(struct inode *inode); diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 1b1a962a1804..ffe17743cc74 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -189,6 +189,9 @@ struct nfsd_net { struct nfsd_fcache_disposal *fcache_disposal; siphash_key_t siphash_key; + + atomic_t nfs4_client_count; + int nfs4_max_clients; }; /* Simple check to find out if a given net was properly initialized */ diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index b5760801d377..9edd3c1a30fb 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -111,7 +111,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) if (error) goto out_errno; - fh_lock(fh); + inode_lock(inode); error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, argp->acl_access); @@ -122,7 +122,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) if (error) goto out_drop_lock; - fh_unlock(fh); + inode_unlock(inode); fh_drop_write(fh); @@ -136,7 +136,7 @@ out: return rpc_success; out_drop_lock: - fh_unlock(fh); + inode_unlock(inode); fh_drop_write(fh); out_errno: resp->status = nfserrno(error); diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 35b2ebda14da..9446c6743664 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -101,7 +101,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) if (error) goto out_errno; - fh_lock(fh); + inode_lock(inode); error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, argp->acl_access); @@ -111,7 +111,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) argp->acl_default); out_drop_lock: - fh_unlock(fh); + inode_unlock(inode); fh_drop_write(fh); out_errno: resp->status = nfserrno(error); diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 981a3a7a6e16..a41cca619338 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -67,12 +67,15 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp) { struct nfsd3_sattrargs *argp = rqstp->rq_argp; struct nfsd3_attrstat *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; dprintk("nfsd: SETATTR(3) %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - resp->status = nfsd_setattr(rqstp, &resp->fh, &argp->attrs, + resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, argp->check_guard, argp->guardtime); return rpc_success; } @@ -233,6 +236,9 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, { struct iattr *iap = &argp->attrs; struct dentry *parent, *child; + struct nfsd_attrs attrs = { + .na_iattr = iap, + }; __u32 v_mtime, v_atime; struct inode *inode; __be32 status; @@ -254,7 +260,7 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err) return nfserrno(host_err); - fh_lock_nested(fhp, I_MUTEX_PARENT); + inode_lock_nested(inode, I_MUTEX_PARENT); child = lookup_one_len(argp->name, parent, argp->len); if (IS_ERR(child)) { @@ -312,11 +318,13 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (!IS_POSIXACL(inode)) iap->ia_mode &= ~current_umask(); + fh_fill_pre_attrs(fhp); host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true); if (host_err < 0) { status = nfserrno(host_err); goto out; } + fh_fill_post_attrs(fhp); /* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) @@ -331,10 +339,10 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, } set_attr: - status = nfsd_create_setattr(rqstp, fhp, resfhp, iap); + status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); out: - fh_unlock(fhp); + inode_unlock(inode); if (child && !IS_ERR(child)) dput(child); fh_drop_write(fhp); @@ -368,6 +376,9 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp) { struct nfsd3_createargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; dprintk("nfsd: MKDIR(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -378,8 +389,7 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp) fh_copy(&resp->dirfh, &argp->fh); fh_init(&resp->fh, NFS3_FHSIZE); resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, - &argp->attrs, S_IFDIR, 0, &resp->fh); - fh_unlock(&resp->dirfh); + &attrs, S_IFDIR, 0, &resp->fh); return rpc_success; } @@ -388,6 +398,9 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) { struct nfsd3_symlinkargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; if (argp->tlen == 0) { resp->status = nfserr_inval; @@ -414,7 +427,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) fh_copy(&resp->dirfh, &argp->ffh); fh_init(&resp->fh, NFS3_FHSIZE); resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, - argp->flen, argp->tname, &resp->fh); + argp->flen, argp->tname, &attrs, &resp->fh); kfree(argp->tname); out: return rpc_success; @@ -428,6 +441,9 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp) { struct nfsd3_mknodargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; int type; dev_t rdev = 0; @@ -453,8 +469,7 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp) type = nfs3_ftypes[argp->ftype]; resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, - &argp->attrs, type, rdev, &resp->fh); - fh_unlock(&resp->dirfh); + &attrs, type, rdev, &resp->fh); out: return rpc_success; } @@ -477,7 +492,6 @@ nfsd3_proc_remove(struct svc_rqst *rqstp) fh_copy(&resp->fh, &argp->fh); resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len); - fh_unlock(&resp->fh); return rpc_success; } @@ -498,7 +512,6 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp) fh_copy(&resp->fh, &argp->fh); resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len); - fh_unlock(&resp->fh); return rpc_success; } diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index eaa3a0cf38f1..bb8e2f6d7d03 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -751,58 +751,26 @@ out_estate: return ret; } -__be32 -nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct nfs4_acl *acl) +__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, + struct nfsd_attrs *attr) { - __be32 error; int host_error; - struct dentry *dentry; - struct inode *inode; - struct posix_acl *pacl = NULL, *dpacl = NULL; unsigned int flags = 0; - /* Get inode */ - error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR); - if (error) - return error; - - dentry = fhp->fh_dentry; - inode = d_inode(dentry); + if (!acl) + return nfs_ok; - if (S_ISDIR(inode->i_mode)) + if (type == NF4DIR) flags = NFS4_ACL_DIR; - host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); + host_error = nfs4_acl_nfsv4_to_posix(acl, &attr->na_pacl, + &attr->na_dpacl, flags); if (host_error == -EINVAL) return nfserr_attrnotsupp; - if (host_error < 0) - goto out_nfserr; - - fh_lock(fhp); - - host_error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, pacl); - if (host_error < 0) - goto out_drop_lock; - - if (S_ISDIR(inode->i_mode)) { - host_error = set_posix_acl(&init_user_ns, inode, - ACL_TYPE_DEFAULT, dpacl); - } - -out_drop_lock: - fh_unlock(fhp); - - posix_acl_release(pacl); - posix_acl_release(dpacl); -out_nfserr: - if (host_error == -EOPNOTSUPP) - return nfserr_attrnotsupp; else return nfserrno(host_error); } - static short ace2type(struct nfs4_ace *ace) { diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 11f8715d92d6..4ce328209f61 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -679,7 +679,7 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp, * case NFS4_OK: * write_response4 coa_resok4; * default: - * length4 coa_bytes_copied; + * length4 coa_bytes_copied; * }; * struct CB_OFFLOAD4args { * nfs_fh4 coa_fh; @@ -688,21 +688,22 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp, * }; */ static void encode_offload_info4(struct xdr_stream *xdr, - __be32 nfserr, - const struct nfsd4_copy *cp) + const struct nfsd4_cb_offload *cbo) { __be32 *p; p = xdr_reserve_space(xdr, 4); - *p++ = nfserr; - if (!nfserr) { + *p = cbo->co_nfserr; + switch (cbo->co_nfserr) { + case nfs_ok: p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE); p = xdr_encode_empty_array(p); - p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written); - *p++ = cpu_to_be32(cp->cp_res.wr_stable_how); - p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data, + p = xdr_encode_hyper(p, cbo->co_res.wr_bytes_written); + *p++ = cpu_to_be32(cbo->co_res.wr_stable_how); + p = xdr_encode_opaque_fixed(p, cbo->co_res.wr_verifier.data, NFS4_VERIFIER_SIZE); - } else { + break; + default: p = xdr_reserve_space(xdr, 8); /* We always return success if bytes were written */ p = xdr_encode_hyper(p, 0); @@ -710,18 +711,16 @@ static void encode_offload_info4(struct xdr_stream *xdr, } static void encode_cb_offload4args(struct xdr_stream *xdr, - __be32 nfserr, - const struct knfsd_fh *fh, - const struct nfsd4_copy *cp, + const struct nfsd4_cb_offload *cbo, struct nfs4_cb_compound_hdr *hdr) { __be32 *p; p = xdr_reserve_space(xdr, 4); - *p++ = cpu_to_be32(OP_CB_OFFLOAD); - encode_nfs_fh4(xdr, fh); - encode_stateid4(xdr, &cp->cp_res.cb_stateid); - encode_offload_info4(xdr, nfserr, cp); + *p = cpu_to_be32(OP_CB_OFFLOAD); + encode_nfs_fh4(xdr, &cbo->co_fh); + encode_stateid4(xdr, &cbo->co_res.cb_stateid); + encode_offload_info4(xdr, cbo); hdr->nops++; } @@ -731,8 +730,8 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req, const void *data) { const struct nfsd4_callback *cb = data; - const struct nfsd4_copy *cp = - container_of(cb, struct nfsd4_copy, cp_cb); + const struct nfsd4_cb_offload *cbo = + container_of(cb, struct nfsd4_cb_offload, co_cb); struct nfs4_cb_compound_hdr hdr = { .ident = 0, .minorversion = cb->cb_clp->cl_minorversion, @@ -740,7 +739,7 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req, encode_cb_compound4args(xdr, &hdr); encode_cb_sequence4args(xdr, cb, &hdr); - encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr); + encode_cb_offload4args(xdr, cbo, &hdr); encode_cb_nops(&hdr); } diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 3895eb52d2b1..a72ab97f77ef 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -64,36 +64,6 @@ MODULE_PARM_DESC(nfsd4_ssc_umount_timeout, "idle msecs before unmount export from source server"); #endif -#ifdef CONFIG_NFSD_V4_SECURITY_LABEL -#include <linux/security.h> - -static inline void -nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval) -{ - struct inode *inode = d_inode(resfh->fh_dentry); - int status; - - inode_lock(inode); - status = security_inode_setsecctx(resfh->fh_dentry, - label->data, label->len); - inode_unlock(inode); - - if (status) - /* - * XXX: We should really fail the whole open, but we may - * already have created a new file, so it may be too - * late. For now this seems the least of evils: - */ - bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; - - return; -} -#else -static inline void -nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval) -{ } -#endif - #define NFSDDBG_FACILITY NFSDDBG_PROC static u32 nfsd_attrmask[] = { @@ -158,26 +128,6 @@ is_create_with_attrs(struct nfsd4_open *open) || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1); } -/* - * if error occurs when setting the acl, just clear the acl bit - * in the returned attr bitmap. - */ -static void -do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct nfs4_acl *acl, u32 *bmval) -{ - __be32 status; - - status = nfsd4_set_nfs4_acl(rqstp, fhp, acl); - if (status) - /* - * We should probably fail the whole open at this point, - * but we've already created the file, so it's too late; - * So this seems the least of evils: - */ - bmval[0] &= ~FATTR4_WORD0_ACL; -} - static inline void fh_dup2(struct svc_fh *dst, struct svc_fh *src) { @@ -286,6 +236,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct svc_fh *resfhp, struct nfsd4_open *open) { struct iattr *iap = &open->op_iattr; + struct nfsd_attrs attrs = { + .na_iattr = iap, + .na_seclabel = &open->op_label, + }; struct dentry *parent, *child; __u32 v_mtime, v_atime; struct inode *inode; @@ -307,7 +261,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err) return nfserrno(host_err); - fh_lock_nested(fhp, I_MUTEX_PARENT); + if (is_create_with_attrs(open)) + nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); + + inode_lock_nested(inode, I_MUTEX_PARENT); child = lookup_one_len(open->op_fname, parent, open->op_fnamelen); if (IS_ERR(child)) { @@ -345,6 +302,11 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (d_really_is_positive(child)) { status = nfs_ok; + /* NFSv4 protocol requires change attributes even though + * no change happened. + */ + fh_fill_both_attrs(fhp); + switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: if (!d_is_reg(child)) @@ -386,10 +348,12 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (!IS_POSIXACL(inode)) iap->ia_mode &= ~current_umask(); + fh_fill_pre_attrs(fhp); status = nfsd4_vfs_create(fhp, child, open); if (status != nfs_ok) goto out; open->op_created = true; + fh_fill_post_attrs(fhp); /* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) @@ -404,10 +368,15 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, } set_attr: - status = nfsd_create_setattr(rqstp, fhp, resfhp, iap); + status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); + if (attrs.na_labelerr) + open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + if (attrs.na_aclerr) + open->op_bmval[0] &= ~FATTR4_WORD0_ACL; out: - fh_unlock(fhp); + inode_unlock(inode); + nfsd_attrs_free(&attrs); if (child && !IS_ERR(child)) dput(child); fh_drop_write(fhp); @@ -447,9 +416,6 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru status = nfsd4_create_file(rqstp, current_fh, *resfh, open); current->fs->umask = 0; - if (!status && open->op_label.len) - nfsd4_security_inode_setsecctx(*resfh, &open->op_label, open->op_bmval); - /* * Following rfc 3530 14.2.16, and rfc 5661 18.16.4 * use the returned bitmask to indicate which attributes @@ -458,24 +424,21 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0) open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_MODIFY); - } else - /* - * Note this may exit with the parent still locked. - * We will hold the lock until nfsd4_open's final - * lookup, to prevent renames or unlinks until we've had - * a chance to an acquire a delegation if appropriate. - */ + } else { status = nfsd_lookup(rqstp, current_fh, open->op_fname, open->op_fnamelen, *resfh); + if (!status) + /* NFSv4 protocol requires change attributes even though + * no change happened. + */ + fh_fill_both_attrs(current_fh); + } if (status) goto out; status = nfsd_check_obj_isreg(*resfh); if (status) goto out; - if (is_create_with_attrs(open) && open->op_acl != NULL) - do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval); - nfsd4_set_open_owner_reply_cache(cstate, open, *resfh); accmode = NFSD_MAY_NOP; if (open->op_created || @@ -547,6 +510,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, open->op_openowner); open->op_filp = NULL; + open->op_rqstp = rqstp; /* This check required by spec. */ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) @@ -630,9 +594,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } status = nfsd4_process_open2(rqstp, resfh, open); - WARN(status && open->op_created, - "nfsd4_process_open2 failed to open newly-created file! status=%u\n", - be32_to_cpu(status)); + if (status && open->op_created) + pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n", + be32_to_cpu(status)); if (reclaim && !status) nn->somebody_reclaimed = true; out: @@ -786,6 +750,10 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) { struct nfsd4_create *create = &u->create; + struct nfsd_attrs attrs = { + .na_iattr = &create->cr_iattr, + .na_seclabel = &create->cr_label, + }; struct svc_fh resfh; __be32 status; dev_t rdev; @@ -801,12 +769,13 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) return status; + status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); current->fs->umask = create->cr_umask; switch (create->cr_type) { case NF4LNK: status = nfsd_symlink(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - create->cr_data, &resfh); + create->cr_data, &attrs, &resfh); break; case NF4BLK: @@ -817,7 +786,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out_umask; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - &create->cr_iattr, S_IFBLK, rdev, &resfh); + &attrs, S_IFBLK, rdev, &resfh); break; case NF4CHR: @@ -828,26 +797,26 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out_umask; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - &create->cr_iattr,S_IFCHR, rdev, &resfh); + &attrs, S_IFCHR, rdev, &resfh); break; case NF4SOCK: status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - &create->cr_iattr, S_IFSOCK, 0, &resfh); + &attrs, S_IFSOCK, 0, &resfh); break; case NF4FIFO: status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - &create->cr_iattr, S_IFIFO, 0, &resfh); + &attrs, S_IFIFO, 0, &resfh); break; case NF4DIR: create->cr_iattr.ia_valid &= ~ATTR_SIZE; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, - &create->cr_iattr, S_IFDIR, 0, &resfh); + &attrs, S_IFDIR, 0, &resfh); break; default: @@ -857,20 +826,17 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto out; - if (create->cr_label.len) - nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval); - - if (create->cr_acl != NULL) - do_set_nfs4_acl(rqstp, &resfh, create->cr_acl, - create->cr_bmval); - - fh_unlock(&cstate->current_fh); + if (attrs.na_labelerr) + create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + if (attrs.na_aclerr) + create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; set_change_info(&create->cr_cinfo, &cstate->current_fh); fh_dup2(&cstate->current_fh, &resfh); out: fh_put(&resfh); out_umask: current->fs->umask = 0; + nfsd_attrs_free(&attrs); return status; } @@ -1043,10 +1009,8 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_grace; status = nfsd_unlink(rqstp, &cstate->current_fh, 0, remove->rm_name, remove->rm_namelen); - if (!status) { - fh_unlock(&cstate->current_fh); + if (!status) set_change_info(&remove->rm_cinfo, &cstate->current_fh); - } return status; } @@ -1086,7 +1050,6 @@ nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, &exp, &dentry); if (err) return err; - fh_unlock(&cstate->current_fh); if (d_really_is_negative(dentry)) { exp_put(exp); err = nfserr_noent; @@ -1141,6 +1104,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) { struct nfsd4_setattr *setattr = &u->setattr; + struct nfsd_attrs attrs = { + .na_iattr = &setattr->sa_iattr, + .na_seclabel = &setattr->sa_label, + }; + struct inode *inode; __be32 status = nfs_ok; int err; @@ -1163,19 +1131,18 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto out; - if (setattr->sa_acl != NULL) - status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh, - setattr->sa_acl); - if (status) - goto out; - if (setattr->sa_label.len) - status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh, - &setattr->sa_label); + inode = cstate->current_fh.fh_dentry->d_inode; + status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, + setattr->sa_acl, &attrs); + if (status) goto out; - status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr, + status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, 0, (time64_t)0); + if (!status) + status = nfserrno(attrs.na_labelerr); out: + nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); return status; } @@ -1285,30 +1252,17 @@ out: return status; } -void nfs4_put_copy(struct nfsd4_copy *copy) +static void nfs4_put_copy(struct nfsd4_copy *copy) { if (!refcount_dec_and_test(©->refcount)) return; + kfree(copy->cp_src); kfree(copy); } -static bool -check_and_set_stop_copy(struct nfsd4_copy *copy) -{ - bool value; - - spin_lock(©->cp_clp->async_lock); - value = copy->stopped; - if (!copy->stopped) - copy->stopped = true; - spin_unlock(©->cp_clp->async_lock); - return value; -} - static void nfsd4_stop_copy(struct nfsd4_copy *copy) { - /* only 1 thread should stop the copy */ - if (!check_and_set_stop_copy(copy)) + if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) kthread_stop(copy->copy_task); nfs4_put_copy(copy); } @@ -1389,7 +1343,7 @@ try_again: return 0; } if (work) { - strncpy(work->nsui_ipaddr, ipaddr, sizeof(work->nsui_ipaddr)); + strlcpy(work->nsui_ipaddr, ipaddr, sizeof(work->nsui_ipaddr) - 1); refcount_set(&work->nsui_refcnt, 2); work->nsui_busy = true; list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list); @@ -1549,7 +1503,7 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp, if (status) goto out; - status = nfsd4_interssc_connect(©->cp_src, rqstp, mount); + status = nfsd4_interssc_connect(copy->cp_src, rqstp, mount); if (status) goto out; @@ -1567,7 +1521,7 @@ out: } static void -nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src, +nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp, struct nfsd_file *dst) { bool found = false; @@ -1576,9 +1530,9 @@ nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src, struct nfsd4_ssc_umount_item *ni = NULL; struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id); - nfs42_ssc_close(src->nf_file); + nfs42_ssc_close(filp); nfsd_file_put(dst); - fput(src->nf_file); + fput(filp); if (!nn) { mntput(ss_mnt); @@ -1621,7 +1575,7 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp, } static void -nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src, +nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp, struct nfsd_file *dst) { } @@ -1658,9 +1612,10 @@ nfsd4_cleanup_intra_ssc(struct nfsd_file *src, struct nfsd_file *dst) static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) { - struct nfsd4_copy *copy = container_of(cb, struct nfsd4_copy, cp_cb); + struct nfsd4_cb_offload *cbo = + container_of(cb, struct nfsd4_cb_offload, co_cb); - nfs4_put_copy(copy); + kfree(cbo); } static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, @@ -1677,15 +1632,16 @@ static const struct nfsd4_callback_ops nfsd4_cb_offload_ops = { static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync) { copy->cp_res.wr_stable_how = - copy->committed ? NFS_FILE_SYNC : NFS_UNSTABLE; - copy->cp_synchronous = sync; + test_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags) ? + NFS_FILE_SYNC : NFS_UNSTABLE; + nfsd4_copy_set_sync(copy, sync); gen_boot_verifier(©->cp_res.wr_verifier, copy->cp_clp->net); } -static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy) +static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy, + struct file *dst, + struct file *src) { - struct file *dst = copy->nf_dst->nf_file; - struct file *src = copy->nf_src->nf_file; errseq_t since; ssize_t bytes_copied = 0; u64 bytes_total = copy->cp_count; @@ -1707,26 +1663,29 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy) copy->cp_res.wr_bytes_written += bytes_copied; src_pos += bytes_copied; dst_pos += bytes_copied; - } while (bytes_total > 0 && !copy->cp_synchronous); + } while (bytes_total > 0 && nfsd4_copy_is_async(copy)); /* for a non-zero asynchronous copy do a commit of data */ - if (!copy->cp_synchronous && copy->cp_res.wr_bytes_written > 0) { + if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) { since = READ_ONCE(dst->f_wb_err); status = vfs_fsync_range(dst, copy->cp_dst_pos, copy->cp_res.wr_bytes_written, 0); if (!status) status = filemap_check_wb_err(dst->f_mapping, since); if (!status) - copy->committed = true; + set_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags); } return bytes_copied; } -static __be32 nfsd4_do_copy(struct nfsd4_copy *copy, bool sync) +static __be32 nfsd4_do_copy(struct nfsd4_copy *copy, + struct file *src, struct file *dst, + bool sync) { __be32 status; ssize_t bytes; - bytes = _nfsd_copy_file_range(copy); + bytes = _nfsd_copy_file_range(copy, dst, src); + /* for async copy, we ignore the error, client can always retry * to get the error */ @@ -1736,13 +1695,6 @@ static __be32 nfsd4_do_copy(struct nfsd4_copy *copy, bool sync) nfsd4_init_copy_res(copy, sync); status = nfs_ok; } - - if (!copy->cp_intra) /* Inter server SSC */ - nfsd4_cleanup_inter_ssc(copy->ss_mnt, copy->nf_src, - copy->nf_dst); - else - nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst); - return status; } @@ -1751,17 +1703,17 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) dst->cp_src_pos = src->cp_src_pos; dst->cp_dst_pos = src->cp_dst_pos; dst->cp_count = src->cp_count; - dst->cp_synchronous = src->cp_synchronous; + dst->cp_flags = src->cp_flags; memcpy(&dst->cp_res, &src->cp_res, sizeof(src->cp_res)); memcpy(&dst->fh, &src->fh, sizeof(src->fh)); dst->cp_clp = src->cp_clp; dst->nf_dst = nfsd_file_get(src->nf_dst); - dst->cp_intra = src->cp_intra; - if (src->cp_intra) /* for inter, file_src doesn't exist yet */ + /* for inter, nf_src doesn't exist yet */ + if (!nfsd4_ssc_is_inter(src)) dst->nf_src = nfsd_file_get(src->nf_src); memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid)); - memcpy(&dst->cp_src, &src->cp_src, sizeof(struct nl4_server)); + memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server)); memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid)); memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh)); dst->ss_mnt = src->ss_mnt; @@ -1771,7 +1723,7 @@ static void cleanup_async_copy(struct nfsd4_copy *copy) { nfs4_free_copy_state(copy); nfsd_file_put(copy->nf_dst); - if (copy->cp_intra) + if (!nfsd4_ssc_is_inter(copy)) nfsd_file_put(copy->nf_src); spin_lock(©->cp_clp->async_lock); list_del(©->copies); @@ -1779,45 +1731,58 @@ static void cleanup_async_copy(struct nfsd4_copy *copy) nfs4_put_copy(copy); } +static void nfsd4_send_cb_offload(struct nfsd4_copy *copy, __be32 nfserr) +{ + struct nfsd4_cb_offload *cbo; + + cbo = kzalloc(sizeof(*cbo), GFP_KERNEL); + if (!cbo) + return; + + memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res)); + memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh)); + cbo->co_nfserr = nfserr; + + nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, + NFSPROC4_CLNT_CB_OFFLOAD); + trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid, + &cbo->co_fh, copy->cp_count, nfserr); + nfsd4_run_cb(&cbo->co_cb); +} + +/** + * nfsd4_do_async_copy - kthread function for background server-side COPY + * @data: arguments for COPY operation + * + * Return values: + * %0: Copy operation is done. + */ static int nfsd4_do_async_copy(void *data) { struct nfsd4_copy *copy = (struct nfsd4_copy *)data; - struct nfsd4_copy *cb_copy; + __be32 nfserr; - if (!copy->cp_intra) { /* Inter server SSC */ - copy->nf_src = kzalloc(sizeof(struct nfsd_file), GFP_KERNEL); - if (!copy->nf_src) { - copy->nfserr = nfserr_serverfault; - nfsd4_interssc_disconnect(copy->ss_mnt); - goto do_callback; - } - copy->nf_src->nf_file = nfs42_ssc_open(copy->ss_mnt, ©->c_fh, - ©->stateid); - if (IS_ERR(copy->nf_src->nf_file)) { - copy->nfserr = nfserr_offload_denied; + if (nfsd4_ssc_is_inter(copy)) { + struct file *filp; + + filp = nfs42_ssc_open(copy->ss_mnt, ©->c_fh, + ©->stateid); + if (IS_ERR(filp)) { + nfserr = nfserr_offload_denied; nfsd4_interssc_disconnect(copy->ss_mnt); goto do_callback; } + nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, + false); + nfsd4_cleanup_inter_ssc(copy->ss_mnt, filp, copy->nf_dst); + } else { + nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, + copy->nf_dst->nf_file, false); + nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst); } - copy->nfserr = nfsd4_do_copy(copy, 0); do_callback: - cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); - if (!cb_copy) - goto out; - refcount_set(&cb_copy->refcount, 1); - memcpy(&cb_copy->cp_res, ©->cp_res, sizeof(copy->cp_res)); - cb_copy->cp_clp = copy->cp_clp; - cb_copy->nfserr = copy->nfserr; - memcpy(&cb_copy->fh, ©->fh, sizeof(copy->fh)); - nfsd4_init_cb(&cb_copy->cp_cb, cb_copy->cp_clp, - &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); - trace_nfsd_cb_offload(copy->cp_clp, ©->cp_res.cb_stateid, - ©->fh, copy->cp_count, copy->nfserr); - nfsd4_run_cb(&cb_copy->cp_cb); -out: - if (!copy->cp_intra) - kfree(copy->nf_src); + nfsd4_send_cb_offload(copy, nfserr); cleanup_async_copy(copy); return 0; } @@ -1830,8 +1795,8 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct nfsd4_copy *async_copy = NULL; - if (!copy->cp_intra) { /* Inter server SSC */ - if (!inter_copy_offload_enable || copy->cp_synchronous) { + if (nfsd4_ssc_is_inter(copy)) { + if (!inter_copy_offload_enable || nfsd4_copy_is_sync(copy)) { status = nfserr_notsupp; goto out; } @@ -1848,13 +1813,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, copy->cp_clp = cstate->clp; memcpy(©->fh, &cstate->current_fh.fh_handle, sizeof(struct knfsd_fh)); - if (!copy->cp_synchronous) { + if (nfsd4_copy_is_async(copy)) { struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); status = nfserrno(-ENOMEM); async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); if (!async_copy) goto out_err; + async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL); + if (!async_copy->cp_src) + goto out_err; if (!nfs4_init_copy_state(nn, copy)) goto out_err; refcount_set(&async_copy->refcount, 1); @@ -1872,7 +1840,9 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, wake_up_process(async_copy->copy_task); status = nfs_ok; } else { - status = nfsd4_do_copy(copy, 1); + status = nfsd4_do_copy(copy, copy->nf_src->nf_file, + copy->nf_dst->nf_file, true); + nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst); } out: return status; @@ -1880,7 +1850,7 @@ out_err: if (async_copy) cleanup_async_copy(async_copy); status = nfserrno(-ENOMEM); - if (!copy->cp_intra) + if (nfsd4_ssc_is_inter(copy)) nfsd4_interssc_disconnect(copy->ss_mnt); goto out; } @@ -1953,9 +1923,9 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* For now, only return one server address in cpn_src, the * address used by the client to connect to this server. */ - cn->cpn_src.nl4_type = NL4_NETADDR; + cn->cpn_src->nl4_type = NL4_NETADDR; status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr, - &cn->cpn_src.u.nl4_addr); + &cn->cpn_src->u.nl4_addr); WARN_ON_ONCE(status); if (status) { nfs4_put_cpntf_state(nn, cps); @@ -2609,7 +2579,7 @@ check_if_stalefh_allowed(struct nfsd4_compoundargs *args) return; } putfh = (struct nfsd4_putfh *)&saved_op->u; - if (!copy->cp_intra) + if (nfsd4_ssc_is_inter(copy)) putfh->no_verify = true; } } @@ -2711,7 +2681,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) if (op->opdesc->op_flags & OP_MODIFIES_SOMETHING) { /* * Don't execute this op if we couldn't encode a - * succesful reply: + * successful reply: */ u32 plen = op->opdesc->op_rsize_bop(rqstp, op); /* diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9409a0dc1b76..c5d199d7e6b4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -820,9 +820,9 @@ static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag) swap(f2, fp->fi_fds[O_RDWR]); spin_unlock(&fp->fi_lock); if (f1) - nfsd_file_put(f1); + nfsd_file_close(f1); if (f2) - nfsd_file_put(f2); + nfsd_file_close(f2); } } @@ -1131,7 +1131,6 @@ static void block_delegations(struct knfsd_fh *fh) static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp, - struct svc_fh *current_fh, struct nfs4_clnt_odstate *odstate) { struct nfs4_delegation *dp; @@ -1141,7 +1140,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp, n = atomic_long_inc_return(&num_delegations); if (n < 0 || n > max_delegations) goto out_dec; - if (delegation_blocked(¤t_fh->fh_handle)) + if (delegation_blocked(&fp->fi_fhandle)) goto out_dec; dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg)); if (dp == NULL) @@ -2053,11 +2052,16 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn) * This type of memory management is somewhat inefficient, but we use it * anyway since SETCLIENTID is not a common operation. */ -static struct nfs4_client *alloc_client(struct xdr_netobj name) +static struct nfs4_client *alloc_client(struct xdr_netobj name, + struct nfsd_net *nn) { struct nfs4_client *clp; int i; + if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) { + mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); + return NULL; + } clp = kmem_cache_zalloc(client_slab, GFP_KERNEL); if (clp == NULL) return NULL; @@ -2076,6 +2080,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) atomic_set(&clp->cl_rpc_users, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN; clp->cl_state = NFSD4_ACTIVE; + atomic_inc(&nn->nfs4_client_count); atomic_set(&clp->cl_delegs_in_recall, 0); INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_openowners); @@ -2183,6 +2188,7 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp) static void __destroy_client(struct nfs4_client *clp) { + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); int i; struct nfs4_openowner *oo; struct nfs4_delegation *dp; @@ -2226,6 +2232,7 @@ __destroy_client(struct nfs4_client *clp) nfsd4_shutdown_callback(clp); if (clp->cl_cb_conn.cb_xprt) svc_xprt_put(clp->cl_cb_conn.cb_xprt); + atomic_add_unless(&nn->nfs4_client_count, -1, 0); free_client(clp); wake_up_all(&expiry_wq); } @@ -2564,7 +2571,7 @@ static void nfs4_show_fname(struct seq_file *s, struct nfsd_file *f) static void nfs4_show_superblock(struct seq_file *s, struct nfsd_file *f) { - struct inode *inode = f->nf_inode; + struct inode *inode = file_inode(f->nf_file); seq_printf(s, "superblock: \"%02x:%02x:%ld\"", MAJOR(inode->i_sb->s_dev), @@ -2848,7 +2855,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct dentry *dentries[ARRAY_SIZE(client_files)]; - clp = alloc_client(name); + clp = alloc_client(name, nn); if (clp == NULL) return NULL; @@ -4330,6 +4337,27 @@ out: return -ENOMEM; } +void nfsd4_init_leases_net(struct nfsd_net *nn) +{ + struct sysinfo si; + u64 max_clients; + + nn->nfsd4_lease = 90; /* default lease time */ + nn->nfsd4_grace = 90; + nn->somebody_reclaimed = false; + nn->track_reclaim_completes = false; + nn->clverifier_counter = prandom_u32(); + nn->clientid_base = prandom_u32(); + nn->clientid_counter = nn->clientid_base + 1; + nn->s2s_cp_cl_id = nn->clientid_counter++; + + atomic_set(&nn->nfs4_client_count, 0); + si_meminfo(&si); + max_clients = (u64)si.totalram * si.mem_unit / (1024 * 1024 * 1024); + max_clients *= NFS4_CLIENTS_PER_GB; + nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB); +} + static void init_nfs4_replay(struct nfs4_replay *rp) { rp->rp_status = nfserr_serverfault; @@ -5032,11 +5060,14 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, .ia_valid = ATTR_SIZE, .ia_size = 0, }; + struct nfsd_attrs attrs = { + .na_iattr = &iattr, + }; if (!open->op_truncate) return 0; if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) return nfserr_inval; - return nfsd_setattr(rqstp, fh, &iattr, 0, (time64_t)0); + return nfsd_setattr(rqstp, fh, &attrs, 0, (time64_t)0); } static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, @@ -5104,6 +5135,7 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, goto out_put_access; nf->nf_file = open->op_filp; open->op_filp = NULL; + trace_nfsd_file_create(rqstp, access, nf); } spin_lock(&fp->fi_lock); @@ -5259,11 +5291,41 @@ static int nfsd4_check_conflicting_opens(struct nfs4_client *clp, return 0; } +/* + * It's possible that between opening the dentry and setting the delegation, + * that it has been renamed or unlinked. Redo the lookup to verify that this + * hasn't happened. + */ +static int +nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp, + struct svc_fh *parent) +{ + struct svc_export *exp; + struct dentry *child; + __be32 err; + + err = nfsd_lookup_dentry(open->op_rqstp, parent, + open->op_fname, open->op_fnamelen, + &exp, &child); + + if (err) + return -EAGAIN; + + dput(child); + if (child != file_dentry(fp->fi_deleg_file->nf_file)) + return -EAGAIN; + + return 0; +} + static struct nfs4_delegation * -nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, - struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate) +nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, + struct svc_fh *parent) { int status = 0; + struct nfs4_client *clp = stp->st_stid.sc_client; + struct nfs4_file *fp = stp->st_stid.sc_file; + struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate; struct nfs4_delegation *dp; struct nfsd_file *nf; struct file_lock *fl; @@ -5305,7 +5367,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, return ERR_PTR(status); status = -ENOMEM; - dp = alloc_init_deleg(clp, fp, fh, odstate); + dp = alloc_init_deleg(clp, fp, odstate); if (!dp) goto out_delegees; @@ -5318,6 +5380,13 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, locks_free_lock(fl); if (status) goto out_clnt_odstate; + + if (parent) { + status = nfsd4_verify_deleg_dentry(open, fp, parent); + if (status) + goto out_unlock; + } + status = nfsd4_check_conflicting_opens(clp, fp); if (status) goto out_unlock; @@ -5373,12 +5442,13 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) * proper support for them. */ static void -nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, - struct nfs4_ol_stateid *stp) +nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, + struct svc_fh *currentfh) { struct nfs4_delegation *dp; struct nfs4_openowner *oo = openowner(stp->st_stateowner); struct nfs4_client *clp = stp->st_stid.sc_client; + struct svc_fh *parent = NULL; int cb_up; int status = 0; @@ -5392,6 +5462,8 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, goto out_no_deleg; break; case NFS4_OPEN_CLAIM_NULL: + parent = currentfh; + fallthrough; case NFS4_OPEN_CLAIM_FH: /* * Let's not give out any delegations till everyone's @@ -5406,7 +5478,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, default: goto out_no_deleg; } - dp = nfs4_set_delegation(clp, fh, stp->st_stid.sc_file, stp->st_clnt_odstate); + dp = nfs4_set_delegation(open, stp, parent); if (IS_ERR(dp)) goto out_no_deleg; @@ -5538,7 +5610,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf * Attempt to hand out a delegation. No error return, because the * OPEN succeeds even if we fail. */ - nfs4_open_delegation(current_fh, open, stp); + nfs4_open_delegation(open, stp, &resp->cstate.current_fh); nodeleg: status = nfs_ok; trace_nfsd_open(&stp->st_stid.sc_stateid); @@ -5792,9 +5864,12 @@ static void nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist, struct laundry_time *lt) { + unsigned int maxreap, reapcnt = 0; struct list_head *pos, *next; struct nfs4_client *clp; + maxreap = (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) ? + NFSD_CLIENT_MAX_TRIM_PER_RUN : 0; INIT_LIST_HEAD(reaplist); spin_lock(&nn->client_lock); list_for_each_safe(pos, next, &nn->client_lru) { @@ -5805,14 +5880,15 @@ nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist, break; if (!atomic_read(&clp->cl_rpc_users)) clp->cl_state = NFSD4_COURTESY; - if (!client_has_state(clp) || - ktime_get_boottime_seconds() >= - (clp->cl_time + NFSD_COURTESY_CLIENT_TIMEOUT)) + if (!client_has_state(clp)) goto exp_client; - if (nfs4_anylock_blockers(clp)) { + if (!nfs4_anylock_blockers(clp)) + if (reapcnt >= maxreap) + continue; exp_client: - if (!mark_client_expired_locked(clp)) - list_add(&clp->cl_lru, reaplist); + if (!mark_client_expired_locked(clp)) { + list_add(&clp->cl_lru, reaplist); + reapcnt++; } } spin_unlock(&nn->client_lock); @@ -7321,21 +7397,22 @@ out: static __be32 nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) { struct nfsd_file *nf; + struct inode *inode; __be32 err; err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_READ, &nf); if (err) return err; - fh_lock(fhp); /* to block new leases till after test_lock: */ - err = nfserrno(nfsd_open_break_lease(fhp->fh_dentry->d_inode, - NFSD_MAY_READ)); + inode = fhp->fh_dentry->d_inode; + inode_lock(inode); /* to block new leases till after test_lock: */ + err = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); if (err) goto out; lock->fl_file = nf->nf_file; err = nfserrno(vfs_test_lock(nf->nf_file, lock)); lock->fl_file = NULL; out: - fh_unlock(fhp); + inode_unlock(inode); nfsd_file_put(nf); return err; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 2acea7792bb2..1e9690a061ec 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1810,7 +1810,7 @@ nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_sta for (i = 0; i < test_stateid->ts_num_ids; i++) { stateid = svcxdr_tmpalloc(argp, sizeof(*stateid)); if (!stateid) - return nfserrno(-ENOMEM); /* XXX: not jukebox? */ + return nfserr_jukebox; INIT_LIST_HEAD(&stateid->ts_id_list); list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid); @@ -1896,8 +1896,8 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp, static __be32 nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy) { + u32 consecutive, i, count, sync; struct nl4_server *ns_dummy; - u32 consecutive, i, count; __be32 status; status = nfsd4_decode_stateid4(argp, ©->cp_src_stateid); @@ -1915,25 +1915,28 @@ nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy) /* ca_consecutive: we always do consecutive copies */ if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0) return nfserr_bad_xdr; - if (xdr_stream_decode_u32(argp->xdr, ©->cp_synchronous) < 0) + if (xdr_stream_decode_bool(argp->xdr, &sync) < 0) return nfserr_bad_xdr; + nfsd4_copy_set_sync(copy, sync); if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; - copy->cp_intra = false; + copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src)); + if (copy->cp_src == NULL) + return nfserr_jukebox; if (count == 0) { /* intra-server copy */ - copy->cp_intra = true; + __set_bit(NFSD4_COPY_F_INTRA, ©->cp_flags); return nfs_ok; } /* decode all the supplied server addresses but use only the first */ - status = nfsd4_decode_nl4_server(argp, ©->cp_src); + status = nfsd4_decode_nl4_server(argp, copy->cp_src); if (status) return status; ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL); if (ns_dummy == NULL) - return nfserrno(-ENOMEM); /* XXX: jukebox? */ + return nfserr_jukebox; for (i = 0; i < count - 1; i++) { status = nfsd4_decode_nl4_server(argp, ns_dummy); if (status) { @@ -1952,10 +1955,17 @@ nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp, { __be32 status; + cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src)); + if (cn->cpn_src == NULL) + return nfserr_jukebox; + cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst)); + if (cn->cpn_dst == NULL) + return nfserr_jukebox; + status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid); if (status) return status; - return nfsd4_decode_nl4_server(argp, &cn->cpn_dst); + return nfsd4_decode_nl4_server(argp, cn->cpn_dst); } static __be32 @@ -2828,10 +2838,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, struct kstat stat; struct svc_fh *tempfh = NULL; struct kstatfs statfs; - __be32 *p; + __be32 *p, *attrlen_p; int starting_len = xdr->buf->len; int attrlen_offset; - __be32 attrlen; u32 dummy; u64 dummy64; u32 rdattr_err = 0; @@ -2919,10 +2928,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, goto out; attrlen_offset = xdr->buf->len; - p = xdr_reserve_space(xdr, 4); - if (!p) + attrlen_p = xdr_reserve_space(xdr, XDR_UNIT); + if (!attrlen_p) goto out_resource; - p++; /* to be backfilled later */ if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { u32 supp[3]; @@ -3344,8 +3352,7 @@ out_acl: *p++ = cpu_to_be32(err == 0); } - attrlen = htonl(xdr->buf->len - attrlen_offset - 4); - write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4); + *attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT); status = nfs_ok; out: @@ -3882,16 +3889,15 @@ static __be32 nfsd4_encode_splice_read( struct xdr_stream *xdr = resp->xdr; struct xdr_buf *buf = xdr->buf; int status, space_left; - u32 eof; __be32 nfserr; - __be32 *p = xdr->p - 2; /* Make sure there will be room for padding if needed */ if (xdr->end - xdr->p < 1) return nfserr_resource; nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp, - file, read->rd_offset, &maxcount, &eof); + file, read->rd_offset, &maxcount, + &read->rd_eof); read->rd_length = maxcount; if (nfserr) goto out_err; @@ -3902,9 +3908,6 @@ static __be32 nfsd4_encode_splice_read( goto out_err; } - *(p++) = htonl(eof); - *(p++) = htonl(maxcount); - buf->page_len = maxcount; buf->len += maxcount; xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1) @@ -3946,11 +3949,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, struct file *file, unsigned long maxcount) { struct xdr_stream *xdr = resp->xdr; - u32 eof; - int starting_len = xdr->buf->len - 8; + unsigned int starting_len = xdr->buf->len; + __be32 zero = xdr_zero; __be32 nfserr; - __be32 tmp; - int pad; read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount); if (read->rd_vlen < 0) @@ -3958,31 +3959,24 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, &maxcount, - &eof); + &read->rd_eof); read->rd_length = maxcount; if (nfserr) return nfserr; - if (svc_encode_result_payload(resp->rqstp, starting_len + 8, maxcount)) + if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount)) return nfserr_io; - xdr_truncate_encode(xdr, starting_len + 8 + xdr_align_size(maxcount)); - - tmp = htonl(eof); - write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4); - tmp = htonl(maxcount); - write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); - - tmp = xdr_zero; - pad = (maxcount&3) ? 4 - (maxcount&3) : 0; - write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount, - &tmp, pad); - return 0; + xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount)); + write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero, + xdr_pad_size(maxcount)); + return nfs_ok; } static __be32 nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_read *read) { + bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags); unsigned long maxcount; struct xdr_stream *xdr = resp->xdr; struct file *file; @@ -3995,11 +3989,10 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */ if (!p) { - WARN_ON_ONCE(test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)); + WARN_ON_ONCE(splice_ok); return nfserr_resource; } - if (resp->xdr->buf->page_len && - test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) { + if (resp->xdr->buf->page_len && splice_ok) { WARN_ON_ONCE(1); return nfserr_resource; } @@ -4008,31 +4001,30 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, maxcount = min_t(unsigned long, read->rd_length, (xdr->buf->buflen - xdr->buf->len)); - if (file->f_op->splice_read && - test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) + if (file->f_op->splice_read && splice_ok) nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); else nfserr = nfsd4_encode_readv(resp, read, file, maxcount); - - if (nfserr) + if (nfserr) { xdr_truncate_encode(xdr, starting_len); + return nfserr; + } - return nfserr; + p = xdr_encode_bool(p, read->rd_eof); + *p = cpu_to_be32(read->rd_length); + return nfs_ok; } static __be32 nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) { - int maxcount; - __be32 wire_count; - int zero = 0; + __be32 *p, *maxcount_p, zero = xdr_zero; struct xdr_stream *xdr = resp->xdr; int length_offset = xdr->buf->len; - int status; - __be32 *p; + int maxcount, status; - p = xdr_reserve_space(xdr, 4); - if (!p) + maxcount_p = xdr_reserve_space(xdr, XDR_UNIT); + if (!maxcount_p) return nfserr_resource; maxcount = PAGE_SIZE; @@ -4057,14 +4049,11 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd nfserr = nfserrno(status); goto out_err; } - - wire_count = htonl(maxcount); - write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4); - xdr_truncate_encode(xdr, length_offset + 4 + ALIGN(maxcount, 4)); - if (maxcount & 3) - write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, - &zero, 4 - (maxcount&3)); - return 0; + *maxcount_p = cpu_to_be32(maxcount); + xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount)); + write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero, + xdr_pad_size(maxcount)); + return nfs_ok; out_err: xdr_truncate_encode(xdr, length_offset); @@ -4715,13 +4704,13 @@ nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr, __be32 *p; nfserr = nfsd42_encode_write_res(resp, ©->cp_res, - !!copy->cp_synchronous); + nfsd4_copy_is_sync(copy)); if (nfserr) return nfserr; p = xdr_reserve_space(resp->xdr, 4 + 4); *p++ = xdr_one; /* cr_consecutive */ - *p++ = cpu_to_be32(copy->cp_synchronous); + *p = nfsd4_copy_is_sync(copy) ? xdr_one : xdr_zero; return 0; } @@ -4919,7 +4908,8 @@ nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr, *p++ = cpu_to_be32(1); - return nfsd42_encode_nl4_server(resp, &cn->cpn_src); + nfserr = nfsd42_encode_nl4_server(resp, cn->cpn_src); + return nfserr; } static __be32 @@ -5373,8 +5363,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) so->so_replay.rp_buf, len); } status: - /* Note that op->status is already in network byte order: */ - write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4); + *p = op->status; } /* diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 0621c2faf242..917fa1892fd2 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -25,6 +25,7 @@ #include "state.h" #include "netns.h" #include "pnfs.h" +#include "filecache.h" /* * We have a single directory with several nodes in it. @@ -45,6 +46,7 @@ enum { NFSD_Ports, NFSD_MaxBlkSize, NFSD_MaxConnections, + NFSD_Filecache, NFSD_SupportedEnctypes, /* * The below MUST come last. Otherwise we leave a hole in nfsd_files[] @@ -229,6 +231,13 @@ static const struct file_operations reply_cache_stats_operations = { .release = single_release, }; +static const struct file_operations filecache_ops = { + .open = nfsd_file_cache_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /*----------------------------------------------------------------------------*/ /* * payload - write methods @@ -633,7 +642,6 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) } /* Now write current state into reply buffer */ - len = 0; sep = ""; remaining = SIMPLE_TRANSACTION_LIMIT; for (num=2 ; num <= 4 ; num++) { @@ -1371,6 +1379,7 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO}, + [NFSD_Filecache] = {"filecache", &filecache_ops, S_IRUGO}, #if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE) [NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO}, #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ @@ -1475,14 +1484,7 @@ static __net_init int nfsd_init_net(struct net *net) retval = nfsd_reply_cache_init(nn); if (retval) goto out_drc_error; - nn->nfsd4_lease = 90; /* default lease time */ - nn->nfsd4_grace = 90; - nn->somebody_reclaimed = false; - nn->track_reclaim_completes = false; - nn->clverifier_counter = prandom_u32(); - nn->clientid_base = prandom_u32(); - nn->clientid_counter = nn->clientid_base + 1; - nn->s2s_cp_cl_id = nn->clientid_counter++; + nfsd4_init_leases_net(nn); get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); seqlock_init(&nn->writeverf_lock); @@ -1517,7 +1519,6 @@ static struct pernet_operations nfsd_net_ops = { static int __init init_nfsd(void) { int retval; - printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); retval = nfsd4_init_slabs(); if (retval) diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 9a8b09afc173..57a468ed85c3 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -341,6 +341,8 @@ void nfsd_lockd_shutdown(void); #define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */ #define NFSD_COURTESY_CLIENT_TIMEOUT (24 * 60 * 60) /* seconds */ +#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128 +#define NFS4_CLIENTS_PER_GB 1024 /* * The following attributes are currently not supported by the NFSv4 server: @@ -496,12 +498,16 @@ extern void unregister_cld_notifier(void); extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn); #endif +extern void nfsd4_init_leases_net(struct nfsd_net *nn); + #else /* CONFIG_NFSD_V4 */ static inline int nfsd4_is_junction(struct dentry *dentry) { return 0; } +static inline void nfsd4_init_leases_net(struct nfsd_net *nn) {}; + #define register_cld_notifier() 0 #define unregister_cld_notifier() do { } while(0) diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index c29baa03dfaf..a5b71526cee0 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -331,8 +331,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access) struct dentry *dentry; __be32 error; - dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); - if (!fhp->fh_dentry) { error = nfsd_set_fh_dentry(rqstp, fhp); if (error) @@ -340,6 +338,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access) } dentry = fhp->fh_dentry; exp = fhp->fh_export; + + trace_nfsd_fh_verify(rqstp, fhp, type, access); + /* * We still have to do all these permission checks, even when * fh_dentry is already set: @@ -548,7 +549,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, if (ref_fh == fhp) fh_put(ref_fh); - if (fhp->fh_locked || fhp->fh_dentry) { + if (fhp->fh_dentry) { printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n", dentry); } @@ -671,6 +672,25 @@ void fh_fill_post_attrs(struct svc_fh *fhp) nfsd4_change_attribute(&fhp->fh_post_attr, inode); } +/** + * fh_fill_both_attrs - Fill pre-op and post-op attributes + * @fhp: file handle to be updated + * + * This is used when the directory wasn't changed, but wcc attributes + * are needed anyway. + */ +void fh_fill_both_attrs(struct svc_fh *fhp) +{ + fh_fill_post_attrs(fhp); + if (!fhp->fh_post_saved) + return; + fhp->fh_pre_change = fhp->fh_post_change; + fhp->fh_pre_mtime = fhp->fh_post_attr.mtime; + fhp->fh_pre_ctime = fhp->fh_post_attr.ctime; + fhp->fh_pre_size = fhp->fh_post_attr.size; + fhp->fh_pre_saved = true; +} + /* * Release a file handle. */ @@ -680,7 +700,6 @@ fh_put(struct svc_fh *fhp) struct dentry * dentry = fhp->fh_dentry; struct svc_export * exp = fhp->fh_export; if (dentry) { - fh_unlock(fhp); fhp->fh_dentry = NULL; dput(dentry); fh_clear_pre_post_attrs(fhp); diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index fb9d358a267e..c3ae6414fc5c 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -81,7 +81,6 @@ typedef struct svc_fh { struct dentry * fh_dentry; /* validated dentry */ struct svc_export * fh_export; /* export pointer */ - bool fh_locked; /* inode locked by us */ bool fh_want_write; /* remount protection taken */ bool fh_no_wcc; /* no wcc data needed */ bool fh_no_atomic_attr; @@ -93,7 +92,7 @@ typedef struct svc_fh { bool fh_post_saved; /* post-op attrs saved */ bool fh_pre_saved; /* pre-op attrs saved */ - /* Pre-op attributes saved during fh_lock */ + /* Pre-op attributes saved when inode is locked */ __u64 fh_pre_size; /* size before operation */ struct timespec64 fh_pre_mtime; /* mtime before oper */ struct timespec64 fh_pre_ctime; /* ctime before oper */ @@ -103,7 +102,7 @@ typedef struct svc_fh { */ u64 fh_pre_change; - /* Post-op attributes saved in fh_unlock */ + /* Post-op attributes saved in fh_fill_post_attrs() */ struct kstat fh_post_attr; /* full attrs after operation */ u64 fh_post_change; /* nfsv4 change; see above */ } svc_fh; @@ -223,8 +222,8 @@ void fh_put(struct svc_fh *); static __inline__ struct svc_fh * fh_copy(struct svc_fh *dst, struct svc_fh *src) { - WARN_ON(src->fh_dentry || src->fh_locked); - + WARN_ON(src->fh_dentry); + *dst = *src; return dst; } @@ -322,52 +321,5 @@ static inline u64 nfsd4_change_attribute(struct kstat *stat, extern void fh_fill_pre_attrs(struct svc_fh *fhp); extern void fh_fill_post_attrs(struct svc_fh *fhp); - - -/* - * Lock a file handle/inode - * NOTE: both fh_lock and fh_unlock are done "by hand" in - * vfs.c:nfsd_rename as it needs to grab 2 i_mutex's at once - * so, any changes here should be reflected there. - */ - -static inline void -fh_lock_nested(struct svc_fh *fhp, unsigned int subclass) -{ - struct dentry *dentry = fhp->fh_dentry; - struct inode *inode; - - BUG_ON(!dentry); - - if (fhp->fh_locked) { - printk(KERN_WARNING "fh_lock: %pd2 already locked!\n", - dentry); - return; - } - - inode = d_inode(dentry); - inode_lock_nested(inode, subclass); - fh_fill_pre_attrs(fhp); - fhp->fh_locked = true; -} - -static inline void -fh_lock(struct svc_fh *fhp) -{ - fh_lock_nested(fhp, I_MUTEX_NORMAL); -} - -/* - * Unlock a file handle/inode - */ -static inline void -fh_unlock(struct svc_fh *fhp) -{ - if (fhp->fh_locked) { - fh_fill_post_attrs(fhp); - inode_unlock(d_inode(fhp->fh_dentry)); - fhp->fh_locked = false; - } -} - +extern void fh_fill_both_attrs(struct svc_fh *fhp); #endif /* _LINUX_NFSD_NFSFH_H */ diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index fcdab8a8a41f..7381972f1677 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -51,6 +51,9 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) struct nfsd_sattrargs *argp = rqstp->rq_argp; struct nfsd_attrstat *resp = rqstp->rq_resp; struct iattr *iap = &argp->attrs; + struct nfsd_attrs attrs = { + .na_iattr = iap, + }; struct svc_fh *fhp; dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", @@ -100,7 +103,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) } } - resp->status = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0); + resp->status = nfsd_setattr(rqstp, fhp, &attrs, 0, (time64_t)0); if (resp->status != nfs_ok) goto out; @@ -260,6 +263,9 @@ nfsd_proc_create(struct svc_rqst *rqstp) svc_fh *dirfhp = &argp->fh; svc_fh *newfhp = &resp->fh; struct iattr *attr = &argp->attrs; + struct nfsd_attrs attrs = { + .na_iattr = attr, + }; struct inode *inode; struct dentry *dchild; int type, mode; @@ -285,7 +291,7 @@ nfsd_proc_create(struct svc_rqst *rqstp) goto done; } - fh_lock_nested(dirfhp, I_MUTEX_PARENT); + inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT); dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); if (IS_ERR(dchild)) { resp->status = nfserrno(PTR_ERR(dchild)); @@ -385,7 +391,7 @@ nfsd_proc_create(struct svc_rqst *rqstp) if (!inode) { /* File doesn't exist. Create it and set attrs */ resp->status = nfsd_create_locked(rqstp, dirfhp, argp->name, - argp->len, attr, type, rdev, + argp->len, &attrs, type, rdev, newfhp); } else if (type == S_IFREG) { dprintk("nfsd: existing %s, valid=%x, size=%ld\n", @@ -396,13 +402,12 @@ nfsd_proc_create(struct svc_rqst *rqstp) */ attr->ia_valid &= ATTR_SIZE; if (attr->ia_valid) - resp->status = nfsd_setattr(rqstp, newfhp, attr, 0, + resp->status = nfsd_setattr(rqstp, newfhp, &attrs, 0, (time64_t)0); } out_unlock: - /* We don't really need to unlock, as fh_put does it. */ - fh_unlock(dirfhp); + inode_unlock(dirfhp->fh_dentry->d_inode); fh_drop_write(dirfhp); done: fh_put(dirfhp); @@ -472,6 +477,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp) { struct nfsd_symlinkargs *argp = rqstp->rq_argp; struct nfsd_stat *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; struct svc_fh newfh; if (argp->tlen > NFS_MAXPATHLEN) { @@ -493,7 +501,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp) fh_init(&newfh, NFS_FHSIZE); resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, - argp->tname, &newfh); + argp->tname, &attrs, &newfh); kfree(argp->tname); fh_put(&argp->ffh); @@ -511,6 +519,9 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp) { struct nfsd_createargs *argp = rqstp->rq_argp; struct nfsd_diropres *resp = rqstp->rq_resp; + struct nfsd_attrs attrs = { + .na_iattr = &argp->attrs, + }; dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name); @@ -522,7 +533,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp) argp->attrs.ia_valid &= ~ATTR_SIZE; fh_init(&resp->fh, NFS_FHSIZE); resp->status = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, - &argp->attrs, S_IFDIR, 0, &resp->fh); + &attrs, S_IFDIR, 0, &resp->fh); fh_put(&argp->fh); if (resp->status != nfs_ok) goto out; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index f3d6313914ed..ae596dbf8667 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -703,7 +703,6 @@ extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); void put_nfs4_file(struct nfs4_file *fi); -extern void nfs4_put_copy(struct nfsd4_copy *copy); extern struct nfsd4_copy * find_async_copy(struct nfs4_client *clp, stateid_t *staetid); extern void nfs4_put_cpntf_state(struct nfsd_net *nn, diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index a60ead3b227a..9ebd67d461f9 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -171,6 +171,52 @@ TRACE_EVENT(nfsd_compound_encode_err, __entry->opnum, __entry->status) ); +#define show_fs_file_type(x) \ + __print_symbolic(x, \ + { S_IFLNK, "LNK" }, \ + { S_IFREG, "REG" }, \ + { S_IFDIR, "DIR" }, \ + { S_IFCHR, "CHR" }, \ + { S_IFBLK, "BLK" }, \ + { S_IFIFO, "FIFO" }, \ + { S_IFSOCK, "SOCK" }) + +TRACE_EVENT(nfsd_fh_verify, + TP_PROTO( + const struct svc_rqst *rqstp, + const struct svc_fh *fhp, + umode_t type, + int access + ), + TP_ARGS(rqstp, fhp, type, access), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __sockaddr(server, rqstp->rq_xprt->xpt_remotelen) + __sockaddr(client, rqstp->rq_xprt->xpt_remotelen) + __field(u32, xid) + __field(u32, fh_hash) + __field(void *, inode) + __field(unsigned long, type) + __field(unsigned long, access) + ), + TP_fast_assign( + __entry->netns_ino = SVC_NET(rqstp)->ns.inum; + __assign_sockaddr(server, &rqstp->rq_xprt->xpt_local, + rqstp->rq_xprt->xpt_locallen); + __assign_sockaddr(client, &rqstp->rq_xprt->xpt_remote, + rqstp->rq_xprt->xpt_remotelen); + __entry->xid = be32_to_cpu(rqstp->rq_xid); + __entry->fh_hash = knfsd_fh_hash(&fhp->fh_handle); + __entry->inode = d_inode(fhp->fh_dentry); + __entry->type = type; + __entry->access = access; + ), + TP_printk("xid=0x%08x fh_hash=0x%08x inode=%p type=%s access=%s", + __entry->xid, __entry->fh_hash, __entry->inode, + show_fs_file_type(__entry->type), + show_nfsd_may_flags(__entry->access) + ) +); DECLARE_EVENT_CLASS(nfsd_fh_err_class, TP_PROTO(struct svc_rqst *rqstp, @@ -696,15 +742,12 @@ DEFINE_CLID_EVENT(confirmed_r); __print_flags(val, "|", \ { 1 << NFSD_FILE_HASHED, "HASHED" }, \ { 1 << NFSD_FILE_PENDING, "PENDING" }, \ - { 1 << NFSD_FILE_BREAK_READ, "BREAK_READ" }, \ - { 1 << NFSD_FILE_BREAK_WRITE, "BREAK_WRITE" }, \ { 1 << NFSD_FILE_REFERENCED, "REFERENCED"}) DECLARE_EVENT_CLASS(nfsd_file_class, TP_PROTO(struct nfsd_file *nf), TP_ARGS(nf), TP_STRUCT__entry( - __field(unsigned int, nf_hashval) __field(void *, nf_inode) __field(int, nf_ref) __field(unsigned long, nf_flags) @@ -712,15 +755,13 @@ DECLARE_EVENT_CLASS(nfsd_file_class, __field(struct file *, nf_file) ), TP_fast_assign( - __entry->nf_hashval = nf->nf_hashval; __entry->nf_inode = nf->nf_inode; __entry->nf_ref = refcount_read(&nf->nf_ref); __entry->nf_flags = nf->nf_flags; __entry->nf_may = nf->nf_may; __entry->nf_file = nf->nf_file; ), - TP_printk("hash=0x%x inode=%p ref=%d flags=%s may=%s file=%p", - __entry->nf_hashval, + TP_printk("inode=%p ref=%d flags=%s may=%s nf_file=%p", __entry->nf_inode, __entry->nf_ref, show_nf_flags(__entry->nf_flags), @@ -733,34 +774,59 @@ DEFINE_EVENT(nfsd_file_class, name, \ TP_PROTO(struct nfsd_file *nf), \ TP_ARGS(nf)) -DEFINE_NFSD_FILE_EVENT(nfsd_file_alloc); DEFINE_NFSD_FILE_EVENT(nfsd_file_put_final); DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash); DEFINE_NFSD_FILE_EVENT(nfsd_file_put); -DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_release_locked); +DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_dispose); + +TRACE_EVENT(nfsd_file_alloc, + TP_PROTO( + const struct nfsd_file *nf + ), + TP_ARGS(nf), + TP_STRUCT__entry( + __field(const void *, nf_inode) + __field(unsigned long, nf_flags) + __field(unsigned long, nf_may) + __field(unsigned int, nf_ref) + ), + TP_fast_assign( + __entry->nf_inode = nf->nf_inode; + __entry->nf_flags = nf->nf_flags; + __entry->nf_ref = refcount_read(&nf->nf_ref); + __entry->nf_may = nf->nf_may; + ), + TP_printk("inode=%p ref=%u flags=%s may=%s", + __entry->nf_inode, __entry->nf_ref, + show_nf_flags(__entry->nf_flags), + show_nfsd_may_flags(__entry->nf_may) + ) +); TRACE_EVENT(nfsd_file_acquire, - TP_PROTO(struct svc_rqst *rqstp, unsigned int hash, - struct inode *inode, unsigned int may_flags, - struct nfsd_file *nf, __be32 status), + TP_PROTO( + const struct svc_rqst *rqstp, + const struct inode *inode, + unsigned int may_flags, + const struct nfsd_file *nf, + __be32 status + ), - TP_ARGS(rqstp, hash, inode, may_flags, nf, status), + TP_ARGS(rqstp, inode, may_flags, nf, status), TP_STRUCT__entry( __field(u32, xid) - __field(unsigned int, hash) - __field(void *, inode) + __field(const void *, inode) __field(unsigned long, may_flags) - __field(int, nf_ref) + __field(unsigned int, nf_ref) __field(unsigned long, nf_flags) __field(unsigned long, nf_may) - __field(struct file *, nf_file) + __field(const void *, nf_file) __field(u32, status) ), TP_fast_assign( __entry->xid = be32_to_cpu(rqstp->rq_xid); - __entry->hash = hash; __entry->inode = inode; __entry->may_flags = may_flags; __entry->nf_ref = nf ? refcount_read(&nf->nf_ref) : 0; @@ -770,19 +836,117 @@ TRACE_EVENT(nfsd_file_acquire, __entry->status = be32_to_cpu(status); ), - TP_printk("xid=0x%x hash=0x%x inode=%p may_flags=%s ref=%d nf_flags=%s nf_may=%s nf_file=%p status=%u", - __entry->xid, __entry->hash, __entry->inode, + TP_printk("xid=0x%x inode=%p may_flags=%s ref=%u nf_flags=%s nf_may=%s nf_file=%p status=%u", + __entry->xid, __entry->inode, show_nfsd_may_flags(__entry->may_flags), __entry->nf_ref, show_nf_flags(__entry->nf_flags), show_nfsd_may_flags(__entry->nf_may), - __entry->nf_file, __entry->status) + __entry->nf_file, __entry->status + ) +); + +TRACE_EVENT(nfsd_file_create, + TP_PROTO( + const struct svc_rqst *rqstp, + unsigned int may_flags, + const struct nfsd_file *nf + ), + + TP_ARGS(rqstp, may_flags, nf), + + TP_STRUCT__entry( + __field(const void *, nf_inode) + __field(const void *, nf_file) + __field(unsigned long, may_flags) + __field(unsigned long, nf_flags) + __field(unsigned long, nf_may) + __field(unsigned int, nf_ref) + __field(u32, xid) + ), + + TP_fast_assign( + __entry->nf_inode = nf->nf_inode; + __entry->nf_file = nf->nf_file; + __entry->may_flags = may_flags; + __entry->nf_flags = nf->nf_flags; + __entry->nf_may = nf->nf_may; + __entry->nf_ref = refcount_read(&nf->nf_ref); + __entry->xid = be32_to_cpu(rqstp->rq_xid); + ), + + TP_printk("xid=0x%x inode=%p may_flags=%s ref=%u nf_flags=%s nf_may=%s nf_file=%p", + __entry->xid, __entry->nf_inode, + show_nfsd_may_flags(__entry->may_flags), + __entry->nf_ref, show_nf_flags(__entry->nf_flags), + show_nfsd_may_flags(__entry->nf_may), __entry->nf_file + ) +); + +TRACE_EVENT(nfsd_file_insert_err, + TP_PROTO( + const struct svc_rqst *rqstp, + const struct inode *inode, + unsigned int may_flags, + long error + ), + TP_ARGS(rqstp, inode, may_flags, error), + TP_STRUCT__entry( + __field(u32, xid) + __field(const void *, inode) + __field(unsigned long, may_flags) + __field(long, error) + ), + TP_fast_assign( + __entry->xid = be32_to_cpu(rqstp->rq_xid); + __entry->inode = inode; + __entry->may_flags = may_flags; + __entry->error = error; + ), + TP_printk("xid=0x%x inode=%p may_flags=%s error=%ld", + __entry->xid, __entry->inode, + show_nfsd_may_flags(__entry->may_flags), + __entry->error + ) +); + +TRACE_EVENT(nfsd_file_cons_err, + TP_PROTO( + const struct svc_rqst *rqstp, + const struct inode *inode, + unsigned int may_flags, + const struct nfsd_file *nf + ), + TP_ARGS(rqstp, inode, may_flags, nf), + TP_STRUCT__entry( + __field(u32, xid) + __field(const void *, inode) + __field(unsigned long, may_flags) + __field(unsigned int, nf_ref) + __field(unsigned long, nf_flags) + __field(unsigned long, nf_may) + __field(const void *, nf_file) + ), + TP_fast_assign( + __entry->xid = be32_to_cpu(rqstp->rq_xid); + __entry->inode = inode; + __entry->may_flags = may_flags; + __entry->nf_ref = refcount_read(&nf->nf_ref); + __entry->nf_flags = nf->nf_flags; + __entry->nf_may = nf->nf_may; + __entry->nf_file = nf->nf_file; + ), + TP_printk("xid=0x%x inode=%p may_flags=%s ref=%u nf_flags=%s nf_may=%s nf_file=%p", + __entry->xid, __entry->inode, + show_nfsd_may_flags(__entry->may_flags), __entry->nf_ref, + show_nf_flags(__entry->nf_flags), + show_nfsd_may_flags(__entry->nf_may), __entry->nf_file + ) ); TRACE_EVENT(nfsd_file_open, TP_PROTO(struct nfsd_file *nf, __be32 status), TP_ARGS(nf, status), TP_STRUCT__entry( - __field(unsigned int, nf_hashval) __field(void *, nf_inode) /* cannot be dereferenced */ __field(int, nf_ref) __field(unsigned long, nf_flags) @@ -790,15 +954,13 @@ TRACE_EVENT(nfsd_file_open, __field(void *, nf_file) /* cannot be dereferenced */ ), TP_fast_assign( - __entry->nf_hashval = nf->nf_hashval; __entry->nf_inode = nf->nf_inode; __entry->nf_ref = refcount_read(&nf->nf_ref); __entry->nf_flags = nf->nf_flags; __entry->nf_may = nf->nf_may; __entry->nf_file = nf->nf_file; ), - TP_printk("hash=0x%x inode=%p ref=%d flags=%s may=%s file=%p", - __entry->nf_hashval, + TP_printk("inode=%p ref=%d flags=%s may=%s file=%p", __entry->nf_inode, __entry->nf_ref, show_nf_flags(__entry->nf_flags), @@ -807,30 +969,53 @@ TRACE_EVENT(nfsd_file_open, ) DECLARE_EVENT_CLASS(nfsd_file_search_class, - TP_PROTO(struct inode *inode, unsigned int hash, int found), - TP_ARGS(inode, hash, found), + TP_PROTO( + const struct inode *inode, + unsigned int count + ), + TP_ARGS(inode, count), TP_STRUCT__entry( - __field(struct inode *, inode) - __field(unsigned int, hash) - __field(int, found) + __field(const struct inode *, inode) + __field(unsigned int, count) ), TP_fast_assign( __entry->inode = inode; - __entry->hash = hash; - __entry->found = found; + __entry->count = count; ), - TP_printk("hash=0x%x inode=%p found=%d", __entry->hash, - __entry->inode, __entry->found) + TP_printk("inode=%p count=%u", + __entry->inode, __entry->count) ); #define DEFINE_NFSD_FILE_SEARCH_EVENT(name) \ DEFINE_EVENT(nfsd_file_search_class, name, \ - TP_PROTO(struct inode *inode, unsigned int hash, int found), \ - TP_ARGS(inode, hash, found)) + TP_PROTO( \ + const struct inode *inode, \ + unsigned int count \ + ), \ + TP_ARGS(inode, count)) DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_close_inode_sync); DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_close_inode); -DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_is_cached); + +TRACE_EVENT(nfsd_file_is_cached, + TP_PROTO( + const struct inode *inode, + int found + ), + TP_ARGS(inode, found), + TP_STRUCT__entry( + __field(const struct inode *, inode) + __field(int, found) + ), + TP_fast_assign( + __entry->inode = inode; + __entry->found = found; + ), + TP_printk("inode=%p is %scached", + __entry->inode, + __entry->found ? "" : "not " + ) +); TRACE_EVENT(nfsd_file_fsnotify_handle_event, TP_PROTO(struct inode *inode, u32 mask), @@ -851,6 +1036,76 @@ TRACE_EVENT(nfsd_file_fsnotify_handle_event, __entry->nlink, __entry->mode, __entry->mask) ); +DECLARE_EVENT_CLASS(nfsd_file_gc_class, + TP_PROTO( + const struct nfsd_file *nf + ), + TP_ARGS(nf), + TP_STRUCT__entry( + __field(void *, nf_inode) + __field(void *, nf_file) + __field(int, nf_ref) + __field(unsigned long, nf_flags) + ), + TP_fast_assign( + __entry->nf_inode = nf->nf_inode; + __entry->nf_file = nf->nf_file; + __entry->nf_ref = refcount_read(&nf->nf_ref); + __entry->nf_flags = nf->nf_flags; + ), + TP_printk("inode=%p ref=%d nf_flags=%s nf_file=%p", + __entry->nf_inode, __entry->nf_ref, + show_nf_flags(__entry->nf_flags), + __entry->nf_file + ) +); + +#define DEFINE_NFSD_FILE_GC_EVENT(name) \ +DEFINE_EVENT(nfsd_file_gc_class, name, \ + TP_PROTO( \ + const struct nfsd_file *nf \ + ), \ + TP_ARGS(nf)) + +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_lru_add); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_lru_add_disposed); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_lru_del); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_lru_del_disposed); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_in_use); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_writeback); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_referenced); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_hashed); +DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_disposed); + +DECLARE_EVENT_CLASS(nfsd_file_lruwalk_class, + TP_PROTO( + unsigned long removed, + unsigned long remaining + ), + TP_ARGS(removed, remaining), + TP_STRUCT__entry( + __field(unsigned long, removed) + __field(unsigned long, remaining) + ), + TP_fast_assign( + __entry->removed = removed; + __entry->remaining = remaining; + ), + TP_printk("%lu entries removed, %lu remaining", + __entry->removed, __entry->remaining) +); + +#define DEFINE_NFSD_FILE_LRUWALK_EVENT(name) \ +DEFINE_EVENT(nfsd_file_lruwalk_class, name, \ + TP_PROTO( \ + unsigned long removed, \ + unsigned long remaining \ + ), \ + TP_ARGS(removed, remaining)) + +DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_gc_removed); +DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_shrinker_removed); + #include "cache.h" TRACE_DEFINE_ENUM(RC_DROPIT); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d79db56475d4..9f486b788ed0 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -199,27 +199,13 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out_nfserr; } } else { - /* - * In the nfsd4_open() case, this may be held across - * subsequent open and delegation acquisition which may - * need to take the child's i_mutex: - */ - fh_lock_nested(fhp, I_MUTEX_PARENT); - dentry = lookup_one_len(name, dparent, len); + dentry = lookup_one_len_unlocked(name, dparent, len); host_err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; if (nfsd_mountpoint(dentry, exp)) { - /* - * We don't need the i_mutex after all. It's - * still possible we could open this (regular - * files can be mountpoints too), but the - * i_mutex is just there to prevent renames of - * something that we might be about to delegate, - * and a mountpoint won't be renamed: - */ - fh_unlock(fhp); - if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { + host_err = nfsd_cross_mnt(rqstp, &dentry, &exp); + if (host_err) { dput(dentry); goto out_nfserr; } @@ -234,7 +220,15 @@ out_nfserr: return nfserrno(host_err); } -/* +/** + * nfsd_lookup - look up a single path component for nfsd + * + * @rqstp: the request context + * @fhp: the file handle of the directory + * @name: the component name, or %NULL to look up parent + * @len: length of name to examine + * @resfh: pointer to pre-initialised filehandle to hold result. + * * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * @@ -244,11 +238,11 @@ out_nfserr: * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 - * NeilBrown <neilb@cse.unsw.edu.au> + * */ __be32 nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, - unsigned int len, struct svc_fh *resfh) + unsigned int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dentry; @@ -349,11 +343,13 @@ nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, * Set various file attributes. After this call fhp needs an fh_put. */ __be32 -nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, +nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct nfsd_attrs *attr, int check_guard, time64_t guardtime) { struct dentry *dentry; struct inode *inode; + struct iattr *iap = attr->na_iattr; int accmode = NFSD_MAY_SATTR; umode_t ftype = 0; __be32 err; @@ -420,7 +416,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, return err; } - fh_lock(fhp); + inode_lock(inode); if (size_change) { /* * RFC5661, Section 18.30.4: @@ -456,7 +452,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, host_err = notify_change(&init_user_ns, dentry, iap, NULL); out_unlock: - fh_unlock(fhp); + if (attr->na_seclabel && attr->na_seclabel->len) + attr->na_labelerr = security_inode_setsecctx(dentry, + attr->na_seclabel->data, attr->na_seclabel->len); + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) + attr->na_aclerr = set_posix_acl(&init_user_ns, + inode, ACL_TYPE_ACCESS, + attr->na_pacl); + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && + !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode)) + attr->na_aclerr = set_posix_acl(&init_user_ns, + inode, ACL_TYPE_DEFAULT, + attr->na_dpacl); + inode_unlock(inode); if (size_change) put_write_access(inode); out: @@ -494,32 +502,6 @@ int nfsd4_is_junction(struct dentry *dentry) return 0; return 1; } -#ifdef CONFIG_NFSD_V4_SECURITY_LABEL -__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct xdr_netobj *label) -{ - __be32 error; - int host_error; - struct dentry *dentry; - - error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); - if (error) - return error; - - dentry = fhp->fh_dentry; - - inode_lock(d_inode(dentry)); - host_error = security_inode_setsecctx(dentry, label->data, label->len); - inode_unlock(d_inode(dentry)); - return nfserrno(host_error); -} -#else -__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct xdr_netobj *label) -{ - return nfserr_notsupp; -} -#endif static struct nfsd4_compound_state *nfsd4_get_cstate(struct svc_rqst *rqstp) { @@ -1202,14 +1184,15 @@ out: * @rqstp: RPC transaction being executed * @fhp: NFS filehandle of parent directory * @resfhp: NFS filehandle of new object - * @iap: requested attributes of new object + * @attrs: requested attributes of new object * * Returns nfs_ok on success, or an nfsstat in network byte order. */ __be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct svc_fh *resfhp, struct iattr *iap) + struct svc_fh *resfhp, struct nfsd_attrs *attrs) { + struct iattr *iap = attrs->na_iattr; __be32 status; /* @@ -1230,7 +1213,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, * if the attributes have not changed. */ if (iap->ia_valid) - status = nfsd_setattr(rqstp, resfhp, iap, 0, (time64_t)0); + status = nfsd_setattr(rqstp, resfhp, attrs, 0, (time64_t)0); else status = nfserrno(commit_metadata(resfhp)); @@ -1269,11 +1252,12 @@ nfsd_check_ignore_resizing(struct iattr *iap) /* The parent directory should already be locked: */ __be32 nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, - char *fname, int flen, struct iattr *iap, - int type, dev_t rdev, struct svc_fh *resfhp) + char *fname, int flen, struct nfsd_attrs *attrs, + int type, dev_t rdev, struct svc_fh *resfhp) { struct dentry *dentry, *dchild; struct inode *dirp; + struct iattr *iap = attrs->na_iattr; __be32 err; int host_err; @@ -1281,13 +1265,6 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, dirp = d_inode(dentry); dchild = dget(resfhp->fh_dentry); - if (!fhp->fh_locked) { - WARN_ONCE(1, "nfsd_create: parent %pd2 not locked!\n", - dentry); - err = nfserr_io; - goto out; - } - err = nfsd_permission(rqstp, fhp->fh_export, dentry, NFSD_MAY_CREATE); if (err) goto out; @@ -1347,7 +1324,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err < 0) goto out_nfserr; - err = nfsd_create_setattr(rqstp, fhp, resfhp, iap); + err = nfsd_create_setattr(rqstp, fhp, resfhp, attrs); out: dput(dchild); @@ -1366,8 +1343,8 @@ out_nfserr: */ __be32 nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, - char *fname, int flen, struct iattr *iap, - int type, dev_t rdev, struct svc_fh *resfhp) + char *fname, int flen, struct nfsd_attrs *attrs, + int type, dev_t rdev, struct svc_fh *resfhp) { struct dentry *dentry, *dchild = NULL; __be32 err; @@ -1386,11 +1363,13 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err) return nfserrno(host_err); - fh_lock_nested(fhp, I_MUTEX_PARENT); + inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT); dchild = lookup_one_len(fname, dentry, flen); host_err = PTR_ERR(dchild); - if (IS_ERR(dchild)) - return nfserrno(host_err); + if (IS_ERR(dchild)) { + err = nfserrno(host_err); + goto out_unlock; + } err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); /* * We unconditionally drop our ref to dchild as fh_compose will have @@ -1398,9 +1377,14 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, */ dput(dchild); if (err) - return err; - return nfsd_create_locked(rqstp, fhp, fname, flen, iap, type, - rdev, resfhp); + goto out_unlock; + fh_fill_pre_attrs(fhp); + err = nfsd_create_locked(rqstp, fhp, fname, flen, attrs, type, + rdev, resfhp); + fh_fill_post_attrs(fhp); +out_unlock: + inode_unlock(dentry->d_inode); + return err; } /* @@ -1441,15 +1425,25 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) return 0; } -/* - * Create a symlink and look up its inode +/** + * nfsd_symlink - Create a symlink and look up its inode + * @rqstp: RPC transaction being executed + * @fhp: NFS filehandle of parent directory + * @fname: filename of the new symlink + * @flen: length of @fname + * @path: content of the new symlink (NUL-terminated) + * @attrs: requested attributes of new object + * @resfhp: NFS filehandle of new object + * * N.B. After this call _both_ fhp and resfhp need an fh_put + * + * Returns nfs_ok on success, or an nfsstat in network byte order. */ __be32 nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, - char *fname, int flen, - char *path, - struct svc_fh *resfhp) + char *fname, int flen, + char *path, struct nfsd_attrs *attrs, + struct svc_fh *resfhp) { struct dentry *dentry, *dnew; __be32 err, cerr; @@ -1467,33 +1461,35 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; host_err = fh_want_write(fhp); - if (host_err) - goto out_nfserr; + if (host_err) { + err = nfserrno(host_err); + goto out; + } - fh_lock(fhp); dentry = fhp->fh_dentry; + inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT); dnew = lookup_one_len(fname, dentry, flen); - host_err = PTR_ERR(dnew); - if (IS_ERR(dnew)) - goto out_nfserr; - + if (IS_ERR(dnew)) { + err = nfserrno(PTR_ERR(dnew)); + inode_unlock(dentry->d_inode); + goto out_drop_write; + } + fh_fill_pre_attrs(fhp); host_err = vfs_symlink(&init_user_ns, d_inode(dentry), dnew, path); err = nfserrno(host_err); - fh_unlock(fhp); + cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp); + if (!err) + nfsd_create_setattr(rqstp, fhp, resfhp, attrs); + fh_fill_post_attrs(fhp); + inode_unlock(dentry->d_inode); if (!err) err = nfserrno(commit_metadata(fhp)); - - fh_drop_write(fhp); - - cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp); dput(dnew); if (err==0) err = cerr; +out_drop_write: + fh_drop_write(fhp); out: return err; - -out_nfserr: - err = nfserrno(host_err); - goto out; } /* @@ -1531,22 +1527,25 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, goto out; } - fh_lock_nested(ffhp, I_MUTEX_PARENT); ddir = ffhp->fh_dentry; dirp = d_inode(ddir); + inode_lock_nested(dirp, I_MUTEX_PARENT); dnew = lookup_one_len(name, ddir, len); - host_err = PTR_ERR(dnew); - if (IS_ERR(dnew)) - goto out_nfserr; + if (IS_ERR(dnew)) { + err = nfserrno(PTR_ERR(dnew)); + goto out_unlock; + } dold = tfhp->fh_dentry; err = nfserr_noent; if (d_really_is_negative(dold)) goto out_dput; + fh_fill_pre_attrs(ffhp); host_err = vfs_link(dold, &init_user_ns, dirp, dnew, NULL); - fh_unlock(ffhp); + fh_fill_post_attrs(ffhp); + inode_unlock(dirp); if (!host_err) { err = nfserrno(commit_metadata(ffhp)); if (!err) @@ -1557,17 +1556,17 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, else err = nfserrno(host_err); } -out_dput: dput(dnew); -out_unlock: - fh_unlock(ffhp); +out_drop_write: fh_drop_write(tfhp); out: return err; -out_nfserr: - err = nfserrno(host_err); - goto out_unlock; +out_dput: + dput(dnew); +out_unlock: + inode_unlock(dirp); + goto out_drop_write; } static void @@ -1628,10 +1627,7 @@ retry: goto out; } - /* cannot use fh_lock as we need deadlock protective ordering - * so do it by hand */ trap = lock_rename(tdentry, fdentry); - ffhp->fh_locked = tfhp->fh_locked = true; fh_fill_pre_attrs(ffhp); fh_fill_pre_attrs(tfhp); @@ -1687,17 +1683,12 @@ retry: dput(odentry); out_nfserr: err = nfserrno(host_err); - /* - * We cannot rely on fh_unlock on the two filehandles, - * as that would do the wrong thing if the two directories - * were the same, so again we do it by hand. - */ + if (!close_cached) { fh_fill_post_attrs(ffhp); fh_fill_post_attrs(tfhp); } unlock_rename(tdentry, fdentry); - ffhp->fh_locked = tfhp->fh_locked = false; fh_drop_write(ffhp); /* @@ -1741,19 +1732,19 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (host_err) goto out_nfserr; - fh_lock_nested(fhp, I_MUTEX_PARENT); dentry = fhp->fh_dentry; dirp = d_inode(dentry); + inode_lock_nested(dirp, I_MUTEX_PARENT); rdentry = lookup_one_len(fname, dentry, flen); host_err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) - goto out_drop_write; + goto out_unlock; if (d_really_is_negative(rdentry)) { dput(rdentry); host_err = -ENOENT; - goto out_drop_write; + goto out_unlock; } rinode = d_inode(rdentry); ihold(rinode); @@ -1761,6 +1752,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (!type) type = d_inode(rdentry)->i_mode & S_IFMT; + fh_fill_pre_attrs(fhp); if (type != S_IFDIR) { if (rdentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) nfsd_close_cached_files(rdentry); @@ -1768,8 +1760,9 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } else { host_err = vfs_rmdir(&init_user_ns, dirp, rdentry); } + fh_fill_post_attrs(fhp); - fh_unlock(fhp); + inode_unlock(dirp); if (!host_err) host_err = commit_metadata(fhp); dput(rdentry); @@ -1791,6 +1784,9 @@ out_nfserr: } out: return err; +out_unlock: + inode_unlock(dirp); + goto out_drop_write; } /* @@ -2144,13 +2140,16 @@ out: return err; } -/* - * Removexattr and setxattr need to call fh_lock to both lock the inode - * and set the change attribute. Since the top-level vfs_removexattr - * and vfs_setxattr calls already do their own inode_lock calls, call - * the _locked variant. Pass in a NULL pointer for delegated_inode, - * and let the client deal with NFS4ERR_DELAY (same as with e.g. - * setattr and remove). +/** + * nfsd_removexattr - Remove an extended attribute + * @rqstp: RPC transaction being executed + * @fhp: NFS filehandle of object with xattr to remove + * @name: name of xattr to remove (NUL-terminate) + * + * Pass in a NULL pointer for delegated_inode, and let the client deal + * with NFS4ERR_DELAY (same as with e.g. setattr and remove). + * + * Returns nfs_ok on success, or an nfsstat in network byte order. */ __be32 nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name) @@ -2166,12 +2165,14 @@ nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name) if (ret) return nfserrno(ret); - fh_lock(fhp); + inode_lock(fhp->fh_dentry->d_inode); + fh_fill_pre_attrs(fhp); ret = __vfs_removexattr_locked(&init_user_ns, fhp->fh_dentry, name, NULL); - fh_unlock(fhp); + fh_fill_post_attrs(fhp); + inode_unlock(fhp->fh_dentry->d_inode); fh_drop_write(fhp); return nfsd_xattr_errno(ret); @@ -2191,12 +2192,13 @@ nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name, ret = fh_want_write(fhp); if (ret) return nfserrno(ret); - fh_lock(fhp); + inode_lock(fhp->fh_dentry->d_inode); + fh_fill_pre_attrs(fhp); ret = __vfs_setxattr_locked(&init_user_ns, fhp->fh_dentry, name, buf, len, flags, NULL); - - fh_unlock(fhp); + fh_fill_post_attrs(fhp); + inode_unlock(fhp->fh_dentry->d_inode); fh_drop_write(fhp); return nfsd_xattr_errno(ret); diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 26347d76f44a..c95cd414b4bb 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -6,6 +6,8 @@ #ifndef LINUX_NFSD_VFS_H #define LINUX_NFSD_VFS_H +#include <linux/fs.h> +#include <linux/posix_acl.h> #include "nfsfh.h" #include "nfsd.h" @@ -42,6 +44,22 @@ struct nfsd_file; typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned); /* nfsd/vfs.c */ +struct nfsd_attrs { + struct iattr *na_iattr; /* input */ + struct xdr_netobj *na_seclabel; /* input */ + struct posix_acl *na_pacl; /* input */ + struct posix_acl *na_dpacl; /* input */ + + int na_labelerr; /* output */ + int na_aclerr; /* output */ +}; + +static inline void nfsd_attrs_free(struct nfsd_attrs *attrs) +{ + posix_acl_release(attrs->na_pacl); + posix_acl_release(attrs->na_dpacl); +} + int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, struct svc_export **expp); __be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *, @@ -50,11 +68,9 @@ __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *, const char *, unsigned int, struct svc_export **, struct dentry **); __be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *, - struct iattr *, int, time64_t); + struct nfsd_attrs *, int, time64_t); int nfsd_mountpoint(struct dentry *, struct svc_export *); #ifdef CONFIG_NFSD_V4 -__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *, - struct xdr_netobj *); __be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *, struct file *, loff_t, loff_t, int); __be32 nfsd4_clone_file_range(struct svc_rqst *rqstp, @@ -63,14 +79,14 @@ __be32 nfsd4_clone_file_range(struct svc_rqst *rqstp, u64 count, bool sync); #endif /* CONFIG_NFSD_V4 */ __be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *, - char *name, int len, struct iattr *attrs, + char *name, int len, struct nfsd_attrs *attrs, int type, dev_t rdev, struct svc_fh *res); __be32 nfsd_create(struct svc_rqst *, struct svc_fh *, - char *name, int len, struct iattr *attrs, + char *name, int len, struct nfsd_attrs *attrs, int type, dev_t rdev, struct svc_fh *res); __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *); __be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct svc_fh *resfhp, struct iattr *iap); + struct svc_fh *resfhp, struct nfsd_attrs *iap); __be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp, u64 offset, u32 count, __be32 *verf); #ifdef CONFIG_NFSD_V4 @@ -110,8 +126,9 @@ __be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, char *, int *); __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, - char *name, int len, char *path, - struct svc_fh *res); + char *name, int len, char *path, + struct nfsd_attrs *attrs, + struct svc_fh *res); __be32 nfsd_link(struct svc_rqst *, struct svc_fh *, char *, int, struct svc_fh *); ssize_t nfsd_copy_file_range(struct file *, u64, diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 7b744011f2d3..96267258e629 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -279,6 +279,7 @@ struct nfsd4_open { struct nfs4_clnt_odstate *op_odstate; /* used during processing */ struct nfs4_acl *op_acl; struct xdr_netobj op_label; + struct svc_rqst *op_rqstp; }; struct nfsd4_open_confirm { @@ -302,9 +303,10 @@ struct nfsd4_read { u32 rd_length; /* request */ int rd_vlen; struct nfsd_file *rd_nf; - + struct svc_rqst *rd_rqstp; /* response */ - struct svc_fh *rd_fhp; /* response */ + struct svc_fh *rd_fhp; /* response */ + u32 rd_eof; /* response */ }; struct nfsd4_readdir { @@ -532,6 +534,13 @@ struct nfsd42_write_res { stateid_t cb_stateid; }; +struct nfsd4_cb_offload { + struct nfsd4_callback co_cb; + struct nfsd42_write_res co_res; + __be32 co_nfserr; + struct knfsd_fh co_fh; +}; + struct nfsd4_copy { /* request */ stateid_t cp_src_stateid; @@ -539,18 +548,16 @@ struct nfsd4_copy { u64 cp_src_pos; u64 cp_dst_pos; u64 cp_count; - struct nl4_server cp_src; - bool cp_intra; + struct nl4_server *cp_src; - /* both */ - u32 cp_synchronous; + unsigned long cp_flags; +#define NFSD4_COPY_F_STOPPED (0) +#define NFSD4_COPY_F_INTRA (1) +#define NFSD4_COPY_F_SYNCHRONOUS (2) +#define NFSD4_COPY_F_COMMITTED (3) /* response */ struct nfsd42_write_res cp_res; - - /* for cb_offload */ - struct nfsd4_callback cp_cb; - __be32 nfserr; struct knfsd_fh fh; struct nfs4_client *cp_clp; @@ -563,14 +570,35 @@ struct nfsd4_copy { struct list_head copies; struct task_struct *copy_task; refcount_t refcount; - bool stopped; struct vfsmount *ss_mnt; struct nfs_fh c_fh; nfs4_stateid stateid; - bool committed; }; +static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync) +{ + if (sync) + set_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags); + else + clear_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags); +} + +static inline bool nfsd4_copy_is_sync(const struct nfsd4_copy *copy) +{ + return test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags); +} + +static inline bool nfsd4_copy_is_async(const struct nfsd4_copy *copy) +{ + return !test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags); +} + +static inline bool nfsd4_ssc_is_inter(const struct nfsd4_copy *copy) +{ + return !test_bit(NFSD4_COPY_F_INTRA, ©->cp_flags); +} + struct nfsd4_seek { /* request */ stateid_t seek_stateid; @@ -594,19 +622,20 @@ struct nfsd4_offload_status { struct nfsd4_copy_notify { /* request */ stateid_t cpn_src_stateid; - struct nl4_server cpn_dst; + struct nl4_server *cpn_dst; /* response */ stateid_t cpn_cnr_stateid; u64 cpn_sec; u32 cpn_nsec; - struct nl4_server cpn_src; + struct nl4_server *cpn_src; }; struct nfsd4_op { u32 opnum; - const struct nfsd4_operation * opdesc; __be32 status; + const struct nfsd4_operation *opdesc; + struct nfs4_replay *replay; union nfsd4_op_u { struct nfsd4_access access; struct nfsd4_close close; @@ -670,7 +699,6 @@ struct nfsd4_op { struct nfsd4_listxattrs listxattrs; struct nfsd4_removexattr removexattr; } u; - struct nfs4_replay * replay; }; bool nfsd4_cache_this_op(struct nfsd4_op *); diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index c75fd54b9185..961d1cf54388 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -197,6 +197,7 @@ static struct inode *ocfs2_get_init_inode(struct inode *dir, umode_t mode) * callers. */ if (S_ISDIR(mode)) set_nlink(inode, 2); + mode = mode_strip_sgid(&init_user_ns, dir, mode); inode_init_owner(&init_user_ns, inode, dir, mode); status = dquot_initialize(inode); if (status) diff --git a/fs/read_write.c b/fs/read_write.c index ea59dd0095c2..1a261dcf1778 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -378,14 +378,13 @@ EXPORT_SYMBOL(rw_verify_area); static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { - struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = (ppos ? *ppos : 0); - iov_iter_init(&iter, READ, &iov, 1, len); + iov_iter_ubuf(&iter, READ, buf, len); ret = call_read_iter(filp, &kiocb, &iter); BUG_ON(ret == -EIOCBQUEUED); @@ -481,14 +480,13 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { - struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = (ppos ? *ppos : 0); - iov_iter_init(&iter, WRITE, &iov, 1, len); + iov_iter_ubuf(&iter, WRITE, (void __user *)buf, len); ret = call_write_iter(filp, &kiocb, &iter); BUG_ON(ret == -EIOCBQUEUED); diff --git a/fs/splice.c b/fs/splice.c index 93a2c9bf6249..0878b852b355 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -301,11 +301,9 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, { struct iov_iter to; struct kiocb kiocb; - unsigned int i_head; int ret; iov_iter_pipe(&to, READ, pipe, len); - i_head = to.head; init_sync_kiocb(&kiocb, in); kiocb.ki_pos = *ppos; ret = call_read_iter(in, &kiocb, &to); @@ -313,9 +311,8 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, *ppos = kiocb.ki_pos; file_accessed(in); } else if (ret < 0) { - to.head = i_head; - to.iov_offset = 0; - iov_iter_advance(&to, 0); /* to free what was emitted */ + /* free what was emitted */ + pipe_discard_from(pipe, to.start_head); /* * callers of ->splice_read() expect -EAGAIN on * "can't put anything in there", rather than -EFAULT. @@ -1161,39 +1158,40 @@ static int iter_to_pipe(struct iov_iter *from, }; size_t total = 0; int ret = 0; - bool failed = false; - while (iov_iter_count(from) && !failed) { + while (iov_iter_count(from)) { struct page *pages[16]; - ssize_t copied; + ssize_t left; size_t start; - int n; + int i, n; - copied = iov_iter_get_pages(from, pages, ~0UL, 16, &start); - if (copied <= 0) { - ret = copied; + left = iov_iter_get_pages2(from, pages, ~0UL, 16, &start); + if (left <= 0) { + ret = left; break; } - for (n = 0; copied; n++, start = 0) { - int size = min_t(int, copied, PAGE_SIZE - start); - if (!failed) { - buf.page = pages[n]; - buf.offset = start; - buf.len = size; - ret = add_to_pipe(pipe, &buf); - if (unlikely(ret < 0)) { - failed = true; - } else { - iov_iter_advance(from, ret); - total += ret; - } - } else { - put_page(pages[n]); + n = DIV_ROUND_UP(left + start, PAGE_SIZE); + for (i = 0; i < n; i++) { + int size = min_t(int, left, PAGE_SIZE - start); + + buf.page = pages[i]; + buf.offset = start; + buf.len = size; + ret = add_to_pipe(pipe, &buf); + if (unlikely(ret < 0)) { + iov_iter_revert(from, left); + // this one got dropped by add_to_pipe() + while (++i < n) + put_page(pages[i]); + goto out; } - copied -= size; + total += ret; + left -= size; + start = 0; } } +out: return total ? total : ret; } diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index 511bb9fa3750..860f0b1032c6 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -231,13 +231,6 @@ static const struct iomap_writeback_ops zonefs_writeback_ops = { .map_blocks = zonefs_write_map_blocks, }; -static int zonefs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct iomap_writepage_ctx wpc = { }; - - return iomap_writepage(page, wbc, &wpc, &zonefs_writeback_ops); -} - static int zonefs_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -265,7 +258,6 @@ static int zonefs_swap_activate(struct swap_info_struct *sis, static const struct address_space_operations zonefs_file_aops = { .read_folio = zonefs_read_folio, .readahead = zonefs_readahead, - .writepage = zonefs_writepage, .writepages = zonefs_writepages, .dirty_folio = filemap_dirty_folio, .release_folio = iomap_release_folio, diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 48f0fd499274..e7d27373ff71 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -344,8 +344,9 @@ struct acpi_device_physical_node { struct acpi_device_properties { const guid_t *guid; - const union acpi_object *properties; + union acpi_object *properties; struct list_head list; + void **bufs; }; /* ACPI Device Specific Data (_DSD) */ diff --git a/include/dt-bindings/pinctrl/r7s9210-pinctrl.h b/include/dt-bindings/pinctrl/r7s9210-pinctrl.h index 2d0c23e5d3a7..8736ce038eca 100644 --- a/include/dt-bindings/pinctrl/r7s9210-pinctrl.h +++ b/include/dt-bindings/pinctrl/r7s9210-pinctrl.h @@ -42,6 +42,6 @@ /* * Convert a port and pin label to its global pin index */ - #define RZA2_PIN(port, pin) ((port) * RZA2_PINS_PER_PORT + (pin)) +#define RZA2_PIN(port, pin) ((port) * RZA2_PINS_PER_PORT + (pin)) #endif /* __DT_BINDINGS_PINCTRL_RENESAS_RZA2_H */ diff --git a/include/dt-bindings/pinctrl/rzg2l-pinctrl.h b/include/dt-bindings/pinctrl/rzg2l-pinctrl.h index b48f8c7a5556..c78ed5e5efb7 100644 --- a/include/dt-bindings/pinctrl/rzg2l-pinctrl.h +++ b/include/dt-bindings/pinctrl/rzg2l-pinctrl.h @@ -18,6 +18,6 @@ #define RZG2L_PORT_PINMUX(b, p, f) ((b) * RZG2L_PINS_PER_PORT + (p) | ((f) << 16)) /* Convert a port and pin label to its global pin index */ - #define RZG2L_GPIO(port, pin) ((port) * RZG2L_PINS_PER_PORT + (pin)) +#define RZG2L_GPIO(port, pin) ((port) * RZG2L_PINS_PER_PORT + (pin)) #endif /* __DT_BINDINGS_RZG2L_PINCTRL_H */ diff --git a/include/dt-bindings/pinctrl/rzv2m-pinctrl.h b/include/dt-bindings/pinctrl/rzv2m-pinctrl.h new file mode 100644 index 000000000000..525532cd15da --- /dev/null +++ b/include/dt-bindings/pinctrl/rzv2m-pinctrl.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * This header provides constants for Renesas RZ/V2M pinctrl bindings. + * + * Copyright (C) 2022 Renesas Electronics Corp. + * + */ + +#ifndef __DT_BINDINGS_RZV2M_PINCTRL_H +#define __DT_BINDINGS_RZV2M_PINCTRL_H + +#define RZV2M_PINS_PER_PORT 16 + +/* + * Create the pin index from its bank and position numbers and store in + * the upper 16 bits the alternate function identifier + */ +#define RZV2M_PORT_PINMUX(b, p, f) ((b) * RZV2M_PINS_PER_PORT + (p) | ((f) << 16)) + +/* Convert a port and pin label to its global pin index */ +#define RZV2M_GPIO(port, pin) ((port) * RZV2M_PINS_PER_PORT + (pin)) + +#endif /* __DT_BINDINGS_RZV2M_PINCTRL_H */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7ee10b2848d5..6f64b2f3dc54 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1251,7 +1251,7 @@ static inline bool acpi_dev_has_props(const struct acpi_device *adev) struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, - const union acpi_object *properties); + union acpi_object *properties); int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr); diff --git a/include/linux/bpfptr.h b/include/linux/bpfptr.h index 46e1757d06a3..79b2f78eec1a 100644 --- a/include/linux/bpfptr.h +++ b/include/linux/bpfptr.h @@ -49,7 +49,9 @@ static inline void bpfptr_add(bpfptr_t *bpfptr, size_t val) static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src, size_t offset, size_t size) { - return copy_from_sockptr_offset(dst, (sockptr_t) src, offset, size); + if (!bpfptr_is_kernel(src)) + return copy_from_user(dst, src.user + offset, size); + return copy_from_kernel_nofault(dst, src.kernel + offset, size); } static inline int copy_from_bpfptr(void *dst, bpfptr_t src, size_t size) @@ -78,7 +80,9 @@ static inline void *kvmemdup_bpfptr(bpfptr_t src, size_t len) static inline long strncpy_from_bpfptr(char *dst, bpfptr_t src, size_t count) { - return strncpy_from_sockptr(dst, (sockptr_t) src, count); + if (bpfptr_is_kernel(src)) + return strncpy_from_kernel_nofault(dst, src.kernel, count); + return strncpy_from_user(dst, src.user, count); } #endif /* _LINUX_BPFPTR_H */ diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 307445d1c69e..def8b8d30ccc 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -118,7 +118,6 @@ static __always_inline int test_clear_buffer_##name(struct buffer_head *bh) \ * of the form "mark_buffer_foo()". These are higher-level functions which * do something in addition to setting a b_state bit. */ -BUFFER_FNS(Uptodate, uptodate) BUFFER_FNS(Dirty, dirty) TAS_BUFFER_FNS(Dirty, dirty) BUFFER_FNS(Lock, locked) @@ -136,6 +135,30 @@ BUFFER_FNS(Meta, meta) BUFFER_FNS(Prio, prio) BUFFER_FNS(Defer_Completion, defer_completion) +static __always_inline void set_buffer_uptodate(struct buffer_head *bh) +{ + /* + * make it consistent with folio_mark_uptodate + * pairs with smp_load_acquire in buffer_uptodate + */ + smp_mb__before_atomic(); + set_bit(BH_Uptodate, &bh->b_state); +} + +static __always_inline void clear_buffer_uptodate(struct buffer_head *bh) +{ + clear_bit(BH_Uptodate, &bh->b_state); +} + +static __always_inline int buffer_uptodate(const struct buffer_head *bh) +{ + /* + * make it consistent with folio_test_uptodate + * pairs with smp_mb__before_atomic in set_buffer_uptodate + */ + return (smp_load_acquire(&bh->b_state) & (1UL << BH_Uptodate)) != 0; +} + #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) /* If we *know* page->private refers to buffer_heads */ diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index 86bf82dbd8b8..49586ff26152 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -433,9 +433,9 @@ union ceph_mds_request_args { __le32 stripe_unit; /* layout for newly created file */ __le32 stripe_count; /* ... */ __le32 object_size; - __le32 file_replication; - __le32 mask; /* CEPH_CAP_* */ - __le32 old_size; + __le32 pool; + __le32 mask; /* CEPH_CAP_* */ + __le64 old_size; } __attribute__ ((packed)) open; struct { __le32 flags; @@ -768,7 +768,7 @@ struct ceph_mds_caps { __le32 xattr_len; __le64 xattr_version; - /* filelock */ + /* a union of non-export and export bodies. */ __le64 size, max_size, truncate_size; __le32 truncate_seq; struct ceph_timespec mtime, atime, ctime; diff --git a/include/linux/ceph/mdsmap.h b/include/linux/ceph/mdsmap.h index 523fd0452856..4c3e0648dc27 100644 --- a/include/linux/ceph/mdsmap.h +++ b/include/linux/ceph/mdsmap.h @@ -25,6 +25,7 @@ struct ceph_mdsmap { u32 m_session_timeout; /* seconds */ u32 m_session_autoclose; /* seconds */ u64 m_max_file_size; + u64 m_max_xattr_size; /* maximum size for xattrs blob */ u32 m_max_mds; /* expected up:active mds number */ u32 m_num_active_mds; /* actual up:active mds number */ u32 possible_max_rank; /* possible max rank index */ diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index cba8a6ffc329..fb6be72104df 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -507,9 +507,8 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, extern void ceph_osdc_get_request(struct ceph_osd_request *req); extern void ceph_osdc_put_request(struct ceph_osd_request *req); -extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail); +void ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req); extern void ceph_osdc_cancel_request(struct ceph_osd_request *req); extern int ceph_osdc_wait_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index c73e5e327e76..92c78ed02b54 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -233,6 +233,8 @@ extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *, wait_queue_head_t *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); +extern bool d_same_name(const struct dentry *dentry, const struct dentry *parent, + const struct qstr *name); extern struct dentry * d_exact_alias(struct dentry *, struct inode *); extern struct dentry *d_find_any_alias(struct inode *inode); extern struct dentry * d_obtain_alias(struct inode *); diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 010df04358aa..d678afeb8a13 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -177,6 +177,7 @@ static inline void dma_pernuma_cma_reserve(void) { } #ifdef CONFIG_DMA_DECLARE_COHERENT int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size); +void dma_release_coherent_memory(struct device *dev); int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size, dma_addr_t *dma_handle, void **ret); int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr); @@ -188,9 +189,11 @@ static inline int dma_declare_coherent_memory(struct device *dev, { return -ENOSYS; } + #define dma_alloc_from_dev_coherent(dev, size, handle, ret) (0) #define dma_release_from_dev_coherent(dev, order, vaddr) (0) #define dma_mmap_from_dev_coherent(dev, vma, vaddr, order, ret) (0) +static inline void dma_release_coherent_memory(struct device *dev) { } #endif /* CONFIG_DMA_DECLARE_COHERENT */ #ifdef CONFIG_DMA_GLOBAL_POOL diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index cbde3b1fa414..9f50dacbf7d6 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -369,6 +369,11 @@ enum pm_pinctrl_drive_strength { PM_PINCTRL_DRIVE_STRENGTH_12MA = 3, }; +enum pm_pinctrl_tri_state { + PM_PINCTRL_TRI_STATE_DISABLE = 0, + PM_PINCTRL_TRI_STATE_ENABLE = 1, +}; + enum zynqmp_pm_shutdown_type { ZYNQMP_PM_SHUTDOWN_TYPE_SHUTDOWN = 0, ZYNQMP_PM_SHUTDOWN_TYPE_RESET = 1, diff --git a/include/linux/fs.h b/include/linux/fs.h index 8c127ffa6563..5113f65c786f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2035,6 +2035,8 @@ extern long compat_ptr_ioctl(struct file *file, unsigned int cmd, void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode, const struct inode *dir, umode_t mode); extern bool may_open_dev(const struct path *path); +umode_t mode_strip_sgid(struct user_namespace *mnt_userns, + const struct inode *dir, umode_t mode); /* * This is the "filldir" function type, used by readdir() to let diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index e60d57c99cb6..7d2f1e0f23b1 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -284,6 +284,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg); int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg); int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); +int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); struct fscrypt_dummy_policy { @@ -327,6 +328,10 @@ void fscrypt_free_inode(struct inode *inode); int fscrypt_drop_inode(struct inode *inode); /* fname.c */ +int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, + u8 *out, unsigned int olen); +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret); int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname, int lookup, struct fscrypt_name *fname); diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 177b07944640..25679035ca28 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -60,11 +60,11 @@ static inline void kmap_flush_unused(void); /** * kmap_local_page - Map a page for temporary usage - * @page: Pointer to the page to be mapped + * @page: Pointer to the page to be mapped * * Returns: The virtual address of the mapping * - * Can be invoked from any context. + * Can be invoked from any context, including interrupts. * * Requires careful handling when nesting multiple mappings because the map * management is stack based. The unmap has to be in the reverse order of @@ -86,8 +86,7 @@ static inline void kmap_flush_unused(void); * temporarily mapped. * * While it is significantly faster than kmap() for the higmem case it - * comes with restrictions about the pointer validity. Only use when really - * necessary. + * comes with restrictions about the pointer validity. * * On HIGHMEM enabled systems mapping a highmem page has the side effect of * disabling migration in order to keep the virtual address stable across diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 4cdfce976644..3ec981a0d8b3 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -43,6 +43,9 @@ enum { SUBPAGE_INDEX_CGROUP_RSVD, /* reuse page->private */ __MAX_CGROUP_SUBPAGE_INDEX = SUBPAGE_INDEX_CGROUP_RSVD, #endif +#ifdef CONFIG_MEMORY_FAILURE + SUBPAGE_INDEX_HWPOISON, +#endif __NR_USED_SUBPAGE, }; @@ -551,7 +554,7 @@ generic_hugetlb_get_unmapped_area(struct file *file, unsigned long addr, * Synchronization: Initially set after new page allocation with no * locking. When examined and modified during migration processing * (isolate, migrate, putback) the hugetlb_lock is held. - * HPG_temporary - - Set on a page that is temporarily allocated from the buddy + * HPG_temporary - Set on a page that is temporarily allocated from the buddy * allocator. Typically used for migration target pages when no pages * are available in the pool. The hugetlb free page path will * immediately free pages with this flag set to the buddy allocator. @@ -561,6 +564,8 @@ generic_hugetlb_get_unmapped_area(struct file *file, unsigned long addr, * HPG_freed - Set when page is on the free lists. * Synchronization: hugetlb_lock held for examination and modification. * HPG_vmemmap_optimized - Set when the vmemmap pages of the page are freed. + * HPG_raw_hwp_unreliable - Set when the hugetlb page has a hwpoison sub-page + * that is not tracked by raw_hwp_page list. */ enum hugetlb_page_flags { HPG_restore_reserve = 0, @@ -568,6 +573,7 @@ enum hugetlb_page_flags { HPG_temporary, HPG_freed, HPG_vmemmap_optimized, + HPG_raw_hwp_unreliable, __NR_HPAGEFLAGS, }; @@ -614,6 +620,7 @@ HPAGEFLAG(Migratable, migratable) HPAGEFLAG(Temporary, temporary) HPAGEFLAG(Freed, freed) HPAGEFLAG(VmemmapOptimized, vmemmap_optimized) +HPAGEFLAG(RawHwpUnreliable, raw_hwp_unreliable) #ifdef CONFIG_HUGETLB_PAGE @@ -638,9 +645,6 @@ struct hstate { unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; unsigned int surplus_huge_pages_node[MAX_NUMNODES]; -#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP - unsigned int optimize_vmemmap_pages; -#endif #ifdef CONFIG_CGROUP_HUGETLB /* cgroup control files */ struct cftype cgroup_files_dfl[8]; @@ -716,7 +720,7 @@ static inline struct hstate *hstate_vma(struct vm_area_struct *vma) return hstate_file(vma->vm_file); } -static inline unsigned long huge_page_size(struct hstate *h) +static inline unsigned long huge_page_size(const struct hstate *h) { return (unsigned long)PAGE_SIZE << h->order; } @@ -745,7 +749,7 @@ static inline bool hstate_is_gigantic(struct hstate *h) return huge_page_order(h) >= MAX_ORDER; } -static inline unsigned int pages_per_huge_page(struct hstate *h) +static inline unsigned int pages_per_huge_page(const struct hstate *h) { return 1 << h->order; } @@ -799,6 +803,14 @@ extern int dissolve_free_huge_page(struct page *page); extern int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn); +#ifdef CONFIG_MEMORY_FAILURE +extern void hugetlb_clear_page_hwpoison(struct page *hpage); +#else +static inline void hugetlb_clear_page_hwpoison(struct page *hpage) +{ +} +#endif + #ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION #ifndef arch_hugetlb_migration_supported static inline bool arch_hugetlb_migration_supported(struct hstate *h) diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 25ac28175e4f..238a03087e17 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -297,9 +297,6 @@ void iomap_finish_ioends(struct iomap_ioend *ioend, int error); void iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends); void iomap_sort_ioends(struct list_head *ioend_list); -int iomap_writepage(struct page *page, struct writeback_control *wbc, - struct iomap_writepage_ctx *wpc, - const struct iomap_writeback_ops *ops); int iomap_writepages(struct address_space *mapping, struct writeback_control *wbc, struct iomap_writepage_ctx *wpc, const struct iomap_writeback_ops *ops); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index ec5f71f7135b..616b683563a9 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -141,6 +141,7 @@ enum { IORES_DESC_DEVICE_PRIVATE_MEMORY = 6, IORES_DESC_RESERVED = 7, IORES_DESC_SOFT_RESERVED = 8, + IORES_DESC_CXL = 9, }; /* @@ -329,6 +330,8 @@ struct resource *devm_request_free_mem_region(struct device *dev, struct resource *base, unsigned long size); struct resource *request_free_mem_region(struct resource *base, unsigned long size, const char *name); +struct resource *alloc_free_mem_region(struct resource *base, + unsigned long size, unsigned long align, const char *name); static inline void irqresource_disabled(struct resource *res, u32 irq) { diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 0d61e07b6827..c74acfa1a3fe 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -59,6 +59,9 @@ enum { /* Platform provides asynchronous flush mechanism */ ND_REGION_ASYNC = 3, + /* Region was created by CXL subsystem */ + ND_REGION_CXL = 4, + /* mark newly adjusted resources as requiring a label update */ DPA_RESOURCE_ADJUSTED = 1 << 0, }; @@ -122,6 +125,7 @@ struct nd_region_desc { int numa_node; int target_node; unsigned long flags; + int memregion; struct device_node *of_node; int (*flush)(struct nd_region *nd_region, struct bio *bio); }; @@ -259,6 +263,7 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, cmd_mask, num_flush, flush_wpq, NULL, NULL, NULL); } void nvdimm_delete(struct nvdimm *nvdimm); +void nvdimm_region_delete(struct nd_region *nd_region); const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index fcef192e5e45..70ce419e2709 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -292,6 +292,7 @@ void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); __be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, struct nlm_lock *); void nlm_release_file(struct nlm_file *); +void nlmsvc_put_lockowner(struct nlm_lockowner *); void nlmsvc_release_lockowner(struct nlm_lock *); void nlmsvc_mark_resources(struct net *); void nlmsvc_free_host_resources(struct nlm_host *); diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index 398f70093cd3..67e4a2c5500b 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -41,6 +41,8 @@ struct nlm_lock { struct nfs_fh fh; struct xdr_netobj oh; u32 svid; + u64 lock_start; + u64 lock_len; struct file_lock fl; }; diff --git a/include/linux/mm.h b/include/linux/mm.h index 18e01474cf6b..3bedc449c14d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3142,13 +3142,6 @@ static inline void print_vma_addr(char *prefix, unsigned long rip) } #endif -#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP -int vmemmap_remap_free(unsigned long start, unsigned long end, - unsigned long reuse); -int vmemmap_remap_alloc(unsigned long start, unsigned long end, - unsigned long reuse, gfp_t gfp_mask); -#endif - void *sparse_buffer_alloc(unsigned long size); struct page * __populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, @@ -3183,6 +3176,7 @@ enum mf_flags { MF_SOFT_OFFLINE = 1 << 3, MF_UNPOISON = 1 << 4, MF_SW_SIMULATED = 1 << 5, + MF_NO_RETRY = 1 << 6, }; int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, unsigned long count, int mf_flags); @@ -3235,7 +3229,6 @@ enum mf_action_page_type { MF_MSG_DIFFERENT_COMPOUND, MF_MSG_HUGE, MF_MSG_FREE_HUGE, - MF_MSG_NON_PMD_HUGE, MF_MSG_UNMAP_FAILED, MF_MSG_DIRTY_SWAPCACHE, MF_MSG_CLEAN_SWAPCACHE, diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h index d7285f8148a3..15ae78cd2853 100644 --- a/include/linux/mmdebug.h +++ b/include/linux/mmdebug.h @@ -54,6 +54,15 @@ void dump_mm(const struct mm_struct *mm); } \ unlikely(__ret_warn_once); \ }) +#define VM_WARN_ON_FOLIO(cond, folio) ({ \ + int __ret_warn = !!(cond); \ + \ + if (unlikely(__ret_warn)) { \ + dump_page(&folio->page, "VM_WARN_ON_FOLIO(" __stringify(cond)")");\ + WARN_ON(1); \ + } \ + unlikely(__ret_warn); \ +}) #define VM_WARN_ON_ONCE_FOLIO(cond, folio) ({ \ static bool __section(".data.once") __warned; \ int __ret_warn_once = !!(cond); \ @@ -79,6 +88,7 @@ void dump_mm(const struct mm_struct *mm); #define VM_WARN_ON(cond) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE(cond) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE_PAGE(cond, page) BUILD_BUG_ON_INVALID(cond) +#define VM_WARN_ON_FOLIO(cond, folio) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE_FOLIO(cond, folio) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond) #define VM_WARN(cond, format...) BUILD_BUG_ON_INVALID(cond) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a17c337dbdf1..b32ed68e7dc4 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -617,6 +617,15 @@ nfs_fileid_to_ino_t(u64 fileid) #define NFS_JUKEBOX_RETRY_TIME (5 * HZ) +/* We need to block new opens while a file is being unlinked. + * If it is opened *before* we decide to unlink, we will silly-rename + * instead. If it is opened *after*, then we need to create or will fail. + * If we allow the two to race, we could end up with a file that is open + * but deleted on the server resulting in ESTALE. + * So use ->d_fsdata to record when the unlink is happening + * and block dentry revalidation while it is set. + */ +#define NFS_FSDATA_BLOCKED ((void*)1) # undef ifdebug # ifdef NFS_DEBUG diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index f0373a6cb5fb..ba7e2e4b0926 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -202,8 +202,7 @@ nfs_list_entry(struct list_head *head) return list_entry(head, struct nfs_page, wb_list); } -static inline -loff_t req_offset(struct nfs_page *req) +static inline loff_t req_offset(const struct nfs_page *req) { return (((loff_t)req->wb_index) << PAGE_SHIFT) + req->wb_offset; } diff --git a/include/linux/nfs_ssc.h b/include/linux/nfs_ssc.h index 222ae8883e85..75843c00f326 100644 --- a/include/linux/nfs_ssc.h +++ b/include/linux/nfs_ssc.h @@ -64,7 +64,7 @@ struct nfsd4_ssc_umount_item { refcount_t nsui_refcnt; unsigned long nsui_expire; struct vfsmount *nsui_vfsmount; - char nsui_ipaddr[RPC_MAX_ADDRBUFLEN]; + char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1]; }; #endif diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0e3aa0f5f324..e86cf6642d21 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1600,6 +1600,7 @@ enum { NFS_IOHDR_STAT, NFS_IOHDR_RESEND_PNFS, NFS_IOHDR_RESEND_MDS, + NFS_IOHDR_UNSTABLE_WRITES, }; struct nfs_io_completion; diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index ea19528564d1..465ff35a8c00 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -205,34 +205,15 @@ enum pageflags { #ifndef __GENERATING_BOUNDS_H #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP -DECLARE_STATIC_KEY_MAYBE(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON, - hugetlb_optimize_vmemmap_key); - -static __always_inline bool hugetlb_optimize_vmemmap_enabled(void) -{ - return static_branch_maybe(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON, - &hugetlb_optimize_vmemmap_key); -} +DECLARE_STATIC_KEY_FALSE(hugetlb_optimize_vmemmap_key); /* - * If the feature of optimizing vmemmap pages associated with each HugeTLB - * page is enabled, the head vmemmap page frame is reused and all of the tail - * vmemmap addresses map to the head vmemmap page frame (furture details can - * refer to the figure at the head of the mm/hugetlb_vmemmap.c). In other - * words, there are more than one page struct with PG_head associated with each - * HugeTLB page. We __know__ that there is only one head page struct, the tail - * page structs with PG_head are fake head page structs. We need an approach - * to distinguish between those two different types of page structs so that - * compound_head() can return the real head page struct when the parameter is - * the tail page struct but with PG_head. - * - * The page_fixed_fake_head() returns the real head page struct if the @page is - * fake page head, otherwise, returns @page which can either be a true page - * head or tail. + * Return the real head page struct iff the @page is a fake head page, otherwise + * return the @page itself. See Documentation/mm/vmemmap_dedup.rst. */ static __always_inline const struct page *page_fixed_fake_head(const struct page *page) { - if (!hugetlb_optimize_vmemmap_enabled()) + if (!static_branch_unlikely(&hugetlb_optimize_vmemmap_key)) return page; /* @@ -260,11 +241,6 @@ static inline const struct page *page_fixed_fake_head(const struct page *page) { return page; } - -static inline bool hugetlb_optimize_vmemmap_enabled(void) -{ - return false; -} #endif static __always_inline int page_is_fake_head(struct page *page) diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h new file mode 100644 index 000000000000..ed9b4df792b8 --- /dev/null +++ b/include/linux/pci-doe.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Data Object Exchange + * PCIe r6.0, sec 6.30 DOE + * + * Copyright (C) 2021 Huawei + * Jonathan Cameron <Jonathan.Cameron@huawei.com> + * + * Copyright (C) 2022 Intel Corporation + * Ira Weiny <ira.weiny@intel.com> + */ + +#ifndef LINUX_PCI_DOE_H +#define LINUX_PCI_DOE_H + +struct pci_doe_protocol { + u16 vid; + u8 type; +}; + +struct pci_doe_mb; + +/** + * struct pci_doe_task - represents a single query/response + * + * @prot: DOE Protocol + * @request_pl: The request payload + * @request_pl_sz: Size of the request payload (bytes) + * @response_pl: The response payload + * @response_pl_sz: Size of the response payload (bytes) + * @rv: Return value. Length of received response or error (bytes) + * @complete: Called when task is complete + * @private: Private data for the consumer + * @work: Used internally by the mailbox + * @doe_mb: Used internally by the mailbox + * + * The payload sizes and rv are specified in bytes with the following + * restrictions concerning the protocol. + * + * 1) The request_pl_sz must be a multiple of double words (4 bytes) + * 2) The response_pl_sz must be >= a single double word (4 bytes) + * 3) rv is returned as bytes but it will be a multiple of double words + * + * NOTE there is no need for the caller to initialize work or doe_mb. + */ +struct pci_doe_task { + struct pci_doe_protocol prot; + u32 *request_pl; + size_t request_pl_sz; + u32 *response_pl; + size_t response_pl_sz; + int rv; + void (*complete)(struct pci_doe_task *task); + void *private; + + /* No need for the user to initialize these fields */ + struct work_struct work; + struct pci_doe_mb *doe_mb; +}; + +/** + * pci_doe_for_each_off - Iterate each DOE capability + * @pdev: struct pci_dev to iterate + * @off: u16 of config space offset of each mailbox capability found + */ +#define pci_doe_for_each_off(pdev, off) \ + for (off = pci_find_next_ext_capability(pdev, off, \ + PCI_EXT_CAP_ID_DOE); \ + off > 0; \ + off = pci_find_next_ext_capability(pdev, off, \ + PCI_EXT_CAP_ID_DOE)) + +struct pci_doe_mb *pcim_doe_create_mb(struct pci_dev *pdev, u16 cap_offset); +bool pci_doe_supports_prot(struct pci_doe_mb *doe_mb, u16 vid, u8 type); +int pci_doe_submit_task(struct pci_doe_mb *doe_mb, struct pci_doe_task *task); + +#endif diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 7fa460ccf7fa..6feade66efdb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -151,6 +151,7 @@ #define PCI_CLASS_OTHERS 0xff /* Vendors and devices. Sort key: vendor first, device next. */ +#define PCI_VENDOR_ID_PCI_SIG 0x0001 #define PCI_VENDOR_ID_LOONGSON 0x0014 diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 70b45d28e7a9..487117ccb1bc 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -27,6 +27,26 @@ struct gpio_chip; struct device_node; /** + * struct pingroup - provides information on pingroup + * @name: a name for pingroup + * @pins: an array of pins in the pingroup + * @npins: number of pins in the pingroup + */ +struct pingroup { + const char *name; + const unsigned int *pins; + size_t npins; +}; + +/* Convenience macro to define a single named or anonymous pingroup */ +#define PINCTRL_PINGROUP(_name, _pins, _npins) \ +(struct pingroup){ \ + .name = _name, \ + .pins = _pins, \ + .npins = _npins, \ +} + +/** * struct pinctrl_pin_desc - boards/machines provide information on their * pins, pads or other muxable units in this struct * @number: unique pin number from the global pin number space diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 4ea496924106..6cb65df3e3ba 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -157,26 +157,6 @@ static inline bool pipe_full(unsigned int head, unsigned int tail, } /** - * pipe_space_for_user - Return number of slots available to userspace - * @head: The pipe ring head pointer - * @tail: The pipe ring tail pointer - * @pipe: The pipe info structure - */ -static inline unsigned int pipe_space_for_user(unsigned int head, unsigned int tail, - struct pipe_inode_info *pipe) -{ - unsigned int p_occupancy, p_space; - - p_occupancy = pipe_occupancy(head, tail); - if (p_occupancy >= pipe->max_usage) - return 0; - p_space = pipe->ring_size - p_occupancy; - if (p_space > pipe->max_usage) - p_space = pipe->max_usage; - return p_space; -} - -/** * pipe_buf_get - get a reference to a pipe_buffer * @pipe: the pipe that the buffer belongs to * @buf: the buffer to get a reference to diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 6708b4ec244d..dc1fb5890792 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -57,37 +57,39 @@ struct dev_pm_opp_icc_bw { u32 peak; }; -/** - * struct dev_pm_opp_info - OPP freq/voltage/current values - * @rate: Target clk rate in hz - * @supplies: Array of voltage/current values for all power supplies - * - * This structure stores the freq/voltage/current values for a single OPP. - */ -struct dev_pm_opp_info { - unsigned long rate; - struct dev_pm_opp_supply *supplies; -}; +typedef int (*config_regulators_t)(struct device *dev, + struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, + struct regulator **regulators, unsigned int count); + +typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table, + struct dev_pm_opp *opp, void *data, bool scaling_down); /** - * struct dev_pm_set_opp_data - Set OPP data - * @old_opp: Old OPP info - * @new_opp: New OPP info - * @regulators: Array of regulator pointers - * @regulator_count: Number of regulators - * @clk: Pointer to clk - * @dev: Pointer to the struct device + * struct dev_pm_opp_config - Device OPP configuration values + * @clk_names: Clk names, NULL terminated array. + * @config_clks: Custom set clk helper. + * @prop_name: Name to postfix to properties. + * @config_regulators: Custom set regulator helper. + * @supported_hw: Array of hierarchy of versions to match. + * @supported_hw_count: Number of elements in the array. + * @regulator_names: Array of pointers to the names of the regulator, NULL terminated. + * @genpd_names: Null terminated array of pointers containing names of genpd to + * attach. + * @virt_devs: Pointer to return the array of virtual devices. * - * This structure contains all information required for setting an OPP. + * This structure contains platform specific OPP configurations for the device. */ -struct dev_pm_set_opp_data { - struct dev_pm_opp_info old_opp; - struct dev_pm_opp_info new_opp; - - struct regulator **regulators; - unsigned int regulator_count; - struct clk *clk; - struct device *dev; +struct dev_pm_opp_config { + /* NULL terminated */ + const char * const *clk_names; + config_clks_t config_clks; + const char *prop_name; + config_regulators_t config_regulators; + const unsigned int *supported_hw; + unsigned int supported_hw_count; + const char * const *regulator_names; + const char * const *genpd_names; + struct device ***virt_devs; }; #if defined(CONFIG_PM_OPP) @@ -97,6 +99,8 @@ void dev_pm_opp_put_opp_table(struct opp_table *opp_table); unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); +int dev_pm_opp_get_supplies(struct dev_pm_opp *opp, struct dev_pm_opp_supply *supplies); + unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp); unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); @@ -119,8 +123,6 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, bool available); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); -struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, - unsigned long u_volt); struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level); @@ -154,23 +156,13 @@ int dev_pm_opp_disable(struct device *dev, unsigned long freq); int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb); int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb); -struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count); -void dev_pm_opp_put_supported_hw(struct opp_table *opp_table); -int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count); -struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name); -void dev_pm_opp_put_prop_name(struct opp_table *opp_table); -struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); -void dev_pm_opp_put_regulators(struct opp_table *opp_table); -int devm_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); -struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name); -void dev_pm_opp_put_clkname(struct opp_table *opp_table); -int devm_pm_opp_set_clkname(struct device *dev, const char *name); -struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); -void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); -int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs); -void dev_pm_opp_detach_genpd(struct opp_table *opp_table); -int devm_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs); +int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config); +int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config); +void dev_pm_opp_clear_config(int token); +int dev_pm_opp_config_clks_simple(struct device *dev, + struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, + bool scaling_down); + struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); @@ -198,6 +190,11 @@ static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) return 0; } +static inline int dev_pm_opp_get_supplies(struct dev_pm_opp *opp, struct dev_pm_opp_supply *supplies) +{ + return -EOPNOTSUPP; +} + static inline unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp) { return 0; @@ -274,12 +271,6 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } -static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, - unsigned long u_volt) -{ - return ERR_PTR(-EOPNOTSUPP); -} - static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { @@ -342,79 +333,21 @@ static inline int dev_pm_opp_unregister_notifier(struct device *dev, struct noti return -EOPNOTSUPP; } -static inline struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, - const u32 *versions, - unsigned int count) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) {} - -static inline int devm_pm_opp_set_supported_hw(struct device *dev, - const u32 *versions, - unsigned int count) -{ - return -EOPNOTSUPP; -} - -static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} - -static inline int devm_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) -{ - return -EOPNOTSUPP; -} - -static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_put_prop_name(struct opp_table *opp_table) {} - -static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {} - -static inline int devm_pm_opp_set_regulators(struct device *dev, - const char * const names[], - unsigned int count) +static inline int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) { return -EOPNOTSUPP; } -static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} - -static inline int devm_pm_opp_set_clkname(struct device *dev, const char *name) +static inline int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) { return -EOPNOTSUPP; } -static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} +static inline void dev_pm_opp_clear_config(int token) {} -static inline int devm_pm_opp_attach_genpd(struct device *dev, - const char * const *names, - struct device ***virt_devs) +static inline int dev_pm_opp_config_clks_simple(struct device *dev, + struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, + bool scaling_down) { return -EOPNOTSUPP; } @@ -469,8 +402,6 @@ static inline int dev_pm_opp_sync_regulators(struct device *dev) int dev_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_add_table_indexed(struct device *dev, int index); int devm_pm_opp_of_add_table_indexed(struct device *dev, int index); -int dev_pm_opp_of_add_table_noclk(struct device *dev, int index); -int devm_pm_opp_of_add_table_noclk(struct device *dev, int index); void dev_pm_opp_of_remove_table(struct device *dev); int devm_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask); @@ -501,16 +432,6 @@ static inline int devm_pm_opp_of_add_table_indexed(struct device *dev, int index return -EOPNOTSUPP; } -static inline int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) -{ - return -EOPNOTSUPP; -} - -static inline int devm_pm_opp_of_add_table_noclk(struct device *dev, int index) -{ - return -EOPNOTSUPP; -} - static inline void dev_pm_opp_of_remove_table(struct device *dev) { } @@ -565,4 +486,149 @@ static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_ta } #endif +/* OPP Configuration helpers */ + +/* Regulators helpers */ +static inline int dev_pm_opp_set_regulators(struct device *dev, + const char * const names[]) +{ + struct dev_pm_opp_config config = { + .regulator_names = names, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_put_regulators(int token) +{ + dev_pm_opp_clear_config(token); +} + +static inline int devm_pm_opp_set_regulators(struct device *dev, + const char * const names[]) +{ + struct dev_pm_opp_config config = { + .regulator_names = names, + }; + + return devm_pm_opp_set_config(dev, &config); +} + +/* Supported-hw helpers */ +static inline int dev_pm_opp_set_supported_hw(struct device *dev, + const u32 *versions, + unsigned int count) +{ + struct dev_pm_opp_config config = { + .supported_hw = versions, + .supported_hw_count = count, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_put_supported_hw(int token) +{ + dev_pm_opp_clear_config(token); +} + +static inline int devm_pm_opp_set_supported_hw(struct device *dev, + const u32 *versions, + unsigned int count) +{ + struct dev_pm_opp_config config = { + .supported_hw = versions, + .supported_hw_count = count, + }; + + return devm_pm_opp_set_config(dev, &config); +} + +/* clkname helpers */ +static inline int dev_pm_opp_set_clkname(struct device *dev, const char *name) +{ + const char *names[] = { name, NULL }; + struct dev_pm_opp_config config = { + .clk_names = names, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_put_clkname(int token) +{ + dev_pm_opp_clear_config(token); +} + +static inline int devm_pm_opp_set_clkname(struct device *dev, const char *name) +{ + const char *names[] = { name, NULL }; + struct dev_pm_opp_config config = { + .clk_names = names, + }; + + return devm_pm_opp_set_config(dev, &config); +} + +/* config-regulators helpers */ +static inline int dev_pm_opp_set_config_regulators(struct device *dev, + config_regulators_t helper) +{ + struct dev_pm_opp_config config = { + .config_regulators = helper, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_put_config_regulators(int token) +{ + dev_pm_opp_clear_config(token); +} + +/* genpd helpers */ +static inline int dev_pm_opp_attach_genpd(struct device *dev, + const char * const *names, + struct device ***virt_devs) +{ + struct dev_pm_opp_config config = { + .genpd_names = names, + .virt_devs = virt_devs, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_detach_genpd(int token) +{ + dev_pm_opp_clear_config(token); +} + +static inline int devm_pm_opp_attach_genpd(struct device *dev, + const char * const *names, + struct device ***virt_devs) +{ + struct dev_pm_opp_config config = { + .genpd_names = names, + .virt_devs = virt_devs, + }; + + return devm_pm_opp_set_config(dev, &config); +} + +/* prop-name helpers */ +static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) +{ + struct dev_pm_opp_config config = { + .prop_name = name, + }; + + return dev_pm_opp_set_config(dev, &config); +} + +static inline void dev_pm_opp_put_prop_name(int token) +{ + dev_pm_opp_clear_config(token); +} + #endif /* __LINUX_OPP_H__ */ diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 153b6dec9b6a..48f4b645193b 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -278,7 +278,8 @@ static inline void sk_msg_sg_copy_clear(struct sk_msg *msg, u32 start) static inline struct sk_psock *sk_psock(const struct sock *sk) { - return rcu_dereference_sk_user_data(sk); + return __rcu_dereference_sk_user_data_with_flags(sk, + SK_USER_DATA_PSOCK); } static inline void sk_psock_set_state(struct sk_psock *psock, diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 90501404fa49..75eea5ebb179 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -234,13 +234,18 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *, struct rpc_xprt_switch *, struct rpc_xprt *, void *); +void rpc_clnt_manage_trunked_xprts(struct rpc_clnt *); +void rpc_clnt_probe_trunked_xprts(struct rpc_clnt *, + struct rpc_add_xprt_test *); const char *rpc_proc_name(const struct rpc_task *task); void rpc_clnt_xprt_switch_put(struct rpc_clnt *); void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *); +void rpc_clnt_xprt_switch_remove_xprt(struct rpc_clnt *, struct rpc_xprt *); bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, const struct sockaddr *sap); +void rpc_clnt_xprt_set_online(struct rpc_clnt *clnt, struct rpc_xprt *xprt); void rpc_cleanup_clids(void); static inline int rpc_reply_expected(struct rpc_task *task) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 1d7a3e51b795..acc62647317c 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -61,8 +61,6 @@ struct rpc_task { struct rpc_wait tk_wait; /* RPC wait */ } u; - int tk_rpc_status; /* Result of last RPC operation */ - /* * RPC call state */ @@ -82,6 +80,8 @@ struct rpc_task { ktime_t tk_start; /* RPC task init timestamp */ pid_t tk_owner; /* Process id for batching tasks */ + + int tk_rpc_status; /* Result of last RPC operation */ unsigned short tk_flags; /* misc flags */ unsigned short tk_timeouts; /* maj timeouts */ diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 5860f32e3958..69175029abbb 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -258,10 +258,13 @@ extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); -extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, unsigned int length); -extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length); +extern void xdr_set_pagelen(struct xdr_stream *, unsigned int len); extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, unsigned int len); +extern unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset, + unsigned int target, unsigned int length); +extern unsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset, + unsigned int length); /** * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. @@ -419,8 +422,8 @@ static inline int xdr_stream_encode_item_absent(struct xdr_stream *xdr) */ static inline __be32 *xdr_encode_bool(__be32 *p, u32 n) { - *p = n ? xdr_one : xdr_zero; - return p++; + *p++ = n ? xdr_one : xdr_zero; + return p; } /** diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 522bbf937957..b9f59aabee53 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -144,7 +144,8 @@ struct rpc_xprt_ops { unsigned short (*get_srcport)(struct rpc_xprt *xprt); int (*buf_alloc)(struct rpc_task *task); void (*buf_free)(struct rpc_task *task); - int (*prepare_request)(struct rpc_rqst *req); + int (*prepare_request)(struct rpc_rqst *req, + struct xdr_buf *buf); int (*send_request)(struct rpc_rqst *req); void (*wait_for_reply_request)(struct rpc_task *task); void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task); @@ -505,4 +506,7 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt) return test_and_set_bit(XPRT_BINDING, &xprt->state); } +void xprt_set_offline_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps); +void xprt_set_online_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps); +void xprt_delete_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps); #endif /* _LINUX_SUNRPC_XPRT_H */ diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h index bbb8a5fa0816..c0514c684b2c 100644 --- a/include/linux/sunrpc/xprtmultipath.h +++ b/include/linux/sunrpc/xprtmultipath.h @@ -55,7 +55,7 @@ extern void rpc_xprt_switch_set_roundrobin(struct rpc_xprt_switch *xps); extern void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps, struct rpc_xprt *xprt); extern void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps, - struct rpc_xprt *xprt); + struct rpc_xprt *xprt, bool offline); extern void xprt_iter_init(struct rpc_xprt_iter *xpi, struct rpc_xprt_switch *xps); @@ -63,8 +63,13 @@ extern void xprt_iter_init(struct rpc_xprt_iter *xpi, extern void xprt_iter_init_listall(struct rpc_xprt_iter *xpi, struct rpc_xprt_switch *xps); +extern void xprt_iter_init_listoffline(struct rpc_xprt_iter *xpi, + struct rpc_xprt_switch *xps); + extern void xprt_iter_destroy(struct rpc_xprt_iter *xpi); +extern void xprt_iter_rewind(struct rpc_xprt_iter *xpi); + extern struct rpc_xprt_switch *xprt_iter_xchg_switch( struct rpc_xprt_iter *xpi, struct rpc_xprt_switch *newswitch); diff --git a/include/linux/swapops.h b/include/linux/swapops.h index bb7afd03a324..a3d435bf9f97 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h @@ -490,6 +490,11 @@ static inline void num_poisoned_pages_dec(void) atomic_long_dec(&num_poisoned_pages); } +static inline void num_poisoned_pages_sub(long i) +{ + atomic_long_sub(i, &num_poisoned_pages); +} + #else static inline swp_entry_t make_hwpoison_entry(struct page *page) @@ -505,6 +510,10 @@ static inline int is_hwpoison_entry(swp_entry_t swp) static inline void num_poisoned_pages_inc(void) { } + +static inline void num_poisoned_pages_sub(long i) +{ +} #endif static inline int non_swap_entry(swp_entry_t entry) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 17b42ce89d3e..780690dc08cd 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -268,6 +268,10 @@ static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * return NULL; } +static inline void register_sysctl_init(const char *path, struct ctl_table *table) +{ +} + static inline struct ctl_table_header *register_sysctl_mount_point(const char *path) { return NULL; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index e3f1e8ac1f85..fd3fe5c8c17f 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -235,6 +235,22 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_WO(_name, _size) #define BIN_ATTR_RW(_name, _size) \ struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size) + +#define __BIN_ATTR_ADMIN_RO(_name, _size) { \ + .attr = { .name = __stringify(_name), .mode = 0400 }, \ + .read = _name##_read, \ + .size = _size, \ +} + +#define __BIN_ATTR_ADMIN_RW(_name, _size) \ + __BIN_ATTR(_name, 0600, _name##_read, _name##_write, _size) + +#define BIN_ATTR_ADMIN_RO(_name, _size) \ +struct bin_attribute bin_attr_##_name = __BIN_ATTR_ADMIN_RO(_name, _size) + +#define BIN_ATTR_ADMIN_RW(_name, _size) \ +struct bin_attribute bin_attr_##_name = __BIN_ATTR_ADMIN_RW(_name, _size) + struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *, char *); ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); diff --git a/include/linux/uio.h b/include/linux/uio.h index 9a2dc496d535..5896af36199c 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -26,6 +26,7 @@ enum iter_type { ITER_PIPE, ITER_XARRAY, ITER_DISCARD, + ITER_UBUF, }; struct iov_iter_state { @@ -38,7 +39,11 @@ struct iov_iter { u8 iter_type; bool nofault; bool data_source; - size_t iov_offset; + bool user_backed; + union { + size_t iov_offset; + int last_offset; + }; size_t count; union { const struct iovec *iov; @@ -46,6 +51,7 @@ struct iov_iter { const struct bio_vec *bvec; struct xarray *xarray; struct pipe_inode_info *pipe; + void __user *ubuf; }; union { unsigned long nr_segs; @@ -70,6 +76,11 @@ static inline void iov_iter_save_state(struct iov_iter *iter, state->nr_segs = iter->nr_segs; } +static inline bool iter_is_ubuf(const struct iov_iter *i) +{ + return iov_iter_type(i) == ITER_UBUF; +} + static inline bool iter_is_iovec(const struct iov_iter *i) { return iov_iter_type(i) == ITER_IOVEC; @@ -105,6 +116,11 @@ static inline unsigned char iov_iter_rw(const struct iov_iter *i) return i->data_source ? WRITE : READ; } +static inline bool user_backed_iter(const struct iov_iter *i) +{ + return i->user_backed; +} + /* * Total number of bytes covered by an iovec. * @@ -231,9 +247,9 @@ void iov_iter_pipe(struct iov_iter *i, unsigned int direction, struct pipe_inode void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count); void iov_iter_xarray(struct iov_iter *i, unsigned int direction, struct xarray *xarray, loff_t start, size_t count); -ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, +ssize_t iov_iter_get_pages2(struct iov_iter *i, struct page **pages, size_t maxsize, unsigned maxpages, size_t *start); -ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages, +ssize_t iov_iter_get_pages_alloc2(struct iov_iter *i, struct page ***pages, size_t maxsize, size_t *start); int iov_iter_npages(const struct iov_iter *i, int maxpages); void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state); @@ -322,4 +338,17 @@ ssize_t __import_iovec(int type, const struct iovec __user *uvec, int import_single_range(int type, void __user *buf, size_t len, struct iovec *iov, struct iov_iter *i); +static inline void iov_iter_ubuf(struct iov_iter *i, unsigned int direction, + void __user *buf, size_t count) +{ + WARN_ON(direction & ~(READ | WRITE)); + *i = (struct iov_iter) { + .iter_type = ITER_UBUF, + .user_backed = true, + .data_source = direction, + .ubuf = buf, + .count = count + }; +} + #endif diff --git a/include/net/ax88796.h b/include/net/ax88796.h index b658471f97f0..303100f08ab8 100644 --- a/include/net/ax88796.h +++ b/include/net/ax88796.h @@ -34,8 +34,8 @@ struct ax_plat_data { const unsigned char *buf, int star_page); void (*block_input)(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset); - /* returns nonzero if a pending interrupt request might by caused by - * the ax88786. Handles all interrupts if set to NULL + /* returns nonzero if a pending interrupt request might be caused by + * the ax88796. Handles all interrupts if set to NULL */ int (*check_irq)(struct platform_device *pdev); }; diff --git a/include/net/bonding.h b/include/net/bonding.h index 6e78d657aa05..afd606df149a 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -161,8 +161,9 @@ struct slave { struct net_device *dev; /* first - useful for panic debug */ struct bonding *bond; /* our master */ int delay; - /* all three in jiffies */ + /* all 4 in jiffies */ unsigned long last_link_up; + unsigned long last_tx; unsigned long last_rx; unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS]; s8 link; /* one of BOND_LINK_XXXX */ @@ -540,6 +541,16 @@ static inline unsigned long slave_last_rx(struct bonding *bond, return slave->last_rx; } +static inline void slave_update_last_tx(struct slave *slave) +{ + WRITE_ONCE(slave->last_tx, jiffies); +} + +static inline unsigned long slave_last_tx(struct slave *slave) +{ + return READ_ONCE(slave->last_tx); +} + #ifdef CONFIG_NET_POLL_CONTROLLER static inline netdev_tx_t bond_netpoll_send_skb(const struct slave *slave, struct sk_buff *skb) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 7cb3fa8310ed..56a50e1c51b9 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -11,6 +11,7 @@ /** * struct genl_multicast_group - generic netlink multicast group * @name: name of the multicast group, names are per-family + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) */ struct genl_multicast_group { char name[GENL_NAMSIZ]; @@ -116,7 +117,7 @@ enum genl_validate_flags { * struct genl_small_ops - generic netlink operations (small version) * @cmd: command identifier * @internal_flags: flags used by the family - * @flags: flags + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) * @validate: validation flags from enum genl_validate_flags * @doit: standard command callback * @dumpit: callback for dumpers @@ -137,7 +138,7 @@ struct genl_small_ops { * struct genl_ops - generic netlink operations * @cmd: command identifier * @internal_flags: flags used by the family - * @flags: flags + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) * @maxattr: maximum number of attributes supported * @policy: netlink policy (takes precedence over family policy) * @validate: validation flags from enum genl_validate_flags diff --git a/include/net/mptcp.h b/include/net/mptcp.h index ac9cf7271d46..412479ebf5ad 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -291,4 +291,8 @@ struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk); static inline struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { return NULL; } #endif +#if !IS_ENABLED(CONFIG_MPTCP) +struct mptcp_sock { }; +#endif + #endif /* __NET_MPTCP_H */ diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 8bfb9c74afbf..99aae36c04b9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -221,13 +221,18 @@ struct nft_ctx { bool report; }; +enum nft_data_desc_flags { + NFT_DATA_DESC_SETELEM = (1 << 0), +}; + struct nft_data_desc { enum nft_data_types type; + unsigned int size; unsigned int len; + unsigned int flags; }; -int nft_data_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, struct nft_data_desc *desc, const struct nlattr *nla); void nft_data_hold(const struct nft_data *data, enum nft_data_types type); void nft_data_release(const struct nft_data *data, enum nft_data_types type); @@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[]; struct nft_set_ext_tmpl { u16 len; u8 offset[NFT_SET_EXT_NUM]; + u8 ext_len[NFT_SET_EXT_NUM]; }; /** @@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, return -EINVAL; tmpl->offset[id] = tmpl->len; - tmpl->len += nft_set_ext_types[id].len + len; + tmpl->ext_len[id] = nft_set_ext_types[id].len + len; + tmpl->len += tmpl->ext_len[id]; return 0; } diff --git a/include/net/sock.h b/include/net/sock.h index a7273b289188..05a1bbdf5805 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -545,14 +545,26 @@ enum sk_pacing { SK_PACING_FQ = 2, }; -/* Pointer stored in sk_user_data might not be suitable for copying - * when cloning the socket. For instance, it can point to a reference - * counted object. sk_user_data bottom bit is set if pointer must not - * be copied. +/* flag bits in sk_user_data + * + * - SK_USER_DATA_NOCOPY: Pointer stored in sk_user_data might + * not be suitable for copying when cloning the socket. For instance, + * it can point to a reference counted object. sk_user_data bottom + * bit is set if pointer must not be copied. + * + * - SK_USER_DATA_BPF: Mark whether sk_user_data field is + * managed/owned by a BPF reuseport array. This bit should be set + * when sk_user_data's sk is added to the bpf's reuseport_array. + * + * - SK_USER_DATA_PSOCK: Mark whether pointer stored in + * sk_user_data points to psock type. This bit should be set + * when sk_user_data is assigned to a psock object. */ #define SK_USER_DATA_NOCOPY 1UL -#define SK_USER_DATA_BPF 2UL /* Managed by BPF */ -#define SK_USER_DATA_PTRMASK ~(SK_USER_DATA_NOCOPY | SK_USER_DATA_BPF) +#define SK_USER_DATA_BPF 2UL +#define SK_USER_DATA_PSOCK 4UL +#define SK_USER_DATA_PTRMASK ~(SK_USER_DATA_NOCOPY | SK_USER_DATA_BPF |\ + SK_USER_DATA_PSOCK) /** * sk_user_data_is_nocopy - Test if sk_user_data pointer must not be copied @@ -565,24 +577,40 @@ static inline bool sk_user_data_is_nocopy(const struct sock *sk) #define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data))) +/** + * __rcu_dereference_sk_user_data_with_flags - return the pointer + * only if argument flags all has been set in sk_user_data. Otherwise + * return NULL + * + * @sk: socket + * @flags: flag bits + */ +static inline void * +__rcu_dereference_sk_user_data_with_flags(const struct sock *sk, + uintptr_t flags) +{ + uintptr_t sk_user_data = (uintptr_t)rcu_dereference(__sk_user_data(sk)); + + WARN_ON_ONCE(flags & SK_USER_DATA_PTRMASK); + + if ((sk_user_data & flags) == flags) + return (void *)(sk_user_data & SK_USER_DATA_PTRMASK); + return NULL; +} + #define rcu_dereference_sk_user_data(sk) \ + __rcu_dereference_sk_user_data_with_flags(sk, 0) +#define __rcu_assign_sk_user_data_with_flags(sk, ptr, flags) \ ({ \ - void *__tmp = rcu_dereference(__sk_user_data((sk))); \ - (void *)((uintptr_t)__tmp & SK_USER_DATA_PTRMASK); \ -}) -#define rcu_assign_sk_user_data(sk, ptr) \ -({ \ - uintptr_t __tmp = (uintptr_t)(ptr); \ - WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ - rcu_assign_pointer(__sk_user_data((sk)), __tmp); \ -}) -#define rcu_assign_sk_user_data_nocopy(sk, ptr) \ -({ \ - uintptr_t __tmp = (uintptr_t)(ptr); \ - WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ + uintptr_t __tmp1 = (uintptr_t)(ptr), \ + __tmp2 = (uintptr_t)(flags); \ + WARN_ON_ONCE(__tmp1 & ~SK_USER_DATA_PTRMASK); \ + WARN_ON_ONCE(__tmp2 & SK_USER_DATA_PTRMASK); \ rcu_assign_pointer(__sk_user_data((sk)), \ - __tmp | SK_USER_DATA_NOCOPY); \ + __tmp1 | __tmp2); \ }) +#define rcu_assign_sk_user_data(sk, ptr) \ + __rcu_assign_sk_user_data_with_flags(sk, ptr, 0) static inline struct net *sock_net(const struct sock *sk) diff --git a/include/net/tls.h b/include/net/tls.h index b75b5727abdb..cb205f9d9473 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -237,7 +237,7 @@ struct tls_context { void *priv_ctx_tx; void *priv_ctx_rx; - struct net_device *netdev; + struct net_device __rcu *netdev; /* rw cache line */ struct cipher_context tx; diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index d0337a41141c..cbd3ddd7c33d 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -360,7 +360,6 @@ TRACE_EVENT(aer_event, EM ( MF_MSG_DIFFERENT_COMPOUND, "different compound page after locking" ) \ EM ( MF_MSG_HUGE, "huge page" ) \ EM ( MF_MSG_FREE_HUGE, "free huge page" ) \ - EM ( MF_MSG_NON_PMD_HUGE, "non-pmd-sized huge page" ) \ EM ( MF_MSG_UNMAP_FAILED, "unmapping failed page" ) \ EM ( MF_MSG_DIRTY_SWAPCACHE, "dirty swapcache page" ) \ EM ( MF_MSG_CLEAN_SWAPCACHE, "clean swapcache page" ) \ diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 499f5fabd20f..e9d412d19dbb 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -727,31 +727,31 @@ TRACE_EVENT(afs_cb_call, ); TRACE_EVENT(afs_call, - TP_PROTO(struct afs_call *call, enum afs_call_trace op, - int usage, int outstanding, const void *where), + TP_PROTO(unsigned int call_debug_id, enum afs_call_trace op, + int ref, int outstanding, const void *where), - TP_ARGS(call, op, usage, outstanding, where), + TP_ARGS(call_debug_id, op, ref, outstanding, where), TP_STRUCT__entry( __field(unsigned int, call ) __field(int, op ) - __field(int, usage ) + __field(int, ref ) __field(int, outstanding ) __field(const void *, where ) ), TP_fast_assign( - __entry->call = call->debug_id; + __entry->call = call_debug_id; __entry->op = op; - __entry->usage = usage; + __entry->ref = ref; __entry->outstanding = outstanding; __entry->where = where; ), - TP_printk("c=%08x %s u=%d o=%d sp=%pSR", + TP_printk("c=%08x %s r=%d o=%d sp=%pSR", __entry->call, __print_symbolic(__entry->op, afs_call_traces), - __entry->usage, + __entry->ref, __entry->outstanding, __entry->where) ); @@ -1433,10 +1433,10 @@ TRACE_EVENT(afs_cb_miss, ); TRACE_EVENT(afs_server, - TP_PROTO(struct afs_server *server, int ref, int active, + TP_PROTO(unsigned int server_debug_id, int ref, int active, enum afs_server_trace reason), - TP_ARGS(server, ref, active, reason), + TP_ARGS(server_debug_id, ref, active, reason), TP_STRUCT__entry( __field(unsigned int, server ) @@ -1446,7 +1446,7 @@ TRACE_EVENT(afs_server, ), TP_fast_assign( - __entry->server = server->debug_id; + __entry->server = server_debug_id; __entry->ref = ref; __entry->active = active; __entry->reason = reason; @@ -1476,36 +1476,36 @@ TRACE_EVENT(afs_volume, __entry->reason = reason; ), - TP_printk("V=%llx %s u=%d", + TP_printk("V=%llx %s ur=%d", __entry->vid, __print_symbolic(__entry->reason, afs_volume_traces), __entry->ref) ); TRACE_EVENT(afs_cell, - TP_PROTO(unsigned int cell_debug_id, int usage, int active, + TP_PROTO(unsigned int cell_debug_id, int ref, int active, enum afs_cell_trace reason), - TP_ARGS(cell_debug_id, usage, active, reason), + TP_ARGS(cell_debug_id, ref, active, reason), TP_STRUCT__entry( __field(unsigned int, cell ) - __field(int, usage ) + __field(int, ref ) __field(int, active ) __field(int, reason ) ), TP_fast_assign( __entry->cell = cell_debug_id; - __entry->usage = usage; + __entry->ref = ref; __entry->active = active; __entry->reason = reason; ), - TP_printk("L=%08x %s u=%d a=%d", + TP_printk("L=%08x %s r=%d a=%d", __entry->cell, __print_symbolic(__entry->reason, afs_cell_traces), - __entry->usage, + __entry->ref, __entry->active) ); diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index cb3fb337e880..c078c48a8e6d 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -49,6 +49,7 @@ enum fscache_volume_trace { enum fscache_cookie_trace { fscache_cookie_collision, fscache_cookie_discard, + fscache_cookie_failed, fscache_cookie_get_attach_object, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, @@ -131,6 +132,7 @@ enum fscache_access_trace { #define fscache_cookie_traces \ EM(fscache_cookie_collision, "*COLLIDE*") \ EM(fscache_cookie_discard, "DISCARD ") \ + EM(fscache_cookie_failed, "FAILED ") \ EM(fscache_cookie_get_attach_object, "GET attch") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 37e1e1a2d67d..3bd31ea23fee 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -282,7 +282,7 @@ DEFINE_EVENT(kvm_async_get_page_class, kvm_try_async_get_page, TP_ARGS(gva, gfn) ); -DEFINE_EVENT(kvm_async_get_page_class, kvm_async_pf_doublefault, +DEFINE_EVENT(kvm_async_get_page_class, kvm_async_pf_repeated_fault, TP_PROTO(u64 gva, u64 gfn), diff --git a/include/trace/events/power.h b/include/trace/events/power.h index c708521e4ed5..77f14f7a11d4 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -40,6 +40,28 @@ DEFINE_EVENT(cpu, cpu_idle, TP_ARGS(state, cpu_id) ); +TRACE_EVENT(cpu_idle_miss, + + TP_PROTO(unsigned int cpu_id, unsigned int state, bool below), + + TP_ARGS(cpu_id, state, below), + + TP_STRUCT__entry( + __field(u32, cpu_id) + __field(u32, state) + __field(bool, below) + ), + + TP_fast_assign( + __entry->cpu_id = cpu_id; + __entry->state = state; + __entry->below = below; + ), + + TP_printk("cpu_id=%lu state=%lu type=%s", (unsigned long)__entry->cpu_id, + (unsigned long)__entry->state, (__entry->below)?"below":"above") +); + TRACE_EVENT(powernv_throttle, TP_PROTO(int chip_id, const char *reason, int pmax), diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index b61d9c90fa26..f48f2ab9d238 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -1266,6 +1266,26 @@ TRACE_EVENT(xprt_reserve, ) ); +TRACE_EVENT(xs_data_ready, + TP_PROTO( + const struct rpc_xprt *xprt + ), + + TP_ARGS(xprt), + + TP_STRUCT__entry( + __string(addr, xprt->address_strings[RPC_DISPLAY_ADDR]) + __string(port, xprt->address_strings[RPC_DISPLAY_PORT]) + ), + + TP_fast_assign( + __assign_str(addr, xprt->address_strings[RPC_DISPLAY_ADDR]); + __assign_str(port, xprt->address_strings[RPC_DISPLAY_PORT]); + ), + + TP_printk("peer=[%s]:%s", __get_str(addr), __get_str(port)) +); + TRACE_EVENT(xs_stream_read_data, TP_PROTO(struct rpc_xprt *xprt, ssize_t err, size_t total), @@ -1989,20 +2009,24 @@ TRACE_EVENT(svc_wake_up, TRACE_EVENT(svc_alloc_arg_err, TP_PROTO( - unsigned int pages + unsigned int requested, + unsigned int allocated ), - TP_ARGS(pages), + TP_ARGS(requested, allocated), TP_STRUCT__entry( - __field(unsigned int, pages) + __field(unsigned int, requested) + __field(unsigned int, allocated) ), TP_fast_assign( - __entry->pages = pages; + __entry->requested = requested; + __entry->allocated = allocated; ), - TP_printk("pages=%u", __entry->pages) + TP_printk("requested=%u allocated=%u", + __entry->requested, __entry->allocated) ); DECLARE_EVENT_CLASS(svc_deferred_event, diff --git a/include/uapi/linux/atm_zatm.h b/include/uapi/linux/atm_zatm.h new file mode 100644 index 000000000000..5135027b93c1 --- /dev/null +++ b/include/uapi/linux/atm_zatm.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atm_zatm.h - Driver-specific declarations of the ZATM driver (for use by + driver-specific utilities) */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef LINUX_ATM_ZATM_H +#define LINUX_ATM_ZATM_H + +/* + * Note: non-kernel programs including this file must also include + * sys/types.h for struct timeval + */ + +#include <linux/atmapi.h> +#include <linux/atmioc.h> + +#define ZATM_GETPOOL _IOW('a',ATMIOC_SARPRV+1,struct atmif_sioc) + /* get pool statistics */ +#define ZATM_GETPOOLZ _IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc) + /* get statistics and zero */ +#define ZATM_SETPOOL _IOW('a',ATMIOC_SARPRV+3,struct atmif_sioc) + /* set pool parameters */ + +struct zatm_pool_info { + int ref_count; /* free buffer pool usage counters */ + int low_water,high_water; /* refill parameters */ + int rqa_count,rqu_count; /* queue condition counters */ + int offset,next_off; /* alignment optimizations: offset */ + int next_cnt,next_thres; /* repetition counter and threshold */ +}; + +struct zatm_pool_req { + int pool_num; /* pool number */ + struct zatm_pool_info info; /* actual information */ +}; + +#define ZATM_OAM_POOL 0 /* free buffer pool for OAM cells */ +#define ZATM_AAL0_POOL 1 /* free buffer pool for AAL0 cells */ +#define ZATM_AAL5_POOL_BASE 2 /* first AAL5 free buffer pool */ +#define ZATM_LAST_POOL ZATM_AAL5_POOL_BASE+10 /* max. 64 kB */ + +#define ZATM_TIMER_HISTORY_SIZE 16 /* number of timer adjustments to + record; must be 2^n */ + +#endif diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h index d83f214b4134..ddba3ca01e39 100644 --- a/include/uapi/linux/genetlink.h +++ b/include/uapi/linux/genetlink.h @@ -87,6 +87,8 @@ enum { __CTRL_ATTR_MCAST_GRP_MAX, }; +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) + enum { CTRL_ATTR_POLICY_UNSPEC, CTRL_ATTR_POLICY_DO, @@ -96,7 +98,6 @@ enum { CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1 }; -#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) - +#define CTRL_ATTR_POLICY_MAX (__CTRL_ATTR_POLICY_DUMP_MAX - 1) #endif /* _UAPI__LINUX_GENERIC_NETLINK_H */ diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h b/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h index 23e91a9c2583..0b7b16dbdec2 100644 --- a/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h +++ b/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h @@ -17,4 +17,4 @@ struct ip6t_log_info { char prefix[30]; }; -#endif /*_IPT_LOG_H*/ +#endif /* _IP6T_LOG_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 108f8523fa04..57b8e2ffb1dd 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -737,7 +737,8 @@ #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT +#define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 @@ -1103,4 +1104,30 @@ #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 +/* Data Object Exchange */ +#define PCI_DOE_CAP 0x04 /* DOE Capabilities Register */ +#define PCI_DOE_CAP_INT_SUP 0x00000001 /* Interrupt Support */ +#define PCI_DOE_CAP_INT_MSG_NUM 0x00000ffe /* Interrupt Message Number */ +#define PCI_DOE_CTRL 0x08 /* DOE Control Register */ +#define PCI_DOE_CTRL_ABORT 0x00000001 /* DOE Abort */ +#define PCI_DOE_CTRL_INT_EN 0x00000002 /* DOE Interrupt Enable */ +#define PCI_DOE_CTRL_GO 0x80000000 /* DOE Go */ +#define PCI_DOE_STATUS 0x0c /* DOE Status Register */ +#define PCI_DOE_STATUS_BUSY 0x00000001 /* DOE Busy */ +#define PCI_DOE_STATUS_INT_STATUS 0x00000002 /* DOE Interrupt Status */ +#define PCI_DOE_STATUS_ERROR 0x00000004 /* DOE Error */ +#define PCI_DOE_STATUS_DATA_OBJECT_READY 0x80000000 /* Data Object Ready */ +#define PCI_DOE_WRITE 0x10 /* DOE Write Data Mailbox Register */ +#define PCI_DOE_READ 0x14 /* DOE Read Data Mailbox Register */ + +/* DOE Data Object - note not actually registers */ +#define PCI_DOE_DATA_OBJECT_HEADER_1_VID 0x0000ffff +#define PCI_DOE_DATA_OBJECT_HEADER_1_TYPE 0x00ff0000 +#define PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH 0x0003ffff + +#define PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX 0x000000ff +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID 0x0000ffff +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000 +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 + #endif /* LINUX_PCI_REGS_H */ diff --git a/init/Kconfig b/init/Kconfig index 8c9ad53b45dc..80fe60fa77fb 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1411,13 +1411,6 @@ config CC_OPTIMIZE_FOR_PERFORMANCE with the "-O2" compiler flag for best performance and most helpful compile-time warnings. -config CC_OPTIMIZE_FOR_PERFORMANCE_O3 - bool "Optimize more for performance (-O3)" - depends on ARC - help - Choosing this option will pass "-O3" to your compiler to optimize - the kernel yet more for performance. - config CC_OPTIMIZE_FOR_SIZE bool "Optimize for size (-Os)" help @@ -1733,16 +1726,17 @@ config KALLSYMS_ALL help Normally kallsyms only contains the symbols of functions for nicer OOPS messages and backtraces (i.e., symbols from the text and inittext - sections). This is sufficient for most cases. And only in very rare - cases (e.g., when a debugger is used) all symbols are required (e.g., - names of variables from the data sections, etc). + sections). This is sufficient for most cases. And only if you want to + enable kernel live patching, or other less common use cases (e.g., + when a debugger is used) all symbols are required (i.e., names of + variables from the data sections, etc). This option makes sure that all symbols are loaded into the kernel image (i.e., symbols from all sections) in cost of increased kernel size (depending on the kernel configuration, it may be 300KiB or something like this). - Say N unless you really need all symbols. + Say N unless you really need all symbols, or kernel live patching. config KALLSYMS_ABSOLUTE_PERCPU bool @@ -1933,298 +1927,7 @@ config MODULE_SIG_FORMAT def_bool n select SYSTEM_DATA_VERIFICATION -menuconfig MODULES - bool "Enable loadable module support" - modules - help - Kernel modules are small pieces of compiled code which can - be inserted in the running kernel, rather than being - permanently built into the kernel. You use the "modprobe" - tool to add (and sometimes remove) them. If you say Y here, - many parts of the kernel can be built as modules (by - answering M instead of Y where indicated): this is most - useful for infrequently used options which are not required - for booting. For more information, see the man pages for - modprobe, lsmod, modinfo, insmod and rmmod. - - If you say Y here, you will need to run "make - modules_install" to put the modules under /lib/modules/ - where modprobe can find them (you may need to be root to do - this). - - If unsure, say Y. - -if MODULES - -config MODULE_FORCE_LOAD - bool "Forced module loading" - default n - help - Allow loading of modules without version information (ie. modprobe - --force). Forced module loading sets the 'F' (forced) taint flag and - is usually a really bad idea. - -config MODULE_UNLOAD - bool "Module unloading" - help - Without this option you will not be able to unload any - modules (note that some modules may not be unloadable - anyway), which makes your kernel smaller, faster - and simpler. If unsure, say Y. - -config MODULE_FORCE_UNLOAD - bool "Forced module unloading" - depends on MODULE_UNLOAD - help - This option allows you to force a module to unload, even if the - kernel believes it is unsafe: the kernel will remove the module - without waiting for anyone to stop using it (using the -f option to - rmmod). This is mainly for kernel developers and desperate users. - If unsure, say N. - -config MODULE_UNLOAD_TAINT_TRACKING - bool "Tainted module unload tracking" - depends on MODULE_UNLOAD - default n - help - This option allows you to maintain a record of each unloaded - module that tainted the kernel. In addition to displaying a - list of linked (or loaded) modules e.g. on detection of a bad - page (see bad_page()), the aforementioned details are also - shown. If unsure, say N. - -config MODVERSIONS - bool "Module versioning support" - help - Usually, you have to use modules compiled with your kernel. - Saying Y here makes it sometimes possible to use modules - compiled for different kernels, by adding enough information - to the modules to (hopefully) spot any changes which would - make them incompatible with the kernel you are running. If - unsure, say N. - -config ASM_MODVERSIONS - bool - default HAVE_ASM_MODVERSIONS && MODVERSIONS - help - This enables module versioning for exported symbols also from - assembly. This can be enabled only when the target architecture - supports it. - -config MODULE_SRCVERSION_ALL - bool "Source checksum for all modules" - help - Modules which contain a MODULE_VERSION get an extra "srcversion" - field inserted into their modinfo section, which contains a - sum of the source files which made it. This helps maintainers - see exactly which source was used to build a module (since - others sometimes change the module source without updating - the version). With this option, such a "srcversion" field - will be created for all modules. If unsure, say N. - -config MODULE_SIG - bool "Module signature verification" - select MODULE_SIG_FORMAT - help - Check modules for valid signatures upon load: the signature - is simply appended to the module. For more information see - <file:Documentation/admin-guide/module-signing.rst>. - - Note that this option adds the OpenSSL development packages as a - kernel build dependency so that the signing tool can use its crypto - library. - - You should enable this option if you wish to use either - CONFIG_SECURITY_LOCKDOWN_LSM or lockdown functionality imposed via - another LSM - otherwise unsigned modules will be loadable regardless - of the lockdown policy. - - !!!WARNING!!! If you enable this option, you MUST make sure that the - module DOES NOT get stripped after being signed. This includes the - debuginfo strip done by some packagers (such as rpmbuild) and - inclusion into an initramfs that wants the module size reduced. - -config MODULE_SIG_FORCE - bool "Require modules to be validly signed" - depends on MODULE_SIG - help - Reject unsigned modules or signed modules for which we don't have a - key. Without this, such modules will simply taint the kernel. - -config MODULE_SIG_ALL - bool "Automatically sign all modules" - default y - depends on MODULE_SIG || IMA_APPRAISE_MODSIG - help - Sign all modules during make modules_install. Without this option, - modules must be signed manually, using the scripts/sign-file tool. - -comment "Do not forget to sign required modules with scripts/sign-file" - depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL - -choice - prompt "Which hash algorithm should modules be signed with?" - depends on MODULE_SIG || IMA_APPRAISE_MODSIG - help - This determines which sort of hashing algorithm will be used during - signature generation. This algorithm _must_ be built into the kernel - directly so that signature verification can take place. It is not - possible to load a signed module containing the algorithm to check - the signature on that module. - -config MODULE_SIG_SHA1 - bool "Sign modules with SHA-1" - select CRYPTO_SHA1 - -config MODULE_SIG_SHA224 - bool "Sign modules with SHA-224" - select CRYPTO_SHA256 - -config MODULE_SIG_SHA256 - bool "Sign modules with SHA-256" - select CRYPTO_SHA256 - -config MODULE_SIG_SHA384 - bool "Sign modules with SHA-384" - select CRYPTO_SHA512 - -config MODULE_SIG_SHA512 - bool "Sign modules with SHA-512" - select CRYPTO_SHA512 - -endchoice - -config MODULE_SIG_HASH - string - depends on MODULE_SIG || IMA_APPRAISE_MODSIG - default "sha1" if MODULE_SIG_SHA1 - default "sha224" if MODULE_SIG_SHA224 - default "sha256" if MODULE_SIG_SHA256 - default "sha384" if MODULE_SIG_SHA384 - default "sha512" if MODULE_SIG_SHA512 - -choice - prompt "Module compression mode" - help - This option allows you to choose the algorithm which will be used to - compress modules when 'make modules_install' is run. (or, you can - choose to not compress modules at all.) - - External modules will also be compressed in the same way during the - installation. - - For modules inside an initrd or initramfs, it's more efficient to - compress the whole initrd or initramfs instead. - - This is fully compatible with signed modules. - - Please note that the tool used to load modules needs to support the - corresponding algorithm. module-init-tools MAY support gzip, and kmod - MAY support gzip, xz and zstd. - - Your build system needs to provide the appropriate compression tool - to compress the modules. - - If in doubt, select 'None'. - -config MODULE_COMPRESS_NONE - bool "None" - help - Do not compress modules. The installed modules are suffixed - with .ko. - -config MODULE_COMPRESS_GZIP - bool "GZIP" - help - Compress modules with GZIP. The installed modules are suffixed - with .ko.gz. - -config MODULE_COMPRESS_XZ - bool "XZ" - help - Compress modules with XZ. The installed modules are suffixed - with .ko.xz. - -config MODULE_COMPRESS_ZSTD - bool "ZSTD" - help - Compress modules with ZSTD. The installed modules are suffixed - with .ko.zst. - -endchoice - -config MODULE_DECOMPRESS - bool "Support in-kernel module decompression" - depends on MODULE_COMPRESS_GZIP || MODULE_COMPRESS_XZ - select ZLIB_INFLATE if MODULE_COMPRESS_GZIP - select XZ_DEC if MODULE_COMPRESS_XZ - help - - Support for decompressing kernel modules by the kernel itself - instead of relying on userspace to perform this task. Useful when - load pinning security policy is enabled. - - If unsure, say N. - -config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS - bool "Allow loading of modules with missing namespace imports" - help - Symbols exported with EXPORT_SYMBOL_NS*() are considered exported in - a namespace. A module that makes use of a symbol exported with such a - namespace is required to import the namespace via MODULE_IMPORT_NS(). - There is no technical reason to enforce correct namespace imports, - but it creates consistency between symbols defining namespaces and - users importing namespaces they make use of. This option relaxes this - requirement and lifts the enforcement when loading a module. - - If unsure, say N. - -config MODPROBE_PATH - string "Path to modprobe binary" - default "/sbin/modprobe" - help - When kernel code requests a module, it does so by calling - the "modprobe" userspace utility. This option allows you to - set the path where that binary is found. This can be changed - at runtime via the sysctl file - /proc/sys/kernel/modprobe. Setting this to the empty string - removes the kernel's ability to request modules (but - userspace can still load modules explicitly). - -config TRIM_UNUSED_KSYMS - bool "Trim unused exported kernel symbols" if EXPERT - depends on !COMPILE_TEST - help - The kernel and some modules make many symbols available for - other modules to use via EXPORT_SYMBOL() and variants. Depending - on the set of modules being selected in your kernel configuration, - many of those exported symbols might never be used. - - This option allows for unused exported symbols to be dropped from - the build. In turn, this provides the compiler more opportunities - (especially when using LTO) for optimizing the code and reducing - binary size. This might have some security advantages as well. - - If unsure, or if you need to build out-of-tree modules, say N. - -config UNUSED_KSYMS_WHITELIST - string "Whitelist of symbols to keep in ksymtab" - depends on TRIM_UNUSED_KSYMS - help - By default, all unused exported symbols will be un-exported from the - build when TRIM_UNUSED_KSYMS is selected. - - UNUSED_KSYMS_WHITELIST allows to whitelist symbols that must be kept - exported at all times, even in absence of in-tree users. The value to - set here is the path to a text file containing the list of symbols, - one per line. The path can be absolute, or relative to the kernel - source tree. - -endif # MODULES - -config MODULES_TREE_LOOKUP - def_bool y - depends on PERF_EVENTS || TRACING || CFI_CLANG +source "kernel/module/Kconfig" config INIT_ALL_POSSIBLE bool diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index d3e734bf8056..624527401d4d 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -649,6 +649,11 @@ static int bpf_iter_init_array_map(void *priv_data, seq_info->percpu_value_buf = value_buf; } + /* bpf_iter_attach_map() acquires a map uref, and the uref may be + * released before or in the middle of iterating map elements, so + * acquire an extra map uref for iterator. + */ + bpf_map_inc_with_uref(map); seq_info->map = map; return 0; } @@ -657,6 +662,7 @@ static void bpf_iter_fini_array_map(void *priv_data) { struct bpf_iter_seq_array_map_info *seq_info = priv_data; + bpf_map_put_with_uref(seq_info->map); kfree(seq_info->percpu_value_buf); } diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c index 2726a5950cfa..24b755eca0b3 100644 --- a/kernel/bpf/bpf_iter.c +++ b/kernel/bpf/bpf_iter.c @@ -68,13 +68,18 @@ static void bpf_iter_done_stop(struct seq_file *seq) iter_priv->done_stop = true; } +static inline bool bpf_iter_target_support_resched(const struct bpf_iter_target_info *tinfo) +{ + return tinfo->reg_info->feature & BPF_ITER_RESCHED; +} + static bool bpf_iter_support_resched(struct seq_file *seq) { struct bpf_iter_priv_data *iter_priv; iter_priv = container_of(seq->private, struct bpf_iter_priv_data, target_private); - return iter_priv->tinfo->reg_info->feature & BPF_ITER_RESCHED; + return bpf_iter_target_support_resched(iter_priv->tinfo); } /* maximum visited objects before bailing out */ @@ -537,6 +542,10 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, if (!tinfo) return -ENOENT; + /* Only allow sleepable program for resched-able iterator */ + if (prog->aux->sleepable && !bpf_iter_target_support_resched(tinfo)) + return -EINVAL; + link = kzalloc(sizeof(*link), GFP_USER | __GFP_NOWARN); if (!link) return -ENOMEM; diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index da7578426a46..6c530a5e560a 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -311,12 +311,8 @@ static struct htab_elem *prealloc_lru_pop(struct bpf_htab *htab, void *key, struct htab_elem *l; if (node) { - u32 key_size = htab->map.key_size; - l = container_of(node, struct htab_elem, lru_node); - memcpy(l->key, key, key_size); - check_and_init_map_value(&htab->map, - l->key + round_up(key_size, 8)); + memcpy(l->key, key, htab->map.key_size); return l; } @@ -2064,6 +2060,7 @@ static int bpf_iter_init_hash_map(void *priv_data, seq_info->percpu_value_buf = value_buf; } + bpf_map_inc_with_uref(map); seq_info->map = map; seq_info->htab = container_of(map, struct bpf_htab, map); return 0; @@ -2073,6 +2070,7 @@ static void bpf_iter_fini_hash_map(void *priv_data) { struct bpf_iter_seq_hash_map_info *seq_info = priv_data; + bpf_map_put_with_uref(seq_info->map); kfree(seq_info->percpu_value_buf); } diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index e2618fb5870e..85fa9dbfa8bf 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -21,14 +21,11 @@ static struct reuseport_array *reuseport_array(struct bpf_map *map) /* The caller must hold the reuseport_lock */ void bpf_sk_reuseport_detach(struct sock *sk) { - uintptr_t sk_user_data; + struct sock __rcu **socks; write_lock_bh(&sk->sk_callback_lock); - sk_user_data = (uintptr_t)sk->sk_user_data; - if (sk_user_data & SK_USER_DATA_BPF) { - struct sock __rcu **socks; - - socks = (void *)(sk_user_data & SK_USER_DATA_PTRMASK); + socks = __rcu_dereference_sk_user_data_with_flags(sk, SK_USER_DATA_BPF); + if (socks) { WRITE_ONCE(sk->sk_user_data, NULL); /* * Do not move this NULL assignment outside of diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 83c7136c5788..a4d40d98428a 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3886,6 +3886,7 @@ static int bpf_prog_get_info_by_fd(struct file *file, union bpf_attr __user *uattr) { struct bpf_prog_info __user *uinfo = u64_to_user_ptr(attr->info.info); + struct btf *attach_btf = bpf_prog_get_target_btf(prog); struct bpf_prog_info info; u32 info_len = attr->info.info_len; struct bpf_prog_kstats stats; @@ -4088,10 +4089,8 @@ static int bpf_prog_get_info_by_fd(struct file *file, if (prog->aux->btf) info.btf_id = btf_obj_id(prog->aux->btf); info.attach_btf_id = prog->aux->attach_btf_id; - if (prog->aux->attach_btf) - info.attach_btf_obj_id = btf_obj_id(prog->aux->attach_btf); - else if (prog->aux->dst_prog) - info.attach_btf_obj_id = btf_obj_id(prog->aux->dst_prog->aux->attach_btf); + if (attach_btf) + info.attach_btf_obj_id = btf_obj_id(attach_btf); ulen = info.nr_func_info; info.nr_func_info = prog->aux->func_info_cnt; @@ -5072,9 +5071,6 @@ static bool syscall_prog_is_valid_access(int off, int size, BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) { - struct bpf_prog * __maybe_unused prog; - struct bpf_tramp_run_ctx __maybe_unused run_ctx; - switch (cmd) { case BPF_MAP_CREATE: case BPF_MAP_UPDATE_ELEM: @@ -5084,6 +5080,26 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) case BPF_LINK_CREATE: case BPF_RAW_TRACEPOINT_OPEN: break; + default: + return -EINVAL; + } + return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size); +} + + +/* To shut up -Wmissing-prototypes. + * This function is used by the kernel light skeleton + * to load bpf programs when modules are loaded or during kernel boot. + * See tools/lib/bpf/skel_internal.h + */ +int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size); + +int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + struct bpf_prog * __maybe_unused prog; + struct bpf_tramp_run_ctx __maybe_unused run_ctx; + + switch (cmd) { #ifdef CONFIG_BPF_JIT /* __bpf_prog_enter_sleepable used by trampoline and JIT */ case BPF_PROG_TEST_RUN: if (attr->test.data_in || attr->test.data_out || @@ -5114,11 +5130,10 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) return 0; #endif default: - return -EINVAL; + return ____bpf_sys_bpf(cmd, attr, size); } - return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size); } -EXPORT_SYMBOL(bpf_sys_bpf); +EXPORT_SYMBOL(kern_sys_bpf); static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 0f532e6a717f..ff87e38af8a7 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -841,7 +841,10 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) * multiple rcu callbacks. */ hlist_del(&tr->hlist); - kfree(tr->fops); + if (tr->fops) { + ftrace_free_filter(tr->fops); + kfree(tr->fops); + } kfree(tr); out: mutex_unlock(&trampoline_mutex); diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c index 375fb3c9538d..c21abc77c53e 100644 --- a/kernel/dma/coherent.c +++ b/kernel/dma/coherent.c @@ -74,7 +74,7 @@ out_unmap_membase: return ERR_PTR(-ENOMEM); } -static void dma_release_coherent_memory(struct dma_coherent_mem *mem) +static void _dma_release_coherent_memory(struct dma_coherent_mem *mem) { if (!mem) return; @@ -126,10 +126,16 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, ret = dma_assign_coherent_memory(dev, mem); if (ret) - dma_release_coherent_memory(mem); + _dma_release_coherent_memory(mem); return ret; } +void dma_release_coherent_memory(struct device *dev) +{ + if (dev) + _dma_release_coherent_memory(dev->dma_mem); +} + static void *__dma_alloc_from_coherent(struct device *dev, struct dma_coherent_mem *mem, ssize_t size, dma_addr_t *dma_handle) diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig new file mode 100644 index 000000000000..26ea5d04f56c --- /dev/null +++ b/kernel/module/Kconfig @@ -0,0 +1,293 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig MODULES + bool "Enable loadable module support" + modules + help + Kernel modules are small pieces of compiled code which can + be inserted in the running kernel, rather than being + permanently built into the kernel. You use the "modprobe" + tool to add (and sometimes remove) them. If you say Y here, + many parts of the kernel can be built as modules (by + answering M instead of Y where indicated): this is most + useful for infrequently used options which are not required + for booting. For more information, see the man pages for + modprobe, lsmod, modinfo, insmod and rmmod. + + If you say Y here, you will need to run "make + modules_install" to put the modules under /lib/modules/ + where modprobe can find them (you may need to be root to do + this). + + If unsure, say Y. + +if MODULES + +config MODULE_FORCE_LOAD + bool "Forced module loading" + default n + help + Allow loading of modules without version information (ie. modprobe + --force). Forced module loading sets the 'F' (forced) taint flag and + is usually a really bad idea. + +config MODULE_UNLOAD + bool "Module unloading" + help + Without this option you will not be able to unload any + modules (note that some modules may not be unloadable + anyway), which makes your kernel smaller, faster + and simpler. If unsure, say Y. + +config MODULE_FORCE_UNLOAD + bool "Forced module unloading" + depends on MODULE_UNLOAD + help + This option allows you to force a module to unload, even if the + kernel believes it is unsafe: the kernel will remove the module + without waiting for anyone to stop using it (using the -f option to + rmmod). This is mainly for kernel developers and desperate users. + If unsure, say N. + +config MODULE_UNLOAD_TAINT_TRACKING + bool "Tainted module unload tracking" + depends on MODULE_UNLOAD + default n + help + This option allows you to maintain a record of each unloaded + module that tainted the kernel. In addition to displaying a + list of linked (or loaded) modules e.g. on detection of a bad + page (see bad_page()), the aforementioned details are also + shown. If unsure, say N. + +config MODVERSIONS + bool "Module versioning support" + help + Usually, you have to use modules compiled with your kernel. + Saying Y here makes it sometimes possible to use modules + compiled for different kernels, by adding enough information + to the modules to (hopefully) spot any changes which would + make them incompatible with the kernel you are running. If + unsure, say N. + +config ASM_MODVERSIONS + bool + default HAVE_ASM_MODVERSIONS && MODVERSIONS + help + This enables module versioning for exported symbols also from + assembly. This can be enabled only when the target architecture + supports it. + +config MODULE_SRCVERSION_ALL + bool "Source checksum for all modules" + help + Modules which contain a MODULE_VERSION get an extra "srcversion" + field inserted into their modinfo section, which contains a + sum of the source files which made it. This helps maintainers + see exactly which source was used to build a module (since + others sometimes change the module source without updating + the version). With this option, such a "srcversion" field + will be created for all modules. If unsure, say N. + +config MODULE_SIG + bool "Module signature verification" + select MODULE_SIG_FORMAT + help + Check modules for valid signatures upon load: the signature + is simply appended to the module. For more information see + <file:Documentation/admin-guide/module-signing.rst>. + + Note that this option adds the OpenSSL development packages as a + kernel build dependency so that the signing tool can use its crypto + library. + + You should enable this option if you wish to use either + CONFIG_SECURITY_LOCKDOWN_LSM or lockdown functionality imposed via + another LSM - otherwise unsigned modules will be loadable regardless + of the lockdown policy. + + !!!WARNING!!! If you enable this option, you MUST make sure that the + module DOES NOT get stripped after being signed. This includes the + debuginfo strip done by some packagers (such as rpmbuild) and + inclusion into an initramfs that wants the module size reduced. + +config MODULE_SIG_FORCE + bool "Require modules to be validly signed" + depends on MODULE_SIG + help + Reject unsigned modules or signed modules for which we don't have a + key. Without this, such modules will simply taint the kernel. + +config MODULE_SIG_ALL + bool "Automatically sign all modules" + default y + depends on MODULE_SIG || IMA_APPRAISE_MODSIG + help + Sign all modules during make modules_install. Without this option, + modules must be signed manually, using the scripts/sign-file tool. + +comment "Do not forget to sign required modules with scripts/sign-file" + depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL + +choice + prompt "Which hash algorithm should modules be signed with?" + depends on MODULE_SIG || IMA_APPRAISE_MODSIG + help + This determines which sort of hashing algorithm will be used during + signature generation. This algorithm _must_ be built into the kernel + directly so that signature verification can take place. It is not + possible to load a signed module containing the algorithm to check + the signature on that module. + +config MODULE_SIG_SHA1 + bool "Sign modules with SHA-1" + select CRYPTO_SHA1 + +config MODULE_SIG_SHA224 + bool "Sign modules with SHA-224" + select CRYPTO_SHA256 + +config MODULE_SIG_SHA256 + bool "Sign modules with SHA-256" + select CRYPTO_SHA256 + +config MODULE_SIG_SHA384 + bool "Sign modules with SHA-384" + select CRYPTO_SHA512 + +config MODULE_SIG_SHA512 + bool "Sign modules with SHA-512" + select CRYPTO_SHA512 + +endchoice + +config MODULE_SIG_HASH + string + depends on MODULE_SIG || IMA_APPRAISE_MODSIG + default "sha1" if MODULE_SIG_SHA1 + default "sha224" if MODULE_SIG_SHA224 + default "sha256" if MODULE_SIG_SHA256 + default "sha384" if MODULE_SIG_SHA384 + default "sha512" if MODULE_SIG_SHA512 + +choice + prompt "Module compression mode" + help + This option allows you to choose the algorithm which will be used to + compress modules when 'make modules_install' is run. (or, you can + choose to not compress modules at all.) + + External modules will also be compressed in the same way during the + installation. + + For modules inside an initrd or initramfs, it's more efficient to + compress the whole initrd or initramfs instead. + + This is fully compatible with signed modules. + + Please note that the tool used to load modules needs to support the + corresponding algorithm. module-init-tools MAY support gzip, and kmod + MAY support gzip, xz and zstd. + + Your build system needs to provide the appropriate compression tool + to compress the modules. + + If in doubt, select 'None'. + +config MODULE_COMPRESS_NONE + bool "None" + help + Do not compress modules. The installed modules are suffixed + with .ko. + +config MODULE_COMPRESS_GZIP + bool "GZIP" + help + Compress modules with GZIP. The installed modules are suffixed + with .ko.gz. + +config MODULE_COMPRESS_XZ + bool "XZ" + help + Compress modules with XZ. The installed modules are suffixed + with .ko.xz. + +config MODULE_COMPRESS_ZSTD + bool "ZSTD" + help + Compress modules with ZSTD. The installed modules are suffixed + with .ko.zst. + +endchoice + +config MODULE_DECOMPRESS + bool "Support in-kernel module decompression" + depends on MODULE_COMPRESS_GZIP || MODULE_COMPRESS_XZ + select ZLIB_INFLATE if MODULE_COMPRESS_GZIP + select XZ_DEC if MODULE_COMPRESS_XZ + help + + Support for decompressing kernel modules by the kernel itself + instead of relying on userspace to perform this task. Useful when + load pinning security policy is enabled. + + If unsure, say N. + +config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS + bool "Allow loading of modules with missing namespace imports" + help + Symbols exported with EXPORT_SYMBOL_NS*() are considered exported in + a namespace. A module that makes use of a symbol exported with such a + namespace is required to import the namespace via MODULE_IMPORT_NS(). + There is no technical reason to enforce correct namespace imports, + but it creates consistency between symbols defining namespaces and + users importing namespaces they make use of. This option relaxes this + requirement and lifts the enforcement when loading a module. + + If unsure, say N. + +config MODPROBE_PATH + string "Path to modprobe binary" + default "/sbin/modprobe" + help + When kernel code requests a module, it does so by calling + the "modprobe" userspace utility. This option allows you to + set the path where that binary is found. This can be changed + at runtime via the sysctl file + /proc/sys/kernel/modprobe. Setting this to the empty string + removes the kernel's ability to request modules (but + userspace can still load modules explicitly). + +config TRIM_UNUSED_KSYMS + bool "Trim unused exported kernel symbols" if EXPERT + depends on !COMPILE_TEST + help + The kernel and some modules make many symbols available for + other modules to use via EXPORT_SYMBOL() and variants. Depending + on the set of modules being selected in your kernel configuration, + many of those exported symbols might never be used. + + This option allows for unused exported symbols to be dropped from + the build. In turn, this provides the compiler more opportunities + (especially when using LTO) for optimizing the code and reducing + binary size. This might have some security advantages as well. + + If unsure, or if you need to build out-of-tree modules, say N. + +config UNUSED_KSYMS_WHITELIST + string "Whitelist of symbols to keep in ksymtab" + depends on TRIM_UNUSED_KSYMS + help + By default, all unused exported symbols will be un-exported from the + build when TRIM_UNUSED_KSYMS is selected. + + UNUSED_KSYMS_WHITELIST allows to whitelist symbols that must be kept + exported at all times, even in absence of in-tree users. The value to + set here is the path to a text file containing the list of symbols, + one per line. The path can be absolute, or relative to the kernel + source tree. + +config MODULES_TREE_LOOKUP + def_bool y + depends on PERF_EVENTS || TRACING || CFI_CLANG + +endif # MODULES diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c index 2fc7081dd7c1..4d0bcb3d9e44 100644 --- a/kernel/module/decompress.c +++ b/kernel/module/decompress.c @@ -119,10 +119,10 @@ static ssize_t module_gzip_decompress(struct load_info *info, goto out_inflate_end; } - s.next_out = kmap(page); + s.next_out = kmap_local_page(page); s.avail_out = PAGE_SIZE; rc = zlib_inflate(&s, 0); - kunmap(page); + kunmap_local(s.next_out); new_size += PAGE_SIZE - s.avail_out; } while (rc == Z_OK); @@ -178,11 +178,11 @@ static ssize_t module_xz_decompress(struct load_info *info, goto out; } - xz_buf.out = kmap(page); + xz_buf.out = kmap_local_page(page); xz_buf.out_pos = 0; xz_buf.out_size = PAGE_SIZE; xz_ret = xz_dec_run(xz_dec, &xz_buf); - kunmap(page); + kunmap_local(xz_buf.out); new_size += xz_buf.out_pos; } while (xz_buf.out_pos == PAGE_SIZE && xz_ret == XZ_OK); diff --git a/kernel/module/internal.h b/kernel/module/internal.h index ec104c2950c3..680d980a4fb2 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -103,7 +103,7 @@ struct module *find_module_all(const char *name, size_t len, bool even_unformed) int cmp_name(const void *name, const void *sym); long module_get_offset(struct module *mod, unsigned int *size, Elf_Shdr *sechdr, unsigned int section); -char *module_flags(struct module *mod, char *buf); +char *module_flags(struct module *mod, char *buf, bool show_state); size_t module_flags_taint(unsigned long taints, char *buf); static inline void module_assert_mutex_or_preempt(void) diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c index 77e75bead569..f5c5c9175333 100644 --- a/kernel/module/kallsyms.c +++ b/kernel/module/kallsyms.c @@ -457,26 +457,39 @@ unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) return 0; } -/* Look for this name: can be of form module:name. */ -unsigned long module_kallsyms_lookup_name(const char *name) +static unsigned long __module_kallsyms_lookup_name(const char *name) { struct module *mod; char *colon; - unsigned long ret = 0; + + colon = strnchr(name, MODULE_NAME_LEN, ':'); + if (colon) { + mod = find_module_all(name, colon - name, false); + if (mod) + return find_kallsyms_symbol_value(mod, colon + 1); + return 0; + } + + list_for_each_entry_rcu(mod, &modules, list) { + unsigned long ret; + + if (mod->state == MODULE_STATE_UNFORMED) + continue; + ret = find_kallsyms_symbol_value(mod, name); + if (ret) + return ret; + } + return 0; +} + +/* Look for this name: can be of form module:name. */ +unsigned long module_kallsyms_lookup_name(const char *name) +{ + unsigned long ret; /* Don't lock: we're in enough trouble already. */ preempt_disable(); - if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { - if ((mod = find_module_all(name, colon - name, false)) != NULL) - ret = find_kallsyms_symbol_value(mod, colon + 1); - } else { - list_for_each_entry_rcu(mod, &modules, list) { - if (mod->state == MODULE_STATE_UNFORMED) - continue; - if ((ret = find_kallsyms_symbol_value(mod, name)) != 0) - break; - } - } + ret = __module_kallsyms_lookup_name(name); preempt_enable(); return ret; } diff --git a/kernel/module/main.c b/kernel/module/main.c index 57fc2821be63..6a477c622544 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -119,7 +119,7 @@ static void mod_update_bounds(struct module *mod) } /* Block module loading/unloading? */ -int modules_disabled = 0; +int modules_disabled; core_param(nomodule, modules_disabled, bint, 0); /* Waiting for a module to finish initializing? */ @@ -524,7 +524,10 @@ static struct module_attribute modinfo_##field = { \ MODINFO_ATTR(version); MODINFO_ATTR(srcversion); -static char last_unloaded_module[MODULE_NAME_LEN+1]; +static struct { + char name[MODULE_NAME_LEN + 1]; + char taints[MODULE_FLAGS_BUF_SIZE]; +} last_unloaded_module; #ifdef CONFIG_MODULE_UNLOAD @@ -694,6 +697,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, { struct module *mod; char name[MODULE_NAME_LEN]; + char buf[MODULE_FLAGS_BUF_SIZE]; int ret, forced = 0; if (!capable(CAP_SYS_MODULE) || modules_disabled) @@ -753,8 +757,9 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, async_synchronize_full(); - /* Store the name of the last unloaded module for diagnostic purposes */ - strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module)); + /* Store the name and taints of the last unloaded module for diagnostic purposes */ + strscpy(last_unloaded_module.name, mod->name, sizeof(last_unloaded_module.name)); + strscpy(last_unloaded_module.taints, module_flags(mod, buf, false), sizeof(last_unloaded_module.taints)); free_module(mod); /* someone could wait for the module in add_unformed_module() */ @@ -2151,7 +2156,7 @@ static int move_module(struct module *mod, struct load_info *info) #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC /* Do the allocs. */ - ptr = vmalloc(mod->data_layout.size); + ptr = vzalloc(mod->data_layout.size); /* * The pointer to this block is stored in the module structure * which is inside the block. Just mark it as not being a @@ -2164,7 +2169,6 @@ static int move_module(struct module *mod, struct load_info *info) return -ENOMEM; } - memset(ptr, 0, mod->data_layout.size); mod->data_layout.base = ptr; #endif /* Transfer each section which specifies SHF_ALLOC */ @@ -2423,6 +2427,12 @@ static void do_free_init(struct work_struct *w) } } +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "module." +/* Default value for module->async_probe_requested */ +static bool async_probe; +module_param(async_probe, bool, 0644); + /* * This is where the real work happens. * @@ -2643,7 +2653,8 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname, int ret; if (strcmp(param, "async_probe") == 0) { - mod->async_probe_requested = true; + if (strtobool(val, &mod->async_probe_requested)) + mod->async_probe_requested = true; return 0; } @@ -2810,6 +2821,8 @@ static int load_module(struct load_info *info, const char __user *uargs, if (err) goto bug_cleanup; + mod->async_probe_requested = async_probe; + /* Module is ready to execute: parsing args may do that. */ after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, -32768, 32767, mod, @@ -2984,24 +2997,27 @@ static void cfi_cleanup(struct module *mod) } /* Keep in sync with MODULE_FLAGS_BUF_SIZE !!! */ -char *module_flags(struct module *mod, char *buf) +char *module_flags(struct module *mod, char *buf, bool show_state) { int bx = 0; BUG_ON(mod->state == MODULE_STATE_UNFORMED); + if (!mod->taints && !show_state) + goto out; if (mod->taints || mod->state == MODULE_STATE_GOING || mod->state == MODULE_STATE_COMING) { buf[bx++] = '('; bx += module_flags_taint(mod->taints, buf + bx); /* Show a - for module-is-being-unloaded */ - if (mod->state == MODULE_STATE_GOING) + if (mod->state == MODULE_STATE_GOING && show_state) buf[bx++] = '-'; /* Show a + for module-is-being-loaded */ - if (mod->state == MODULE_STATE_COMING) + if (mod->state == MODULE_STATE_COMING && show_state) buf[bx++] = '+'; buf[bx++] = ')'; } +out: buf[bx] = '\0'; return buf; @@ -3134,12 +3150,13 @@ void print_modules(void) list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; - pr_cont(" %s%s", mod->name, module_flags(mod, buf)); + pr_cont(" %s%s", mod->name, module_flags(mod, buf, true)); } print_unloaded_tainted_modules(); preempt_enable(); - if (last_unloaded_module[0]) - pr_cont(" [last unloaded: %s]", last_unloaded_module); + if (last_unloaded_module.name[0]) + pr_cont(" [last unloaded: %s%s]", last_unloaded_module.name, + last_unloaded_module.taints); pr_cont("\n"); } diff --git a/kernel/module/procfs.c b/kernel/module/procfs.c index 9a8f4f0f6329..cf5b9f1e6ec4 100644 --- a/kernel/module/procfs.c +++ b/kernel/module/procfs.c @@ -91,7 +91,7 @@ static int m_show(struct seq_file *m, void *p) /* Taints info */ if (mod->taints) - seq_printf(m, " %s", module_flags(mod, buf)); + seq_printf(m, " %s", module_flags(mod, buf, true)); seq_puts(m, "\n"); return 0; diff --git a/kernel/resource.c b/kernel/resource.c index 34eaee179689..4c5e80b92f2f 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -489,8 +489,9 @@ int __weak page_is_ram(unsigned long pfn) } EXPORT_SYMBOL_GPL(page_is_ram); -static int __region_intersects(resource_size_t start, size_t size, - unsigned long flags, unsigned long desc) +static int __region_intersects(struct resource *parent, resource_size_t start, + size_t size, unsigned long flags, + unsigned long desc) { struct resource res; int type = 0; int other = 0; @@ -499,7 +500,7 @@ static int __region_intersects(resource_size_t start, size_t size, res.start = start; res.end = start + size - 1; - for (p = iomem_resource.child; p ; p = p->sibling) { + for (p = parent->child; p ; p = p->sibling) { bool is_type = (((p->flags & flags) == flags) && ((desc == IORES_DESC_NONE) || (desc == p->desc))); @@ -543,7 +544,7 @@ int region_intersects(resource_size_t start, size_t size, unsigned long flags, int ret; read_lock(&resource_lock); - ret = __region_intersects(start, size, flags, desc); + ret = __region_intersects(&iomem_resource, start, size, flags, desc); read_unlock(&resource_lock); return ret; @@ -891,6 +892,13 @@ void insert_resource_expand_to_fit(struct resource *root, struct resource *new) } write_unlock(&resource_lock); } +/* + * Not for general consumption, only early boot memory map parsing, PCI + * resource discovery, and late discovery of CXL resources are expected + * to use this interface. The former are built-in and only the latter, + * CXL, is a module. + */ +EXPORT_SYMBOL_NS_GPL(insert_resource_expand_to_fit, CXL); /** * remove_resource - Remove a resource in the resource tree @@ -1773,62 +1781,139 @@ void resource_list_free(struct list_head *head) } EXPORT_SYMBOL(resource_list_free); -#ifdef CONFIG_DEVICE_PRIVATE -static struct resource *__request_free_mem_region(struct device *dev, - struct resource *base, unsigned long size, const char *name) +#ifdef CONFIG_GET_FREE_REGION +#define GFR_DESCENDING (1UL << 0) +#define GFR_REQUEST_REGION (1UL << 1) +#define GFR_DEFAULT_ALIGN (1UL << PA_SECTION_SHIFT) + +static resource_size_t gfr_start(struct resource *base, resource_size_t size, + resource_size_t align, unsigned long flags) +{ + if (flags & GFR_DESCENDING) { + resource_size_t end; + + end = min_t(resource_size_t, base->end, + (1ULL << MAX_PHYSMEM_BITS) - 1); + return end - size + 1; + } + + return ALIGN(base->start, align); +} + +static bool gfr_continue(struct resource *base, resource_size_t addr, + resource_size_t size, unsigned long flags) +{ + if (flags & GFR_DESCENDING) + return addr > size && addr >= base->start; + /* + * In the ascend case be careful that the last increment by + * @size did not wrap 0. + */ + return addr > addr - size && + addr <= min_t(resource_size_t, base->end, + (1ULL << MAX_PHYSMEM_BITS) - 1); +} + +static resource_size_t gfr_next(resource_size_t addr, resource_size_t size, + unsigned long flags) { - resource_size_t end, addr; + if (flags & GFR_DESCENDING) + return addr - size; + return addr + size; +} + +static void remove_free_mem_region(void *_res) +{ + struct resource *res = _res; + + if (res->parent) + remove_resource(res); + free_resource(res); +} + +static struct resource * +get_free_mem_region(struct device *dev, struct resource *base, + resource_size_t size, const unsigned long align, + const char *name, const unsigned long desc, + const unsigned long flags) +{ + resource_size_t addr; struct resource *res; struct region_devres *dr = NULL; - size = ALIGN(size, 1UL << PA_SECTION_SHIFT); - end = min_t(unsigned long, base->end, (1UL << MAX_PHYSMEM_BITS) - 1); - addr = end - size + 1UL; + size = ALIGN(size, align); res = alloc_resource(GFP_KERNEL); if (!res) return ERR_PTR(-ENOMEM); - if (dev) { + if (dev && (flags & GFR_REQUEST_REGION)) { dr = devres_alloc(devm_region_release, sizeof(struct region_devres), GFP_KERNEL); if (!dr) { free_resource(res); return ERR_PTR(-ENOMEM); } + } else if (dev) { + if (devm_add_action_or_reset(dev, remove_free_mem_region, res)) + return ERR_PTR(-ENOMEM); } write_lock(&resource_lock); - for (; addr > size && addr >= base->start; addr -= size) { - if (__region_intersects(addr, size, 0, IORES_DESC_NONE) != - REGION_DISJOINT) + for (addr = gfr_start(base, size, align, flags); + gfr_continue(base, addr, size, flags); + addr = gfr_next(addr, size, flags)) { + if (__region_intersects(base, addr, size, 0, IORES_DESC_NONE) != + REGION_DISJOINT) continue; - if (__request_region_locked(res, &iomem_resource, addr, size, - name, 0)) - break; + if (flags & GFR_REQUEST_REGION) { + if (__request_region_locked(res, &iomem_resource, addr, + size, name, 0)) + break; - if (dev) { - dr->parent = &iomem_resource; - dr->start = addr; - dr->n = size; - devres_add(dev, dr); - } + if (dev) { + dr->parent = &iomem_resource; + dr->start = addr; + dr->n = size; + devres_add(dev, dr); + } - res->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY; - write_unlock(&resource_lock); + res->desc = desc; + write_unlock(&resource_lock); + + + /* + * A driver is claiming this region so revoke any + * mappings. + */ + revoke_iomem(res); + } else { + res->start = addr; + res->end = addr + size - 1; + res->name = name; + res->desc = desc; + res->flags = IORESOURCE_MEM; + + /* + * Only succeed if the resource hosts an exclusive + * range after the insert + */ + if (__insert_resource(base, res) || res->child) + break; + + write_unlock(&resource_lock); + } - /* - * A driver is claiming this region so revoke any mappings. - */ - revoke_iomem(res); return res; } write_unlock(&resource_lock); - free_resource(res); - if (dr) + if (flags & GFR_REQUEST_REGION) { + free_resource(res); devres_free(dr); + } else if (dev) + devm_release_action(dev, remove_free_mem_region, res); return ERR_PTR(-ERANGE); } @@ -1847,18 +1932,48 @@ static struct resource *__request_free_mem_region(struct device *dev, struct resource *devm_request_free_mem_region(struct device *dev, struct resource *base, unsigned long size) { - return __request_free_mem_region(dev, base, size, dev_name(dev)); + unsigned long flags = GFR_DESCENDING | GFR_REQUEST_REGION; + + return get_free_mem_region(dev, base, size, GFR_DEFAULT_ALIGN, + dev_name(dev), + IORES_DESC_DEVICE_PRIVATE_MEMORY, flags); } EXPORT_SYMBOL_GPL(devm_request_free_mem_region); struct resource *request_free_mem_region(struct resource *base, unsigned long size, const char *name) { - return __request_free_mem_region(NULL, base, size, name); + unsigned long flags = GFR_DESCENDING | GFR_REQUEST_REGION; + + return get_free_mem_region(NULL, base, size, GFR_DEFAULT_ALIGN, name, + IORES_DESC_DEVICE_PRIVATE_MEMORY, flags); } EXPORT_SYMBOL_GPL(request_free_mem_region); -#endif /* CONFIG_DEVICE_PRIVATE */ +/** + * alloc_free_mem_region - find a free region relative to @base + * @base: resource that will parent the new resource + * @size: size in bytes of memory to allocate from @base + * @align: alignment requirements for the allocation + * @name: resource name + * + * Buses like CXL, that can dynamically instantiate new memory regions, + * need a method to allocate physical address space for those regions. + * Allocate and insert a new resource to cover a free, unclaimed by a + * descendant of @base, range in the span of @base. + */ +struct resource *alloc_free_mem_region(struct resource *base, + unsigned long size, unsigned long align, + const char *name) +{ + /* Default of ascending direction and insert resource */ + unsigned long flags = 0; + + return get_free_mem_region(NULL, base, size, align, name, + IORES_DESC_NONE, flags); +} +EXPORT_SYMBOL_NS_GPL(alloc_free_mem_region, CXL); +#endif /* CONFIG_GET_FREE_REGION */ static int __init strict_iomem(char *str) { diff --git a/kernel/sysctl.c b/kernel/sysctl.c index b233714a1c78..205d605cacc5 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -492,12 +492,12 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, int *i, vleft, first = 1, err = 0; size_t left; char *p; - + if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { *lenp = 0; return 0; } - + i = (int *) tbl_data; vleft = table->maxlen / sizeof(*i); left = *lenp; @@ -729,7 +729,7 @@ int proc_dobool(struct ctl_table *table, int write, void *buffer, * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string. + * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */ @@ -1273,7 +1273,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string. + * values from/to the user buffer, treated as an ASCII string. * The values read are assumed to be in seconds, and are converted into * jiffies. * @@ -1306,8 +1306,8 @@ int proc_dointvec_ms_jiffies_minmax(struct ctl_table *table, int write, * @ppos: pointer to the file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string. - * The values read are assumed to be in 1/USER_HZ seconds, and + * values from/to the user buffer, treated as an ASCII string. + * The values read are assumed to be in 1/USER_HZ seconds, and * are converted into jiffies. * * Returns 0 on success. @@ -1315,8 +1315,8 @@ int proc_dointvec_ms_jiffies_minmax(struct ctl_table *table, int write, int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_dointvec(table,write,buffer,lenp,ppos, - do_proc_dointvec_userhz_jiffies_conv,NULL); + return do_proc_dointvec(table, write, buffer, lenp, ppos, + do_proc_dointvec_userhz_jiffies_conv, NULL); } /** @@ -2061,7 +2061,7 @@ static struct ctl_table kern_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, -#if defined(CONFIG_TREE_RCU) +#ifdef CONFIG_TREE_RCU { .procname = "panic_on_rcu_stall", .data = &sysctl_panic_on_rcu_stall, @@ -2071,8 +2071,6 @@ static struct ctl_table kern_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, -#endif -#if defined(CONFIG_TREE_RCU) { .procname = "max_rcu_stall_to_panic", .data = &sysctl_max_rcu_stall_to_panic, diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 0e0be334dbee..4b7fce72e3e5 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -16,6 +16,16 @@ #define PIPE_PARANOIA /* for now */ +/* covers ubuf and kbuf alike */ +#define iterate_buf(i, n, base, len, off, __p, STEP) { \ + size_t __maybe_unused off = 0; \ + len = n; \ + base = __p + i->iov_offset; \ + len -= (STEP); \ + i->iov_offset += len; \ + n = len; \ +} + /* covers iovec and kvec alike */ #define iterate_iovec(i, n, base, len, off, __p, STEP) { \ size_t off = 0; \ @@ -110,7 +120,12 @@ __out: \ if (unlikely(i->count < n)) \ n = i->count; \ if (likely(n)) { \ - if (likely(iter_is_iovec(i))) { \ + if (likely(iter_is_ubuf(i))) { \ + void __user *base; \ + size_t len; \ + iterate_buf(i, n, base, len, off, \ + i->ubuf, (I)) \ + } else if (likely(iter_is_iovec(i))) { \ const struct iovec *iov = i->iov; \ void __user *base; \ size_t len; \ @@ -168,26 +183,31 @@ static int copyin(void *to, const void __user *from, size_t n) return n; } +static inline struct pipe_buffer *pipe_buf(const struct pipe_inode_info *pipe, + unsigned int slot) +{ + return &pipe->bufs[slot & (pipe->ring_size - 1)]; +} + #ifdef PIPE_PARANOIA static bool sanity(const struct iov_iter *i) { struct pipe_inode_info *pipe = i->pipe; unsigned int p_head = pipe->head; unsigned int p_tail = pipe->tail; - unsigned int p_mask = pipe->ring_size - 1; unsigned int p_occupancy = pipe_occupancy(p_head, p_tail); unsigned int i_head = i->head; unsigned int idx; - if (i->iov_offset) { + if (i->last_offset) { struct pipe_buffer *p; if (unlikely(p_occupancy == 0)) goto Bad; // pipe must be non-empty if (unlikely(i_head != p_head - 1)) goto Bad; // must be at the last buffer... - p = &pipe->bufs[i_head & p_mask]; - if (unlikely(p->offset + p->len != i->iov_offset)) + p = pipe_buf(pipe, i_head); + if (unlikely(p->offset + p->len != abs(i->last_offset))) goto Bad; // ... at the end of segment } else { if (i_head != p_head) @@ -195,7 +215,7 @@ static bool sanity(const struct iov_iter *i) } return true; Bad: - printk(KERN_ERR "idx = %d, offset = %zd\n", i_head, i->iov_offset); + printk(KERN_ERR "idx = %d, offset = %d\n", i_head, i->last_offset); printk(KERN_ERR "head = %d, tail = %d, buffers = %d\n", p_head, p_tail, pipe->ring_size); for (idx = 0; idx < pipe->ring_size; idx++) @@ -211,15 +231,79 @@ Bad: #define sanity(i) true #endif +static struct page *push_anon(struct pipe_inode_info *pipe, unsigned size) +{ + struct page *page = alloc_page(GFP_USER); + if (page) { + struct pipe_buffer *buf = pipe_buf(pipe, pipe->head++); + *buf = (struct pipe_buffer) { + .ops = &default_pipe_buf_ops, + .page = page, + .offset = 0, + .len = size + }; + } + return page; +} + +static void push_page(struct pipe_inode_info *pipe, struct page *page, + unsigned int offset, unsigned int size) +{ + struct pipe_buffer *buf = pipe_buf(pipe, pipe->head++); + *buf = (struct pipe_buffer) { + .ops = &page_cache_pipe_buf_ops, + .page = page, + .offset = offset, + .len = size + }; + get_page(page); +} + +static inline int last_offset(const struct pipe_buffer *buf) +{ + if (buf->ops == &default_pipe_buf_ops) + return buf->len; // buf->offset is 0 for those + else + return -(buf->offset + buf->len); +} + +static struct page *append_pipe(struct iov_iter *i, size_t size, + unsigned int *off) +{ + struct pipe_inode_info *pipe = i->pipe; + int offset = i->last_offset; + struct pipe_buffer *buf; + struct page *page; + + if (offset > 0 && offset < PAGE_SIZE) { + // some space in the last buffer; add to it + buf = pipe_buf(pipe, pipe->head - 1); + size = min_t(size_t, size, PAGE_SIZE - offset); + buf->len += size; + i->last_offset += size; + i->count -= size; + *off = offset; + return buf->page; + } + // OK, we need a new buffer + *off = 0; + size = min_t(size_t, size, PAGE_SIZE); + if (pipe_full(pipe->head, pipe->tail, pipe->max_usage)) + return NULL; + page = push_anon(pipe, size); + if (!page) + return NULL; + i->head = pipe->head - 1; + i->last_offset = size; + i->count -= size; + return page; +} + static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { struct pipe_inode_info *pipe = i->pipe; - struct pipe_buffer *buf; - unsigned int p_tail = pipe->tail; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head = i->head; - size_t off; + unsigned int head = pipe->head; if (unlikely(bytes > i->count)) bytes = i->count; @@ -230,32 +314,21 @@ static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t by if (!sanity(i)) return 0; - off = i->iov_offset; - buf = &pipe->bufs[i_head & p_mask]; - if (off) { - if (offset == off && buf->page == page) { - /* merge with the last one */ + if (offset && i->last_offset == -offset) { // could we merge it? + struct pipe_buffer *buf = pipe_buf(pipe, head - 1); + if (buf->page == page) { buf->len += bytes; - i->iov_offset += bytes; - goto out; + i->last_offset -= bytes; + i->count -= bytes; + return bytes; } - i_head++; - buf = &pipe->bufs[i_head & p_mask]; } - if (pipe_full(i_head, p_tail, pipe->max_usage)) + if (pipe_full(pipe->head, pipe->tail, pipe->max_usage)) return 0; - buf->ops = &page_cache_pipe_buf_ops; - buf->flags = 0; - get_page(page); - buf->page = page; - buf->offset = offset; - buf->len = bytes; - - pipe->head = i_head + 1; - i->iov_offset = offset + bytes; - i->head = i_head; -out: + push_page(pipe, page, offset, bytes); + i->last_offset = -(offset + bytes); + i->head = head; i->count -= bytes; return bytes; } @@ -275,7 +348,11 @@ out: */ size_t fault_in_iov_iter_readable(const struct iov_iter *i, size_t size) { - if (iter_is_iovec(i)) { + if (iter_is_ubuf(i)) { + size_t n = min(size, iov_iter_count(i)); + n -= fault_in_readable(i->ubuf + i->iov_offset, n); + return size - n; + } else if (iter_is_iovec(i)) { size_t count = min(size, iov_iter_count(i)); const struct iovec *p; size_t skip; @@ -314,7 +391,11 @@ EXPORT_SYMBOL(fault_in_iov_iter_readable); */ size_t fault_in_iov_iter_writeable(const struct iov_iter *i, size_t size) { - if (iter_is_iovec(i)) { + if (iter_is_ubuf(i)) { + size_t n = min(size, iov_iter_count(i)); + n -= fault_in_safe_writeable(i->ubuf + i->iov_offset, n); + return size - n; + } else if (iter_is_iovec(i)) { size_t count = min(size, iov_iter_count(i)); const struct iovec *p; size_t skip; @@ -345,6 +426,7 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction, *i = (struct iov_iter) { .iter_type = ITER_IOVEC, .nofault = false, + .user_backed = true, .data_source = direction, .iov = iov, .nr_segs = nr_segs, @@ -354,101 +436,43 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction, } EXPORT_SYMBOL(iov_iter_init); -static inline bool allocated(struct pipe_buffer *buf) -{ - return buf->ops == &default_pipe_buf_ops; -} - -static inline void data_start(const struct iov_iter *i, - unsigned int *iter_headp, size_t *offp) -{ - unsigned int p_mask = i->pipe->ring_size - 1; - unsigned int iter_head = i->head; - size_t off = i->iov_offset; - - if (off && (!allocated(&i->pipe->bufs[iter_head & p_mask]) || - off == PAGE_SIZE)) { - iter_head++; - off = 0; - } - *iter_headp = iter_head; - *offp = off; -} - -static size_t push_pipe(struct iov_iter *i, size_t size, - int *iter_headp, size_t *offp) +// returns the offset in partial buffer (if any) +static inline unsigned int pipe_npages(const struct iov_iter *i, int *npages) { struct pipe_inode_info *pipe = i->pipe; - unsigned int p_tail = pipe->tail; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int iter_head; - size_t off; - ssize_t left; + int used = pipe->head - pipe->tail; + int off = i->last_offset; - if (unlikely(size > i->count)) - size = i->count; - if (unlikely(!size)) - return 0; + *npages = max((int)pipe->max_usage - used, 0); - left = size; - data_start(i, &iter_head, &off); - *iter_headp = iter_head; - *offp = off; - if (off) { - left -= PAGE_SIZE - off; - if (left <= 0) { - pipe->bufs[iter_head & p_mask].len += size; - return size; - } - pipe->bufs[iter_head & p_mask].len = PAGE_SIZE; - iter_head++; + if (off > 0 && off < PAGE_SIZE) { // anon and not full + (*npages)++; + return off; } - while (!pipe_full(iter_head, p_tail, pipe->max_usage)) { - struct pipe_buffer *buf = &pipe->bufs[iter_head & p_mask]; - struct page *page = alloc_page(GFP_USER); - if (!page) - break; - - buf->ops = &default_pipe_buf_ops; - buf->flags = 0; - buf->page = page; - buf->offset = 0; - buf->len = min_t(ssize_t, left, PAGE_SIZE); - left -= buf->len; - iter_head++; - pipe->head = iter_head; - - if (left == 0) - return size; - } - return size - left; + return 0; } static size_t copy_pipe_to_iter(const void *addr, size_t bytes, struct iov_iter *i) { - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head; - size_t n, off; + unsigned int off, chunk; - if (!sanity(i)) + if (unlikely(bytes > i->count)) + bytes = i->count; + if (unlikely(!bytes)) return 0; - bytes = n = push_pipe(i, bytes, &i_head, &off); - if (unlikely(!n)) + if (!sanity(i)) return 0; - do { - size_t chunk = min_t(size_t, n, PAGE_SIZE - off); - memcpy_to_page(pipe->bufs[i_head & p_mask].page, off, addr, chunk); - i->head = i_head; - i->iov_offset = off + chunk; - n -= chunk; + + for (size_t n = bytes; n; n -= chunk) { + struct page *page = append_pipe(i, n, &off); + chunk = min_t(size_t, n, PAGE_SIZE - off); + if (!page) + return bytes - n; + memcpy_to_page(page, off, addr, chunk); addr += chunk; - off = 0; - i_head++; - } while (n); - i->count -= bytes; + } return bytes; } @@ -462,31 +486,32 @@ static __wsum csum_and_memcpy(void *to, const void *from, size_t len, static size_t csum_and_copy_to_pipe_iter(const void *addr, size_t bytes, struct iov_iter *i, __wsum *sump) { - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; __wsum sum = *sump; size_t off = 0; - unsigned int i_head; - size_t r; + unsigned int chunk, r; + + if (unlikely(bytes > i->count)) + bytes = i->count; + if (unlikely(!bytes)) + return 0; if (!sanity(i)) return 0; - bytes = push_pipe(i, bytes, &i_head, &r); while (bytes) { - size_t chunk = min_t(size_t, bytes, PAGE_SIZE - r); - char *p = kmap_local_page(pipe->bufs[i_head & p_mask].page); + struct page *page = append_pipe(i, bytes, &r); + char *p; + + if (!page) + break; + chunk = min_t(size_t, bytes, PAGE_SIZE - r); + p = kmap_local_page(page); sum = csum_and_memcpy(p + r, addr + off, chunk, sum, off); kunmap_local(p); - i->head = i_head; - i->iov_offset = r + chunk; - bytes -= chunk; off += chunk; - r = 0; - i_head++; + bytes -= chunk; } *sump = sum; - i->count -= off; return off; } @@ -494,7 +519,7 @@ size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) { if (unlikely(iov_iter_is_pipe(i))) return copy_pipe_to_iter(addr, bytes, i); - if (iter_is_iovec(i)) + if (user_backed_iter(i)) might_fault(); iterate_and_advance(i, bytes, base, len, off, copyout(base, addr + off, len), @@ -518,39 +543,36 @@ static int copyout_mc(void __user *to, const void *from, size_t n) static size_t copy_mc_pipe_to_iter(const void *addr, size_t bytes, struct iov_iter *i) { - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head; - unsigned int valid = pipe->head; - size_t n, off, xfer = 0; + size_t xfer = 0; + unsigned int off, chunk; + + if (unlikely(bytes > i->count)) + bytes = i->count; + if (unlikely(!bytes)) + return 0; if (!sanity(i)) return 0; - n = push_pipe(i, bytes, &i_head, &off); - while (n) { - size_t chunk = min_t(size_t, n, PAGE_SIZE - off); - char *p = kmap_local_page(pipe->bufs[i_head & p_mask].page); + while (bytes) { + struct page *page = append_pipe(i, bytes, &off); unsigned long rem; + char *p; + + if (!page) + break; + chunk = min_t(size_t, bytes, PAGE_SIZE - off); + p = kmap_local_page(page); rem = copy_mc_to_kernel(p + off, addr + xfer, chunk); chunk -= rem; kunmap_local(p); - if (chunk) { - i->head = i_head; - i->iov_offset = off + chunk; - xfer += chunk; - valid = i_head + 1; - } + xfer += chunk; + bytes -= chunk; if (rem) { - pipe->bufs[i_head & p_mask].len -= rem; - pipe_discard_from(pipe, valid); + iov_iter_revert(i, rem); break; } - n -= chunk; - off = 0; - i_head++; } - i->count -= xfer; return xfer; } @@ -583,7 +605,7 @@ size_t _copy_mc_to_iter(const void *addr, size_t bytes, struct iov_iter *i) { if (unlikely(iov_iter_is_pipe(i))) return copy_mc_pipe_to_iter(addr, bytes, i); - if (iter_is_iovec(i)) + if (user_backed_iter(i)) might_fault(); __iterate_and_advance(i, bytes, base, len, off, copyout_mc(base, addr + off, len), @@ -601,7 +623,7 @@ size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) WARN_ON(1); return 0; } - if (iter_is_iovec(i)) + if (user_backed_iter(i)) might_fault(); iterate_and_advance(i, bytes, base, len, off, copyin(addr + off, base, len), @@ -684,30 +706,21 @@ static inline bool page_copy_sane(struct page *page, size_t offset, size_t n) return false; } -static size_t __copy_page_to_iter(struct page *page, size_t offset, size_t bytes, - struct iov_iter *i) -{ - if (unlikely(iov_iter_is_pipe(i))) { - return copy_page_to_iter_pipe(page, offset, bytes, i); - } else { - void *kaddr = kmap_local_page(page); - size_t wanted = _copy_to_iter(kaddr + offset, bytes, i); - kunmap_local(kaddr); - return wanted; - } -} - size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { size_t res = 0; if (unlikely(!page_copy_sane(page, offset, bytes))) return 0; + if (unlikely(iov_iter_is_pipe(i))) + return copy_page_to_iter_pipe(page, offset, bytes, i); page += offset / PAGE_SIZE; // first subpage offset %= PAGE_SIZE; while (1) { - size_t n = __copy_page_to_iter(page, offset, - min(bytes, (size_t)PAGE_SIZE - offset), i); + void *kaddr = kmap_local_page(page); + size_t n = min(bytes, (size_t)PAGE_SIZE - offset); + n = _copy_to_iter(kaddr + offset, n, i); + kunmap_local(kaddr); res += n; bytes -= n; if (!bytes || !n) @@ -725,42 +738,53 @@ EXPORT_SYMBOL(copy_page_to_iter); size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { - if (page_copy_sane(page, offset, bytes)) { + size_t res = 0; + if (!page_copy_sane(page, offset, bytes)) + return 0; + page += offset / PAGE_SIZE; // first subpage + offset %= PAGE_SIZE; + while (1) { void *kaddr = kmap_local_page(page); - size_t wanted = _copy_from_iter(kaddr + offset, bytes, i); + size_t n = min(bytes, (size_t)PAGE_SIZE - offset); + n = _copy_from_iter(kaddr + offset, n, i); kunmap_local(kaddr); - return wanted; + res += n; + bytes -= n; + if (!bytes || !n) + break; + offset += n; + if (offset == PAGE_SIZE) { + page++; + offset = 0; + } } - return 0; + return res; } EXPORT_SYMBOL(copy_page_from_iter); static size_t pipe_zero(size_t bytes, struct iov_iter *i) { - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head; - size_t n, off; + unsigned int chunk, off; - if (!sanity(i)) + if (unlikely(bytes > i->count)) + bytes = i->count; + if (unlikely(!bytes)) return 0; - bytes = n = push_pipe(i, bytes, &i_head, &off); - if (unlikely(!n)) + if (!sanity(i)) return 0; - do { - size_t chunk = min_t(size_t, n, PAGE_SIZE - off); - char *p = kmap_local_page(pipe->bufs[i_head & p_mask].page); + for (size_t n = bytes; n; n -= chunk) { + struct page *page = append_pipe(i, n, &off); + char *p; + + if (!page) + return bytes - n; + chunk = min_t(size_t, n, PAGE_SIZE - off); + p = kmap_local_page(page); memset(p + off, 0, chunk); kunmap_local(p); - i->head = i_head; - i->iov_offset = off + chunk; - n -= chunk; - off = 0; - i_head++; - } while (n); - i->count -= bytes; + } return bytes; } @@ -799,56 +823,30 @@ size_t copy_page_from_iter_atomic(struct page *page, unsigned offset, size_t byt } EXPORT_SYMBOL(copy_page_from_iter_atomic); -static inline void pipe_truncate(struct iov_iter *i) -{ - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_tail = pipe->tail; - unsigned int p_head = pipe->head; - unsigned int p_mask = pipe->ring_size - 1; - - if (!pipe_empty(p_head, p_tail)) { - struct pipe_buffer *buf; - unsigned int i_head = i->head; - size_t off = i->iov_offset; - - if (off) { - buf = &pipe->bufs[i_head & p_mask]; - buf->len = off - buf->offset; - i_head++; - } - while (p_head != i_head) { - p_head--; - pipe_buf_release(pipe, &pipe->bufs[p_head & p_mask]); - } - - pipe->head = p_head; - } -} - static void pipe_advance(struct iov_iter *i, size_t size) { struct pipe_inode_info *pipe = i->pipe; - if (size) { - struct pipe_buffer *buf; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head = i->head; - size_t off = i->iov_offset, left = size; + int off = i->last_offset; + if (!off && !size) { + pipe_discard_from(pipe, i->start_head); // discard everything + return; + } + i->count -= size; + while (1) { + struct pipe_buffer *buf = pipe_buf(pipe, i->head); if (off) /* make it relative to the beginning of buffer */ - left += off - pipe->bufs[i_head & p_mask].offset; - while (1) { - buf = &pipe->bufs[i_head & p_mask]; - if (left <= buf->len) - break; - left -= buf->len; - i_head++; + size += abs(off) - buf->offset; + if (size <= buf->len) { + buf->len = size; + i->last_offset = last_offset(buf); + break; } - i->head = i_head; - i->iov_offset = buf->offset + left; + size -= buf->len; + i->head++; + off = 0; } - i->count -= size; - /* ... and discard everything past that point */ - pipe_truncate(i); + pipe_discard_from(pipe, i->head + 1); // discard everything past this one } static void iov_iter_bvec_advance(struct iov_iter *i, size_t size) @@ -894,16 +892,16 @@ void iov_iter_advance(struct iov_iter *i, size_t size) { if (unlikely(i->count < size)) size = i->count; - if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i))) { + if (likely(iter_is_ubuf(i)) || unlikely(iov_iter_is_xarray(i))) { + i->iov_offset += size; + i->count -= size; + } else if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i))) { /* iovec and kvec have identical layouts */ iov_iter_iovec_advance(i, size); } else if (iov_iter_is_bvec(i)) { iov_iter_bvec_advance(i, size); } else if (iov_iter_is_pipe(i)) { pipe_advance(i, size); - } else if (unlikely(iov_iter_is_xarray(i))) { - i->iov_offset += size; - i->count -= size; } else if (iov_iter_is_discard(i)) { i->count -= size; } @@ -919,28 +917,22 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll) i->count += unroll; if (unlikely(iov_iter_is_pipe(i))) { struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; - unsigned int i_head = i->head; - size_t off = i->iov_offset; - while (1) { - struct pipe_buffer *b = &pipe->bufs[i_head & p_mask]; - size_t n = off - b->offset; - if (unroll < n) { - off -= unroll; - break; - } - unroll -= n; - if (!unroll && i_head == i->start_head) { - off = 0; - break; + unsigned int head = pipe->head; + + while (head > i->start_head) { + struct pipe_buffer *b = pipe_buf(pipe, --head); + if (unroll < b->len) { + b->len -= unroll; + i->last_offset = last_offset(b); + i->head = head; + return; } - i_head--; - b = &pipe->bufs[i_head & p_mask]; - off = b->offset + b->len; + unroll -= b->len; + pipe_buf_release(pipe, b); + pipe->head--; } - i->iov_offset = off; - i->head = i_head; - pipe_truncate(i); + i->last_offset = 0; + i->head = head; return; } if (unlikely(iov_iter_is_discard(i))) @@ -950,7 +942,7 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll) return; } unroll -= i->iov_offset; - if (iov_iter_is_xarray(i)) { + if (iov_iter_is_xarray(i) || iter_is_ubuf(i)) { BUG(); /* We should never go beyond the start of the specified * range since we might then be straying into pages that * aren't pinned. @@ -1042,7 +1034,7 @@ void iov_iter_pipe(struct iov_iter *i, unsigned int direction, .pipe = pipe, .head = pipe->head, .start_head = pipe->head, - .iov_offset = 0, + .last_offset = 0, .count = count }; } @@ -1158,6 +1150,14 @@ static bool iov_iter_aligned_bvec(const struct iov_iter *i, unsigned addr_mask, bool iov_iter_is_aligned(const struct iov_iter *i, unsigned addr_mask, unsigned len_mask) { + if (likely(iter_is_ubuf(i))) { + if (i->count & len_mask) + return false; + if ((unsigned long)(i->ubuf + i->iov_offset) & addr_mask) + return false; + return true; + } + if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i))) return iov_iter_aligned_iovec(i, addr_mask, len_mask); @@ -1165,13 +1165,12 @@ bool iov_iter_is_aligned(const struct iov_iter *i, unsigned addr_mask, return iov_iter_aligned_bvec(i, addr_mask, len_mask); if (iov_iter_is_pipe(i)) { - unsigned int p_mask = i->pipe->ring_size - 1; size_t size = i->count; if (size & len_mask) return false; - if (size && allocated(&i->pipe->bufs[i->head & p_mask])) { - if (i->iov_offset & addr_mask) + if (size && i->last_offset > 0) { + if (i->last_offset & addr_mask) return false; } @@ -1233,6 +1232,13 @@ static unsigned long iov_iter_alignment_bvec(const struct iov_iter *i) unsigned long iov_iter_alignment(const struct iov_iter *i) { + if (likely(iter_is_ubuf(i))) { + size_t size = i->count; + if (size) + return ((unsigned long)i->ubuf + i->iov_offset) | size; + return 0; + } + /* iovec and kvec have identical layouts */ if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i))) return iov_iter_alignment_iovec(i); @@ -1241,11 +1247,10 @@ unsigned long iov_iter_alignment(const struct iov_iter *i) return iov_iter_alignment_bvec(i); if (iov_iter_is_pipe(i)) { - unsigned int p_mask = i->pipe->ring_size - 1; size_t size = i->count; - if (size && i->iov_offset && allocated(&i->pipe->bufs[i->head & p_mask])) - return size | i->iov_offset; + if (size && i->last_offset > 0) + return size | i->last_offset; return size; } @@ -1263,6 +1268,9 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i) size_t size = i->count; unsigned k; + if (iter_is_ubuf(i)) + return 0; + if (WARN_ON(!iter_is_iovec(i))) return ~0U; @@ -1281,45 +1289,50 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i) } EXPORT_SYMBOL(iov_iter_gap_alignment); -static inline ssize_t __pipe_get_pages(struct iov_iter *i, - size_t maxsize, - struct page **pages, - int iter_head, - size_t *start) +static int want_pages_array(struct page ***res, size_t size, + size_t start, unsigned int maxpages) { - struct pipe_inode_info *pipe = i->pipe; - unsigned int p_mask = pipe->ring_size - 1; - ssize_t n = push_pipe(i, maxsize, &iter_head, start); - if (!n) - return -EFAULT; + unsigned int count = DIV_ROUND_UP(size + start, PAGE_SIZE); - maxsize = n; - n += *start; - while (n > 0) { - get_page(*pages++ = pipe->bufs[iter_head & p_mask].page); - iter_head++; - n -= PAGE_SIZE; + if (count > maxpages) + count = maxpages; + WARN_ON(!count); // caller should've prevented that + if (!*res) { + *res = kvmalloc_array(count, sizeof(struct page *), GFP_KERNEL); + if (!*res) + return 0; } - - return maxsize; + return count; } static ssize_t pipe_get_pages(struct iov_iter *i, - struct page **pages, size_t maxsize, unsigned maxpages, + struct page ***pages, size_t maxsize, unsigned maxpages, size_t *start) { - unsigned int iter_head, npages; - size_t capacity; + unsigned int npages, count, off, chunk; + struct page **p; + size_t left; if (!sanity(i)) return -EFAULT; - data_start(i, &iter_head, start); - /* Amount of free space: some of this one + all after this one */ - npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe); - capacity = min(npages, maxpages) * PAGE_SIZE - *start; - - return __pipe_get_pages(i, min(maxsize, capacity), pages, iter_head, start); + *start = off = pipe_npages(i, &npages); + if (!npages) + return -EFAULT; + count = want_pages_array(pages, maxsize, off, min(npages, maxpages)); + if (!count) + return -ENOMEM; + p = *pages; + for (npages = 0, left = maxsize ; npages < count; npages++, left -= chunk) { + struct page *page = append_pipe(i, left, &off); + if (!page) + break; + chunk = min_t(size_t, left, PAGE_SIZE - off); + get_page(*p++ = page); + } + if (!npages) + return -EFAULT; + return maxsize - left; } static ssize_t iter_xarray_populate_pages(struct page **pages, struct xarray *xa, @@ -1350,47 +1363,40 @@ static ssize_t iter_xarray_populate_pages(struct page **pages, struct xarray *xa } static ssize_t iter_xarray_get_pages(struct iov_iter *i, - struct page **pages, size_t maxsize, + struct page ***pages, size_t maxsize, unsigned maxpages, size_t *_start_offset) { - unsigned nr, offset; - pgoff_t index, count; - size_t size = maxsize; + unsigned nr, offset, count; + pgoff_t index; loff_t pos; - if (!size || !maxpages) - return 0; - pos = i->xarray_start + i->iov_offset; index = pos >> PAGE_SHIFT; offset = pos & ~PAGE_MASK; *_start_offset = offset; - count = 1; - if (size > PAGE_SIZE - offset) { - size -= PAGE_SIZE - offset; - count += size >> PAGE_SHIFT; - size &= ~PAGE_MASK; - if (size) - count++; - } - - if (count > maxpages) - count = maxpages; - - nr = iter_xarray_populate_pages(pages, i->xarray, index, count); + count = want_pages_array(pages, maxsize, offset, maxpages); + if (!count) + return -ENOMEM; + nr = iter_xarray_populate_pages(*pages, i->xarray, index, count); if (nr == 0) return 0; - return min_t(size_t, nr * PAGE_SIZE - offset, maxsize); + maxsize = min_t(size_t, nr * PAGE_SIZE - offset, maxsize); + i->iov_offset += maxsize; + i->count -= maxsize; + return maxsize; } -/* must be done on non-empty ITER_IOVEC one */ +/* must be done on non-empty ITER_UBUF or ITER_IOVEC one */ static unsigned long first_iovec_segment(const struct iov_iter *i, size_t *size) { size_t skip; long k; + if (iter_is_ubuf(i)) + return (unsigned long)i->ubuf + i->iov_offset; + for (k = 0, skip = i->iov_offset; k < i->nr_segs; k++, skip = 0) { size_t len = i->iov[k].iov_len - skip; @@ -1419,11 +1425,11 @@ static struct page *first_bvec_segment(const struct iov_iter *i, return page; } -ssize_t iov_iter_get_pages(struct iov_iter *i, - struct page **pages, size_t maxsize, unsigned maxpages, - size_t *start) +static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i, + struct page ***pages, size_t maxsize, + unsigned int maxpages, size_t *start) { - int n, res; + unsigned int n; if (maxsize > i->count) maxsize = i->count; @@ -1432,9 +1438,10 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, if (maxsize > MAX_RW_COUNT) maxsize = MAX_RW_COUNT; - if (likely(iter_is_iovec(i))) { + if (likely(user_backed_iter(i))) { unsigned int gup_flags = 0; unsigned long addr; + int res; if (iov_iter_rw(i) != WRITE) gup_flags |= FOLL_WRITE; @@ -1444,24 +1451,36 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, addr = first_iovec_segment(i, &maxsize); *start = addr % PAGE_SIZE; addr &= PAGE_MASK; - n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE); - if (n > maxpages) - n = maxpages; - res = get_user_pages_fast(addr, n, gup_flags, pages); + n = want_pages_array(pages, maxsize, *start, maxpages); + if (!n) + return -ENOMEM; + res = get_user_pages_fast(addr, n, gup_flags, *pages); if (unlikely(res <= 0)) return res; - return min_t(size_t, maxsize, res * PAGE_SIZE - *start); + maxsize = min_t(size_t, maxsize, res * PAGE_SIZE - *start); + iov_iter_advance(i, maxsize); + return maxsize; } if (iov_iter_is_bvec(i)) { + struct page **p; struct page *page; page = first_bvec_segment(i, &maxsize, start); - n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE); - if (n > maxpages) - n = maxpages; + n = want_pages_array(pages, maxsize, *start, maxpages); + if (!n) + return -ENOMEM; + p = *pages; for (int k = 0; k < n; k++) - get_page(*pages++ = page++); - return min_t(size_t, maxsize, n * PAGE_SIZE - *start); + get_page(p[k] = page + k); + maxsize = min_t(size_t, maxsize, n * PAGE_SIZE - *start); + i->count -= maxsize; + i->iov_offset += maxsize; + if (i->iov_offset == i->bvec->bv_len) { + i->iov_offset = 0; + i->bvec++; + i->nr_segs--; + } + return maxsize; } if (iov_iter_is_pipe(i)) return pipe_get_pages(i, pages, maxsize, maxpages, start); @@ -1469,140 +1488,35 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, return iter_xarray_get_pages(i, pages, maxsize, maxpages, start); return -EFAULT; } -EXPORT_SYMBOL(iov_iter_get_pages); -static struct page **get_pages_array(size_t n) -{ - return kvmalloc_array(n, sizeof(struct page *), GFP_KERNEL); -} - -static ssize_t pipe_get_pages_alloc(struct iov_iter *i, - struct page ***pages, size_t maxsize, +ssize_t iov_iter_get_pages2(struct iov_iter *i, + struct page **pages, size_t maxsize, unsigned maxpages, size_t *start) { - struct page **p; - unsigned int iter_head, npages; - ssize_t n; - - if (!sanity(i)) - return -EFAULT; - - data_start(i, &iter_head, start); - /* Amount of free space: some of this one + all after this one */ - npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe); - n = npages * PAGE_SIZE - *start; - if (maxsize > n) - maxsize = n; - else - npages = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE); - p = get_pages_array(npages); - if (!p) - return -ENOMEM; - n = __pipe_get_pages(i, maxsize, p, iter_head, start); - if (n > 0) - *pages = p; - else - kvfree(p); - return n; -} - -static ssize_t iter_xarray_get_pages_alloc(struct iov_iter *i, - struct page ***pages, size_t maxsize, - size_t *_start_offset) -{ - struct page **p; - unsigned nr, offset; - pgoff_t index, count; - size_t size = maxsize; - loff_t pos; - - if (!size) + if (!maxpages) return 0; + BUG_ON(!pages); - pos = i->xarray_start + i->iov_offset; - index = pos >> PAGE_SHIFT; - offset = pos & ~PAGE_MASK; - *_start_offset = offset; - - count = 1; - if (size > PAGE_SIZE - offset) { - size -= PAGE_SIZE - offset; - count += size >> PAGE_SHIFT; - size &= ~PAGE_MASK; - if (size) - count++; - } - - p = get_pages_array(count); - if (!p) - return -ENOMEM; - *pages = p; - - nr = iter_xarray_populate_pages(p, i->xarray, index, count); - if (nr == 0) - return 0; - - return min_t(size_t, nr * PAGE_SIZE - offset, maxsize); + return __iov_iter_get_pages_alloc(i, &pages, maxsize, maxpages, start); } +EXPORT_SYMBOL(iov_iter_get_pages2); -ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, +ssize_t iov_iter_get_pages_alloc2(struct iov_iter *i, struct page ***pages, size_t maxsize, size_t *start) { - struct page **p; - int n, res; + ssize_t len; - if (maxsize > i->count) - maxsize = i->count; - if (!maxsize) - return 0; - if (maxsize > MAX_RW_COUNT) - maxsize = MAX_RW_COUNT; - - if (likely(iter_is_iovec(i))) { - unsigned int gup_flags = 0; - unsigned long addr; - - if (iov_iter_rw(i) != WRITE) - gup_flags |= FOLL_WRITE; - if (i->nofault) - gup_flags |= FOLL_NOFAULT; - - addr = first_iovec_segment(i, &maxsize); - *start = addr % PAGE_SIZE; - addr &= PAGE_MASK; - n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE); - p = get_pages_array(n); - if (!p) - return -ENOMEM; - res = get_user_pages_fast(addr, n, gup_flags, p); - if (unlikely(res <= 0)) { - kvfree(p); - *pages = NULL; - return res; - } - *pages = p; - return min_t(size_t, maxsize, res * PAGE_SIZE - *start); - } - if (iov_iter_is_bvec(i)) { - struct page *page; + *pages = NULL; - page = first_bvec_segment(i, &maxsize, start); - n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE); - *pages = p = get_pages_array(n); - if (!p) - return -ENOMEM; - for (int k = 0; k < n; k++) - get_page(*p++ = page++); - return min_t(size_t, maxsize, n * PAGE_SIZE - *start); + len = __iov_iter_get_pages_alloc(i, pages, maxsize, ~0U, start); + if (len <= 0) { + kvfree(*pages); + *pages = NULL; } - if (iov_iter_is_pipe(i)) - return pipe_get_pages_alloc(i, pages, maxsize, start); - if (iov_iter_is_xarray(i)) - return iter_xarray_get_pages_alloc(i, pages, maxsize, start); - return -EFAULT; + return len; } -EXPORT_SYMBOL(iov_iter_get_pages_alloc); +EXPORT_SYMBOL(iov_iter_get_pages_alloc2); size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i) @@ -1715,22 +1629,23 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) { if (unlikely(!i->count)) return 0; + if (likely(iter_is_ubuf(i))) { + unsigned offs = offset_in_page(i->ubuf + i->iov_offset); + int npages = DIV_ROUND_UP(offs + i->count, PAGE_SIZE); + return min(npages, maxpages); + } /* iovec and kvec have identical layouts */ if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i))) return iov_npages(i, maxpages); if (iov_iter_is_bvec(i)) return bvec_npages(i, maxpages); if (iov_iter_is_pipe(i)) { - unsigned int iter_head; int npages; - size_t off; if (!sanity(i)) return 0; - data_start(i, &iter_head, &off); - /* some of this one + all after this one */ - npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe); + pipe_npages(i, &npages); return min(npages, maxpages); } if (iov_iter_is_xarray(i)) { @@ -1749,17 +1664,16 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) WARN_ON(1); return NULL; } - if (unlikely(iov_iter_is_discard(new) || iov_iter_is_xarray(new))) - return NULL; if (iov_iter_is_bvec(new)) return new->bvec = kmemdup(new->bvec, new->nr_segs * sizeof(struct bio_vec), flags); - else + else if (iov_iter_is_kvec(new) || iter_is_iovec(new)) /* iovec and kvec have identical layout */ return new->iov = kmemdup(new->iov, new->nr_segs * sizeof(struct iovec), flags); + return NULL; } EXPORT_SYMBOL(dup_iter); @@ -1953,10 +1867,12 @@ EXPORT_SYMBOL(import_single_range); void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state) { if (WARN_ON_ONCE(!iov_iter_is_bvec(i) && !iter_is_iovec(i)) && - !iov_iter_is_kvec(i)) + !iov_iter_is_kvec(i) && !iter_is_ubuf(i)) return; i->iov_offset = state->iov_offset; i->count = state->count; + if (iter_is_ubuf(i)) + return; /* * For the *vec iters, nr_segs + iov is constant - if we increment * the vec, then we also decrement the nr_segs count. Hence we don't diff --git a/lib/nodemask.c b/lib/nodemask.c deleted file mode 100644 index b8a433d16b51..000000000000 --- a/lib/nodemask.c +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/nodemask.h> -#include <linux/module.h> -#include <linux/random.h> - -EXPORT_SYMBOL(__next_node_in); - -#ifdef CONFIG_NUMA -/* - * Return the bit number of a random bit set in the nodemask. - * (returns NUMA_NO_NODE if nodemask is empty) - */ -int node_random(const nodemask_t *maskp) -{ - int w, bit = NUMA_NO_NODE; - - w = nodes_weight(*maskp); - if (w) - bit = bitmap_ord_to_pos(maskp->bits, - get_random_int() % w, MAX_NUMNODES); - return bit; -} -#endif diff --git a/mm/Kconfig b/mm/Kconfig index e59cf5fe5ce9..0331f1461f81 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -983,9 +983,14 @@ config HMM_MIRROR bool depends on MMU +config GET_FREE_REGION + depends on SPARSEMEM + bool + config DEVICE_PRIVATE bool "Unaddressable device memory (GPU memory, ...)" depends on ZONE_DEVICE + select GET_FREE_REGION help Allows creation of struct pages to represent unaddressable device diff --git a/mm/hugetlb.c b/mm/hugetlb.c index f044962ad9df..0aee2f3ae15c 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1535,7 +1535,14 @@ static void __update_and_free_page(struct hstate *h, struct page *page) if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported()) return; - if (hugetlb_vmemmap_alloc(h, page)) { + /* + * If we don't know which subpages are hwpoisoned, we can't free + * the hugepage, so it's leaked intentionally. + */ + if (HPageRawHwpUnreliable(page)) + return; + + if (hugetlb_vmemmap_restore(h, page)) { spin_lock_irq(&hugetlb_lock); /* * If we cannot allocate vmemmap pages, just refuse to free the @@ -1547,6 +1554,13 @@ static void __update_and_free_page(struct hstate *h, struct page *page) return; } + /* + * Move PageHWPoison flag from head page to the raw error pages, + * which makes any healthy subpages reusable. + */ + if (unlikely(PageHWPoison(page))) + hugetlb_clear_page_hwpoison(page); + for (i = 0; i < pages_per_huge_page(h); i++, subpage = mem_map_next(subpage, page, i)) { subpage->flags &= ~(1 << PG_locked | 1 << PG_error | @@ -1612,7 +1626,7 @@ static DECLARE_WORK(free_hpage_work, free_hpage_workfn); static inline void flush_free_hpage_work(struct hstate *h) { - if (hugetlb_optimize_vmemmap_pages(h)) + if (hugetlb_vmemmap_optimizable(h)) flush_work(&free_hpage_work); } @@ -1734,7 +1748,7 @@ static void __prep_account_new_huge_page(struct hstate *h, int nid) static void __prep_new_huge_page(struct hstate *h, struct page *page) { - hugetlb_vmemmap_free(h, page); + hugetlb_vmemmap_optimize(h, page); INIT_LIST_HEAD(&page->lru); set_compound_page_dtor(page, HUGETLB_PAGE_DTOR); hugetlb_set_page_subpool(page, NULL); @@ -2107,17 +2121,8 @@ retry: * Attempt to allocate vmemmmap here so that we can take * appropriate action on failure. */ - rc = hugetlb_vmemmap_alloc(h, head); + rc = hugetlb_vmemmap_restore(h, head); if (!rc) { - /* - * Move PageHWPoison flag from head page to the raw - * error page, which makes any subpages rather than - * the error page reusable. - */ - if (PageHWPoison(head) && page != head) { - SetPageHWPoison(page); - ClearPageHWPoison(head); - } update_and_free_page(h, head, false); } else { spin_lock_irq(&hugetlb_lock); @@ -2432,8 +2437,7 @@ static void return_unused_surplus_pages(struct hstate *h, /* Uncommit the reservation */ h->resv_huge_pages -= unused_resv_pages; - /* Cannot return gigantic pages currently */ - if (hstate_is_gigantic(h)) + if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported()) goto out; /* @@ -3182,8 +3186,10 @@ static void __init report_hugepages(void) char buf[32]; string_get_size(huge_page_size(h), 1, STRING_UNITS_2, buf, 32); - pr_info("HugeTLB registered %s page size, pre-allocated %ld pages\n", + pr_info("HugeTLB: registered %s page size, pre-allocated %ld pages\n", buf, h->free_huge_pages); + pr_info("HugeTLB: %d KiB vmemmap can be freed for a %s page\n", + hugetlb_vmemmap_optimizable_size(h) / SZ_1K, buf); } } @@ -3421,7 +3427,7 @@ static int demote_free_huge_page(struct hstate *h, struct page *page) remove_hugetlb_page_for_demote(h, page, false); spin_unlock_irq(&hugetlb_lock); - rc = hugetlb_vmemmap_alloc(h, page); + rc = hugetlb_vmemmap_restore(h, page); if (rc) { /* Allocation of vmemmmap failed, we can not demote page */ spin_lock_irq(&hugetlb_lock); @@ -4111,7 +4117,6 @@ void __init hugetlb_add_hstate(unsigned int order) h->next_nid_to_free = first_memory_node; snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", huge_page_size(h)/1024); - hugetlb_vmemmap_init(h); parsed_hstate = h; } @@ -6985,10 +6990,38 @@ struct page * __weak follow_huge_pud(struct mm_struct *mm, unsigned long address, pud_t *pud, int flags) { - if (flags & (FOLL_GET | FOLL_PIN)) + struct page *page = NULL; + spinlock_t *ptl; + pte_t pte; + + if (WARN_ON_ONCE(flags & FOLL_PIN)) return NULL; - return pte_page(*(pte_t *)pud) + ((address & ~PUD_MASK) >> PAGE_SHIFT); +retry: + ptl = huge_pte_lock(hstate_sizelog(PUD_SHIFT), mm, (pte_t *)pud); + if (!pud_huge(*pud)) + goto out; + pte = huge_ptep_get((pte_t *)pud); + if (pte_present(pte)) { + page = pud_page(*pud) + ((address & ~PUD_MASK) >> PAGE_SHIFT); + if (WARN_ON_ONCE(!try_grab_page(page, flags))) { + page = NULL; + goto out; + } + } else { + if (is_hugetlb_entry_migration(pte)) { + spin_unlock(ptl); + __migration_entry_wait(mm, (pte_t *)pud, ptl); + goto retry; + } + /* + * hwpoisoned entry is treated as no_page_table in + * follow_page_mask(). + */ + } +out: + spin_unlock(ptl); + return page; } struct page * __weak diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index 1362feb3c6c9..20f414c0379f 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Optimize vmemmap pages associated with HugeTLB + * HugeTLB Vmemmap Optimization (HVO) * - * Copyright (c) 2020, Bytedance. All rights reserved. + * Copyright (c) 2020, ByteDance. All rights reserved. * * Author: Muchun Song <songmuchun@bytedance.com> * @@ -10,84 +10,443 @@ */ #define pr_fmt(fmt) "HugeTLB: " fmt -#include <linux/memory.h> +#include <linux/pgtable.h> +#include <linux/bootmem_info.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> #include "hugetlb_vmemmap.h" -/* - * There are a lot of struct page structures associated with each HugeTLB page. - * For tail pages, the value of compound_head is the same. So we can reuse first - * page of head page structures. We map the virtual addresses of all the pages - * of tail page structures to the head page struct, and then free these page - * frames. Therefore, we need to reserve one pages as vmemmap areas. +/** + * struct vmemmap_remap_walk - walk vmemmap page table + * + * @remap_pte: called for each lowest-level entry (PTE). + * @nr_walked: the number of walked pte. + * @reuse_page: the page which is reused for the tail vmemmap pages. + * @reuse_addr: the virtual address of the @reuse_page page. + * @vmemmap_pages: the list head of the vmemmap pages that can be freed + * or is mapped from. */ -#define RESERVE_VMEMMAP_NR 1U -#define RESERVE_VMEMMAP_SIZE (RESERVE_VMEMMAP_NR << PAGE_SHIFT) - -enum vmemmap_optimize_mode { - VMEMMAP_OPTIMIZE_OFF, - VMEMMAP_OPTIMIZE_ON, +struct vmemmap_remap_walk { + void (*remap_pte)(pte_t *pte, unsigned long addr, + struct vmemmap_remap_walk *walk); + unsigned long nr_walked; + struct page *reuse_page; + unsigned long reuse_addr; + struct list_head *vmemmap_pages; }; -DEFINE_STATIC_KEY_MAYBE(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON, - hugetlb_optimize_vmemmap_key); -EXPORT_SYMBOL(hugetlb_optimize_vmemmap_key); +static int __split_vmemmap_huge_pmd(pmd_t *pmd, unsigned long start) +{ + pmd_t __pmd; + int i; + unsigned long addr = start; + struct page *page = pmd_page(*pmd); + pte_t *pgtable = pte_alloc_one_kernel(&init_mm); + + if (!pgtable) + return -ENOMEM; + + pmd_populate_kernel(&init_mm, &__pmd, pgtable); + + for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) { + pte_t entry, *pte; + pgprot_t pgprot = PAGE_KERNEL; + + entry = mk_pte(page + i, pgprot); + pte = pte_offset_kernel(&__pmd, addr); + set_pte_at(&init_mm, addr, pte, entry); + } -static enum vmemmap_optimize_mode vmemmap_optimize_mode = - IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON); + spin_lock(&init_mm.page_table_lock); + if (likely(pmd_leaf(*pmd))) { + /* + * Higher order allocations from buddy allocator must be able to + * be treated as indepdenent small pages (as they can be freed + * individually). + */ + if (!PageReserved(page)) + split_page(page, get_order(PMD_SIZE)); + + /* Make pte visible before pmd. See comment in pmd_install(). */ + smp_wmb(); + pmd_populate_kernel(&init_mm, pmd, pgtable); + flush_tlb_kernel_range(start, start + PMD_SIZE); + } else { + pte_free_kernel(&init_mm, pgtable); + } + spin_unlock(&init_mm.page_table_lock); + + return 0; +} -static void vmemmap_optimize_mode_switch(enum vmemmap_optimize_mode to) +static int split_vmemmap_huge_pmd(pmd_t *pmd, unsigned long start) { - if (vmemmap_optimize_mode == to) - return; + int leaf; - if (to == VMEMMAP_OPTIMIZE_OFF) - static_branch_dec(&hugetlb_optimize_vmemmap_key); - else - static_branch_inc(&hugetlb_optimize_vmemmap_key); - WRITE_ONCE(vmemmap_optimize_mode, to); + spin_lock(&init_mm.page_table_lock); + leaf = pmd_leaf(*pmd); + spin_unlock(&init_mm.page_table_lock); + + if (!leaf) + return 0; + + return __split_vmemmap_huge_pmd(pmd, start); +} + +static void vmemmap_pte_range(pmd_t *pmd, unsigned long addr, + unsigned long end, + struct vmemmap_remap_walk *walk) +{ + pte_t *pte = pte_offset_kernel(pmd, addr); + + /* + * The reuse_page is found 'first' in table walk before we start + * remapping (which is calling @walk->remap_pte). + */ + if (!walk->reuse_page) { + walk->reuse_page = pte_page(*pte); + /* + * Because the reuse address is part of the range that we are + * walking, skip the reuse address range. + */ + addr += PAGE_SIZE; + pte++; + walk->nr_walked++; + } + + for (; addr != end; addr += PAGE_SIZE, pte++) { + walk->remap_pte(pte, addr, walk); + walk->nr_walked++; + } } -static int __init hugetlb_vmemmap_early_param(char *buf) +static int vmemmap_pmd_range(pud_t *pud, unsigned long addr, + unsigned long end, + struct vmemmap_remap_walk *walk) { - bool enable; - enum vmemmap_optimize_mode mode; + pmd_t *pmd; + unsigned long next; - if (kstrtobool(buf, &enable)) - return -EINVAL; + pmd = pmd_offset(pud, addr); + do { + int ret; - mode = enable ? VMEMMAP_OPTIMIZE_ON : VMEMMAP_OPTIMIZE_OFF; - vmemmap_optimize_mode_switch(mode); + ret = split_vmemmap_huge_pmd(pmd, addr & PMD_MASK); + if (ret) + return ret; + + next = pmd_addr_end(addr, end); + vmemmap_pte_range(pmd, addr, next, walk); + } while (pmd++, addr = next, addr != end); + + return 0; +} + +static int vmemmap_pud_range(p4d_t *p4d, unsigned long addr, + unsigned long end, + struct vmemmap_remap_walk *walk) +{ + pud_t *pud; + unsigned long next; + + pud = pud_offset(p4d, addr); + do { + int ret; + + next = pud_addr_end(addr, end); + ret = vmemmap_pmd_range(pud, addr, next, walk); + if (ret) + return ret; + } while (pud++, addr = next, addr != end); + + return 0; +} + +static int vmemmap_p4d_range(pgd_t *pgd, unsigned long addr, + unsigned long end, + struct vmemmap_remap_walk *walk) +{ + p4d_t *p4d; + unsigned long next; + + p4d = p4d_offset(pgd, addr); + do { + int ret; + + next = p4d_addr_end(addr, end); + ret = vmemmap_pud_range(p4d, addr, next, walk); + if (ret) + return ret; + } while (p4d++, addr = next, addr != end); + + return 0; +} + +static int vmemmap_remap_range(unsigned long start, unsigned long end, + struct vmemmap_remap_walk *walk) +{ + unsigned long addr = start; + unsigned long next; + pgd_t *pgd; + + VM_BUG_ON(!PAGE_ALIGNED(start)); + VM_BUG_ON(!PAGE_ALIGNED(end)); + + pgd = pgd_offset_k(addr); + do { + int ret; + + next = pgd_addr_end(addr, end); + ret = vmemmap_p4d_range(pgd, addr, next, walk); + if (ret) + return ret; + } while (pgd++, addr = next, addr != end); + + /* + * We only change the mapping of the vmemmap virtual address range + * [@start + PAGE_SIZE, end), so we only need to flush the TLB which + * belongs to the range. + */ + flush_tlb_kernel_range(start + PAGE_SIZE, end); return 0; } -early_param("hugetlb_free_vmemmap", hugetlb_vmemmap_early_param); /* - * Previously discarded vmemmap pages will be allocated and remapping - * after this function returns zero. + * Free a vmemmap page. A vmemmap page can be allocated from the memblock + * allocator or buddy allocator. If the PG_reserved flag is set, it means + * that it allocated from the memblock allocator, just free it via the + * free_bootmem_page(). Otherwise, use __free_page(). */ -int hugetlb_vmemmap_alloc(struct hstate *h, struct page *head) +static inline void free_vmemmap_page(struct page *page) +{ + if (PageReserved(page)) + free_bootmem_page(page); + else + __free_page(page); +} + +/* Free a list of the vmemmap pages */ +static void free_vmemmap_page_list(struct list_head *list) +{ + struct page *page, *next; + + list_for_each_entry_safe(page, next, list, lru) { + list_del(&page->lru); + free_vmemmap_page(page); + } +} + +static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, + struct vmemmap_remap_walk *walk) +{ + /* + * Remap the tail pages as read-only to catch illegal write operation + * to the tail pages. + */ + pgprot_t pgprot = PAGE_KERNEL_RO; + pte_t entry = mk_pte(walk->reuse_page, pgprot); + struct page *page = pte_page(*pte); + + list_add_tail(&page->lru, walk->vmemmap_pages); + set_pte_at(&init_mm, addr, pte, entry); +} + +/* + * How many struct page structs need to be reset. When we reuse the head + * struct page, the special metadata (e.g. page->flags or page->mapping) + * cannot copy to the tail struct page structs. The invalid value will be + * checked in the free_tail_pages_check(). In order to avoid the message + * of "corrupted mapping in tail page". We need to reset at least 3 (one + * head struct page struct and two tail struct page structs) struct page + * structs. + */ +#define NR_RESET_STRUCT_PAGE 3 + +static inline void reset_struct_pages(struct page *start) +{ + int i; + struct page *from = start + NR_RESET_STRUCT_PAGE; + + for (i = 0; i < NR_RESET_STRUCT_PAGE; i++) + memcpy(start + i, from, sizeof(*from)); +} + +static void vmemmap_restore_pte(pte_t *pte, unsigned long addr, + struct vmemmap_remap_walk *walk) +{ + pgprot_t pgprot = PAGE_KERNEL; + struct page *page; + void *to; + + BUG_ON(pte_page(*pte) != walk->reuse_page); + + page = list_first_entry(walk->vmemmap_pages, struct page, lru); + list_del(&page->lru); + to = page_to_virt(page); + copy_page(to, (void *)walk->reuse_addr); + reset_struct_pages(to); + + set_pte_at(&init_mm, addr, pte, mk_pte(page, pgprot)); +} + +/** + * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end) + * to the page which @reuse is mapped to, then free vmemmap + * which the range are mapped to. + * @start: start address of the vmemmap virtual address range that we want + * to remap. + * @end: end address of the vmemmap virtual address range that we want to + * remap. + * @reuse: reuse address. + * + * Return: %0 on success, negative error code otherwise. + */ +static int vmemmap_remap_free(unsigned long start, unsigned long end, + unsigned long reuse) +{ + int ret; + LIST_HEAD(vmemmap_pages); + struct vmemmap_remap_walk walk = { + .remap_pte = vmemmap_remap_pte, + .reuse_addr = reuse, + .vmemmap_pages = &vmemmap_pages, + }; + + /* + * In order to make remapping routine most efficient for the huge pages, + * the routine of vmemmap page table walking has the following rules + * (see more details from the vmemmap_pte_range()): + * + * - The range [@start, @end) and the range [@reuse, @reuse + PAGE_SIZE) + * should be continuous. + * - The @reuse address is part of the range [@reuse, @end) that we are + * walking which is passed to vmemmap_remap_range(). + * - The @reuse address is the first in the complete range. + * + * So we need to make sure that @start and @reuse meet the above rules. + */ + BUG_ON(start - reuse != PAGE_SIZE); + + mmap_read_lock(&init_mm); + ret = vmemmap_remap_range(reuse, end, &walk); + if (ret && walk.nr_walked) { + end = reuse + walk.nr_walked * PAGE_SIZE; + /* + * vmemmap_pages contains pages from the previous + * vmemmap_remap_range call which failed. These + * are pages which were removed from the vmemmap. + * They will be restored in the following call. + */ + walk = (struct vmemmap_remap_walk) { + .remap_pte = vmemmap_restore_pte, + .reuse_addr = reuse, + .vmemmap_pages = &vmemmap_pages, + }; + + vmemmap_remap_range(reuse, end, &walk); + } + mmap_read_unlock(&init_mm); + + free_vmemmap_page_list(&vmemmap_pages); + + return ret; +} + +static int alloc_vmemmap_page_list(unsigned long start, unsigned long end, + gfp_t gfp_mask, struct list_head *list) +{ + unsigned long nr_pages = (end - start) >> PAGE_SHIFT; + int nid = page_to_nid((struct page *)start); + struct page *page, *next; + + while (nr_pages--) { + page = alloc_pages_node(nid, gfp_mask, 0); + if (!page) + goto out; + list_add_tail(&page->lru, list); + } + + return 0; +out: + list_for_each_entry_safe(page, next, list, lru) + __free_pages(page, 0); + return -ENOMEM; +} + +/** + * vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end) + * to the page which is from the @vmemmap_pages + * respectively. + * @start: start address of the vmemmap virtual address range that we want + * to remap. + * @end: end address of the vmemmap virtual address range that we want to + * remap. + * @reuse: reuse address. + * @gfp_mask: GFP flag for allocating vmemmap pages. + * + * Return: %0 on success, negative error code otherwise. + */ +static int vmemmap_remap_alloc(unsigned long start, unsigned long end, + unsigned long reuse, gfp_t gfp_mask) +{ + LIST_HEAD(vmemmap_pages); + struct vmemmap_remap_walk walk = { + .remap_pte = vmemmap_restore_pte, + .reuse_addr = reuse, + .vmemmap_pages = &vmemmap_pages, + }; + + /* See the comment in the vmemmap_remap_free(). */ + BUG_ON(start - reuse != PAGE_SIZE); + + if (alloc_vmemmap_page_list(start, end, gfp_mask, &vmemmap_pages)) + return -ENOMEM; + + mmap_read_lock(&init_mm); + vmemmap_remap_range(reuse, end, &walk); + mmap_read_unlock(&init_mm); + + return 0; +} + +DEFINE_STATIC_KEY_FALSE(hugetlb_optimize_vmemmap_key); +EXPORT_SYMBOL(hugetlb_optimize_vmemmap_key); + +static bool vmemmap_optimize_enabled = IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON); +core_param(hugetlb_free_vmemmap, vmemmap_optimize_enabled, bool, 0); + +/** + * hugetlb_vmemmap_restore - restore previously optimized (by + * hugetlb_vmemmap_optimize()) vmemmap pages which + * will be reallocated and remapped. + * @h: struct hstate. + * @head: the head page whose vmemmap pages will be restored. + * + * Return: %0 if @head's vmemmap pages have been reallocated and remapped, + * negative error code otherwise. + */ +int hugetlb_vmemmap_restore(const struct hstate *h, struct page *head) { int ret; - unsigned long vmemmap_addr = (unsigned long)head; - unsigned long vmemmap_end, vmemmap_reuse, vmemmap_pages; + unsigned long vmemmap_start = (unsigned long)head, vmemmap_end; + unsigned long vmemmap_reuse; if (!HPageVmemmapOptimized(head)) return 0; - vmemmap_addr += RESERVE_VMEMMAP_SIZE; - vmemmap_pages = hugetlb_optimize_vmemmap_pages(h); - vmemmap_end = vmemmap_addr + (vmemmap_pages << PAGE_SHIFT); - vmemmap_reuse = vmemmap_addr - PAGE_SIZE; + vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h); + vmemmap_reuse = vmemmap_start; + vmemmap_start += HUGETLB_VMEMMAP_RESERVE_SIZE; /* - * The pages which the vmemmap virtual address range [@vmemmap_addr, + * The pages which the vmemmap virtual address range [@vmemmap_start, * @vmemmap_end) are mapped to are freed to the buddy allocator, and * the range is mapped to the page which @vmemmap_reuse is mapped to. * When a HugeTLB page is freed to the buddy allocator, previously * discarded vmemmap pages must be allocated and remapping. */ - ret = vmemmap_remap_alloc(vmemmap_addr, vmemmap_end, vmemmap_reuse, + ret = vmemmap_remap_alloc(vmemmap_start, vmemmap_end, vmemmap_reuse, GFP_KERNEL | __GFP_NORETRY | __GFP_THISNODE); if (!ret) { ClearHPageVmemmapOptimized(head); @@ -97,11 +456,14 @@ int hugetlb_vmemmap_alloc(struct hstate *h, struct page *head) return ret; } -static unsigned int vmemmap_optimizable_pages(struct hstate *h, - struct page *head) +/* Return true iff a HugeTLB whose vmemmap should and can be optimized. */ +static bool vmemmap_should_optimize(const struct hstate *h, const struct page *head) { - if (READ_ONCE(vmemmap_optimize_mode) == VMEMMAP_OPTIMIZE_OFF) - return 0; + if (!READ_ONCE(vmemmap_optimize_enabled)) + return false; + + if (!hugetlb_vmemmap_optimizable(h)) + return false; if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) { pmd_t *pmdp, pmd; @@ -144,118 +506,73 @@ static unsigned int vmemmap_optimizable_pages(struct hstate *h, * +-------------------------------------------+ */ if (PageVmemmapSelfHosted(vmemmap_page)) - return 0; + return false; } - return hugetlb_optimize_vmemmap_pages(h); + return true; } -void hugetlb_vmemmap_free(struct hstate *h, struct page *head) +/** + * hugetlb_vmemmap_optimize - optimize @head page's vmemmap pages. + * @h: struct hstate. + * @head: the head page whose vmemmap pages will be optimized. + * + * This function only tries to optimize @head's vmemmap pages and does not + * guarantee that the optimization will succeed after it returns. The caller + * can use HPageVmemmapOptimized(@head) to detect if @head's vmemmap pages + * have been optimized. + */ +void hugetlb_vmemmap_optimize(const struct hstate *h, struct page *head) { - unsigned long vmemmap_addr = (unsigned long)head; - unsigned long vmemmap_end, vmemmap_reuse, vmemmap_pages; + unsigned long vmemmap_start = (unsigned long)head, vmemmap_end; + unsigned long vmemmap_reuse; - vmemmap_pages = vmemmap_optimizable_pages(h, head); - if (!vmemmap_pages) + if (!vmemmap_should_optimize(h, head)) return; static_branch_inc(&hugetlb_optimize_vmemmap_key); - vmemmap_addr += RESERVE_VMEMMAP_SIZE; - vmemmap_end = vmemmap_addr + (vmemmap_pages << PAGE_SHIFT); - vmemmap_reuse = vmemmap_addr - PAGE_SIZE; + vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h); + vmemmap_reuse = vmemmap_start; + vmemmap_start += HUGETLB_VMEMMAP_RESERVE_SIZE; /* - * Remap the vmemmap virtual address range [@vmemmap_addr, @vmemmap_end) + * Remap the vmemmap virtual address range [@vmemmap_start, @vmemmap_end) * to the page which @vmemmap_reuse is mapped to, then free the pages - * which the range [@vmemmap_addr, @vmemmap_end] is mapped to. + * which the range [@vmemmap_start, @vmemmap_end] is mapped to. */ - if (vmemmap_remap_free(vmemmap_addr, vmemmap_end, vmemmap_reuse)) + if (vmemmap_remap_free(vmemmap_start, vmemmap_end, vmemmap_reuse)) static_branch_dec(&hugetlb_optimize_vmemmap_key); else SetHPageVmemmapOptimized(head); } -void __init hugetlb_vmemmap_init(struct hstate *h) -{ - unsigned int nr_pages = pages_per_huge_page(h); - unsigned int vmemmap_pages; - - /* - * There are only (RESERVE_VMEMMAP_SIZE / sizeof(struct page)) struct - * page structs that can be used when CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP, - * so add a BUILD_BUG_ON to catch invalid usage of the tail struct page. - */ - BUILD_BUG_ON(__NR_USED_SUBPAGE >= - RESERVE_VMEMMAP_SIZE / sizeof(struct page)); - - if (!is_power_of_2(sizeof(struct page))) { - pr_warn_once("cannot optimize vmemmap pages because \"struct page\" crosses page boundaries\n"); - static_branch_disable(&hugetlb_optimize_vmemmap_key); - return; - } - - vmemmap_pages = (nr_pages * sizeof(struct page)) >> PAGE_SHIFT; - /* - * The head page is not to be freed to buddy allocator, the other tail - * pages will map to the head page, so they can be freed. - * - * Could RESERVE_VMEMMAP_NR be greater than @vmemmap_pages? It is true - * on some architectures (e.g. aarch64). See Documentation/arm64/ - * hugetlbpage.rst for more details. - */ - if (likely(vmemmap_pages > RESERVE_VMEMMAP_NR)) - h->optimize_vmemmap_pages = vmemmap_pages - RESERVE_VMEMMAP_NR; - - pr_info("can optimize %d vmemmap pages for %s\n", - h->optimize_vmemmap_pages, h->name); -} - -#ifdef CONFIG_PROC_SYSCTL -static int hugetlb_optimize_vmemmap_handler(struct ctl_table *table, int write, - void *buffer, size_t *length, - loff_t *ppos) -{ - int ret; - enum vmemmap_optimize_mode mode; - static DEFINE_MUTEX(sysctl_mutex); - - if (write && !capable(CAP_SYS_ADMIN)) - return -EPERM; - - mutex_lock(&sysctl_mutex); - mode = vmemmap_optimize_mode; - table->data = &mode; - ret = proc_dointvec_minmax(table, write, buffer, length, ppos); - if (write && !ret) - vmemmap_optimize_mode_switch(mode); - mutex_unlock(&sysctl_mutex); - - return ret; -} - static struct ctl_table hugetlb_vmemmap_sysctls[] = { { .procname = "hugetlb_optimize_vmemmap", - .maxlen = sizeof(enum vmemmap_optimize_mode), + .data = &vmemmap_optimize_enabled, + .maxlen = sizeof(int), .mode = 0644, - .proc_handler = hugetlb_optimize_vmemmap_handler, - .extra1 = SYSCTL_ZERO, - .extra2 = SYSCTL_ONE, + .proc_handler = proc_dobool, }, { } }; -static __init int hugetlb_vmemmap_sysctls_init(void) +static int __init hugetlb_vmemmap_init(void) { - /* - * If "struct page" crosses page boundaries, the vmemmap pages cannot - * be optimized. - */ - if (is_power_of_2(sizeof(struct page))) - register_sysctl_init("vm", hugetlb_vmemmap_sysctls); - + /* HUGETLB_VMEMMAP_RESERVE_SIZE should cover all used struct pages */ + BUILD_BUG_ON(__NR_USED_SUBPAGE * sizeof(struct page) > HUGETLB_VMEMMAP_RESERVE_SIZE); + + if (IS_ENABLED(CONFIG_PROC_SYSCTL)) { + const struct hstate *h; + + for_each_hstate(h) { + if (hugetlb_vmemmap_optimizable(h)) { + register_sysctl_init("vm", hugetlb_vmemmap_sysctls); + break; + } + } + } return 0; } -late_initcall(hugetlb_vmemmap_sysctls_init); -#endif /* CONFIG_PROC_SYSCTL */ +late_initcall(hugetlb_vmemmap_init); diff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap.h index 109b0a53b6fe..25bd0e002431 100644 --- a/mm/hugetlb_vmemmap.h +++ b/mm/hugetlb_vmemmap.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Optimize vmemmap pages associated with HugeTLB + * HugeTLB Vmemmap Optimization (HVO) * - * Copyright (c) 2020, Bytedance. All rights reserved. + * Copyright (c) 2020, ByteDance. All rights reserved. * * Author: Muchun Song <songmuchun@bytedance.com> */ @@ -11,35 +11,50 @@ #include <linux/hugetlb.h> #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP -int hugetlb_vmemmap_alloc(struct hstate *h, struct page *head); -void hugetlb_vmemmap_free(struct hstate *h, struct page *head); -void hugetlb_vmemmap_init(struct hstate *h); +int hugetlb_vmemmap_restore(const struct hstate *h, struct page *head); +void hugetlb_vmemmap_optimize(const struct hstate *h, struct page *head); /* - * How many vmemmap pages associated with a HugeTLB page that can be - * optimized and freed to the buddy allocator. + * Reserve one vmemmap page, all vmemmap addresses are mapped to it. See + * Documentation/vm/vmemmap_dedup.rst. */ -static inline unsigned int hugetlb_optimize_vmemmap_pages(struct hstate *h) +#define HUGETLB_VMEMMAP_RESERVE_SIZE PAGE_SIZE + +static inline unsigned int hugetlb_vmemmap_size(const struct hstate *h) { - return h->optimize_vmemmap_pages; + return pages_per_huge_page(h) * sizeof(struct page); +} + +/* + * Return how many vmemmap size associated with a HugeTLB page that can be + * optimized and can be freed to the buddy allocator. + */ +static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h) +{ + int size = hugetlb_vmemmap_size(h) - HUGETLB_VMEMMAP_RESERVE_SIZE; + + if (!is_power_of_2(sizeof(struct page))) + return 0; + return size > 0 ? size : 0; } #else -static inline int hugetlb_vmemmap_alloc(struct hstate *h, struct page *head) +static inline int hugetlb_vmemmap_restore(const struct hstate *h, struct page *head) { return 0; } -static inline void hugetlb_vmemmap_free(struct hstate *h, struct page *head) +static inline void hugetlb_vmemmap_optimize(const struct hstate *h, struct page *head) { } -static inline void hugetlb_vmemmap_init(struct hstate *h) +static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h) { + return 0; } +#endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */ -static inline unsigned int hugetlb_optimize_vmemmap_pages(struct hstate *h) +static inline bool hugetlb_vmemmap_optimizable(const struct hstate *h) { - return 0; + return hugetlb_vmemmap_optimizable_size(h) != 0; } -#endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */ #endif /* _LINUX_HUGETLB_VMEMMAP_H */ diff --git a/mm/memblock.c b/mm/memblock.c index c0894c137954..b5d3026979fc 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -597,6 +597,17 @@ static int __init_memblock memblock_add_range(struct memblock_type *type, type->total_size = size; return 0; } + + /* + * The worst case is when new range overlaps all existing regions, + * then we'll need type->cnt + 1 empty regions in @type. So if + * type->cnt * 2 + 1 is less than type->max, we know + * that there is enough empty regions in @type, and we can insert + * regions directly. + */ + if (type->cnt * 2 + 1 < type->max) + insert = true; + repeat: /* * The following is executed twice. Once with %false @insert and diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 9a7a228ad04a..14439806b5ef 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -74,7 +74,13 @@ atomic_long_t num_poisoned_pages __read_mostly = ATOMIC_LONG_INIT(0); static bool hw_memory_failure __read_mostly = false; -static bool __page_handle_poison(struct page *page) +/* + * Return values: + * 1: the page is dissolved (if needed) and taken off from buddy, + * 0: the page is dissolved (if needed) and not taken off from buddy, + * < 0: failed to dissolve. + */ +static int __page_handle_poison(struct page *page) { int ret; @@ -84,7 +90,7 @@ static bool __page_handle_poison(struct page *page) ret = take_page_off_buddy(page); zone_pcp_enable(page_zone(page)); - return ret > 0; + return ret; } static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, bool release) @@ -94,7 +100,7 @@ static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, boo * Doing this check for free pages is also fine since dissolve_free_huge_page * returns 0 for non-hugetlb pages as well. */ - if (!__page_handle_poison(page)) + if (__page_handle_poison(page) <= 0) /* * We could fail to take off the target page from buddy * for example due to racy page allocation, but that's @@ -762,7 +768,6 @@ static const char * const action_page_types[] = { [MF_MSG_DIFFERENT_COMPOUND] = "different compound page after locking", [MF_MSG_HUGE] = "huge page", [MF_MSG_FREE_HUGE] = "free huge page", - [MF_MSG_NON_PMD_HUGE] = "non-pmd-sized huge page", [MF_MSG_UNMAP_FAILED] = "unmapping failed page", [MF_MSG_DIRTY_SWAPCACHE] = "dirty swapcache page", [MF_MSG_CLEAN_SWAPCACHE] = "clean swapcache page", @@ -1078,7 +1083,6 @@ static int me_huge_page(struct page_state *ps, struct page *p) res = truncate_error_page(hpage, page_to_pfn(p), mapping); unlock_page(hpage); } else { - res = MF_FAILED; unlock_page(hpage); /* * migration entry prevents later access on error hugepage, @@ -1086,9 +1090,11 @@ static int me_huge_page(struct page_state *ps, struct page *p) * subpages. */ put_page(hpage); - if (__page_handle_poison(p)) { + if (__page_handle_poison(p) >= 0) { page_ref_inc(p); res = MF_RECOVERED; + } else { + res = MF_FAILED; } } @@ -1662,6 +1668,113 @@ unlock: EXPORT_SYMBOL_GPL(mf_dax_kill_procs); #endif /* CONFIG_FS_DAX */ +#ifdef CONFIG_HUGETLB_PAGE +/* + * Struct raw_hwp_page represents information about "raw error page", + * constructing singly linked list originated from ->private field of + * SUBPAGE_INDEX_HWPOISON-th tail page. + */ +struct raw_hwp_page { + struct llist_node node; + struct page *page; +}; + +static inline struct llist_head *raw_hwp_list_head(struct page *hpage) +{ + return (struct llist_head *)&page_private(hpage + SUBPAGE_INDEX_HWPOISON); +} + +static unsigned long __free_raw_hwp_pages(struct page *hpage, bool move_flag) +{ + struct llist_head *head; + struct llist_node *t, *tnode; + unsigned long count = 0; + + head = raw_hwp_list_head(hpage); + llist_for_each_safe(tnode, t, head->first) { + struct raw_hwp_page *p = container_of(tnode, struct raw_hwp_page, node); + + if (move_flag) + SetPageHWPoison(p->page); + kfree(p); + count++; + } + llist_del_all(head); + return count; +} + +static int hugetlb_set_page_hwpoison(struct page *hpage, struct page *page) +{ + struct llist_head *head; + struct raw_hwp_page *raw_hwp; + struct llist_node *t, *tnode; + int ret = TestSetPageHWPoison(hpage) ? -EHWPOISON : 0; + + /* + * Once the hwpoison hugepage has lost reliable raw error info, + * there is little meaning to keep additional error info precisely, + * so skip to add additional raw error info. + */ + if (HPageRawHwpUnreliable(hpage)) + return -EHWPOISON; + head = raw_hwp_list_head(hpage); + llist_for_each_safe(tnode, t, head->first) { + struct raw_hwp_page *p = container_of(tnode, struct raw_hwp_page, node); + + if (p->page == page) + return -EHWPOISON; + } + + raw_hwp = kmalloc(sizeof(struct raw_hwp_page), GFP_ATOMIC); + if (raw_hwp) { + raw_hwp->page = page; + llist_add(&raw_hwp->node, head); + /* the first error event will be counted in action_result(). */ + if (ret) + num_poisoned_pages_inc(); + } else { + /* + * Failed to save raw error info. We no longer trace all + * hwpoisoned subpages, and we need refuse to free/dissolve + * this hwpoisoned hugepage. + */ + SetHPageRawHwpUnreliable(hpage); + /* + * Once HPageRawHwpUnreliable is set, raw_hwp_page is not + * used any more, so free it. + */ + __free_raw_hwp_pages(hpage, false); + } + return ret; +} + +static unsigned long free_raw_hwp_pages(struct page *hpage, bool move_flag) +{ + /* + * HPageVmemmapOptimized hugepages can't be freed because struct + * pages for tail pages are required but they don't exist. + */ + if (move_flag && HPageVmemmapOptimized(hpage)) + return 0; + + /* + * HPageRawHwpUnreliable hugepages shouldn't be unpoisoned by + * definition. + */ + if (HPageRawHwpUnreliable(hpage)) + return 0; + + return __free_raw_hwp_pages(hpage, move_flag); +} + +void hugetlb_clear_page_hwpoison(struct page *hpage) +{ + if (HPageRawHwpUnreliable(hpage)) + return; + ClearPageHWPoison(hpage); + free_raw_hwp_pages(hpage, true); +} + /* * Called from hugetlb code with hugetlb_lock held. * @@ -1693,10 +1806,11 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags) count_increased = true; } else { ret = -EBUSY; - goto out; + if (!(flags & MF_NO_RETRY)) + goto out; } - if (TestSetPageHWPoison(head)) { + if (hugetlb_set_page_hwpoison(head, page)) { ret = -EHWPOISON; goto out; } @@ -1708,7 +1822,6 @@ out: return ret; } -#ifdef CONFIG_HUGETLB_PAGE /* * Taking refcount of hugetlb pages needs extra care about race conditions * with basic operations like hugepage allocation/free/demotion. @@ -1721,7 +1834,6 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb struct page *p = pfn_to_page(pfn); struct page *head; unsigned long page_flags; - bool retry = true; *hugetlb = 1; retry: @@ -1737,8 +1849,8 @@ retry: } return res; } else if (res == -EBUSY) { - if (retry) { - retry = false; + if (!(flags & MF_NO_RETRY)) { + flags |= MF_NO_RETRY; goto retry; } action_result(pfn, MF_MSG_UNKNOWN, MF_IGNORED); @@ -1749,7 +1861,7 @@ retry: lock_page(head); if (hwpoison_filter(p)) { - ClearPageHWPoison(head); + hugetlb_clear_page_hwpoison(head); res = -EOPNOTSUPP; goto out; } @@ -1760,10 +1872,11 @@ retry: */ if (res == 0) { unlock_page(head); - res = MF_FAILED; - if (__page_handle_poison(p)) { + if (__page_handle_poison(p) >= 0) { page_ref_inc(p); res = MF_RECOVERED; + } else { + res = MF_FAILED; } action_result(pfn, MF_MSG_FREE_HUGE, res); return res == MF_RECOVERED ? 0 : -EBUSY; @@ -1771,21 +1884,6 @@ retry: page_flags = head->flags; - /* - * TODO: hwpoison for pud-sized hugetlb doesn't work right now, so - * simply disable it. In order to make it work properly, we need - * make sure that: - * - conversion of a pud that maps an error hugetlb into hwpoison - * entry properly works, and - * - other mm code walking over page table is aware of pud-aligned - * hwpoison entries. - */ - if (huge_page_size(page_hstate(head)) > PMD_SIZE) { - action_result(pfn, MF_MSG_NON_PMD_HUGE, MF_IGNORED); - res = -EBUSY; - goto out; - } - if (!hwpoison_user_mappings(p, pfn, flags, head)) { action_result(pfn, MF_MSG_UNMAP_FAILED, MF_IGNORED); res = -EBUSY; @@ -1804,6 +1902,10 @@ static inline int try_memory_failure_hugetlb(unsigned long pfn, int flags, int * return 0; } +static inline unsigned long free_raw_hwp_pages(struct page *hpage, bool flag) +{ + return 0; +} #endif /* CONFIG_HUGETLB_PAGE */ static int memory_failure_dev_pagemap(unsigned long pfn, int flags, @@ -2209,6 +2311,7 @@ int unpoison_memory(unsigned long pfn) struct page *p; int ret = -EBUSY; int freeit = 0; + unsigned long count = 1; static DEFINE_RATELIMIT_STATE(unpoison_rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); @@ -2256,6 +2359,13 @@ int unpoison_memory(unsigned long pfn) ret = get_hwpoison_page(p, MF_UNPOISON); if (!ret) { + if (PageHuge(p)) { + count = free_raw_hwp_pages(page, false); + if (count == 0) { + ret = -EBUSY; + goto unlock_mutex; + } + } ret = TestClearPageHWPoison(page) ? 0 : -EBUSY; } else if (ret < 0) { if (ret == -EHWPOISON) { @@ -2264,6 +2374,13 @@ int unpoison_memory(unsigned long pfn) unpoison_pr_info("Unpoison: failed to grab page %#lx\n", pfn, &unpoison_rs); } else { + if (PageHuge(p)) { + count = free_raw_hwp_pages(page, false); + if (count == 0) { + ret = -EBUSY; + goto unlock_mutex; + } + } freeit = !!TestClearPageHWPoison(p); put_page(page); @@ -2276,7 +2393,7 @@ int unpoison_memory(unsigned long pfn) unlock_mutex: mutex_unlock(&mf_mutex); if (!ret || freeit) { - num_poisoned_pages_dec(); + num_poisoned_pages_sub(count); unpoison_pr_info("Unpoison: Software-unpoisoned page %#lx\n", page_to_pfn(p), &unpoison_rs); } diff --git a/mm/shmem.c b/mm/shmem.c index e975fcd9d2e1..5783f11351bb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2626,7 +2626,7 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to) ret = copy_page_to_iter(page, offset, nr, to); put_page(page); - } else if (iter_is_iovec(to)) { + } else if (user_backed_iter(to)) { /* * Copy to user tends to be so well optimized, but * clear_user() not so much, that it is noticeably diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 5f0ed4717ed0..46ae542118c0 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -27,408 +27,9 @@ #include <linux/spinlock.h> #include <linux/vmalloc.h> #include <linux/sched.h> -#include <linux/pgtable.h> -#include <linux/bootmem_info.h> #include <asm/dma.h> #include <asm/pgalloc.h> -#include <asm/tlbflush.h> - -#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP -/** - * struct vmemmap_remap_walk - walk vmemmap page table - * - * @remap_pte: called for each lowest-level entry (PTE). - * @nr_walked: the number of walked pte. - * @reuse_page: the page which is reused for the tail vmemmap pages. - * @reuse_addr: the virtual address of the @reuse_page page. - * @vmemmap_pages: the list head of the vmemmap pages that can be freed - * or is mapped from. - */ -struct vmemmap_remap_walk { - void (*remap_pte)(pte_t *pte, unsigned long addr, - struct vmemmap_remap_walk *walk); - unsigned long nr_walked; - struct page *reuse_page; - unsigned long reuse_addr; - struct list_head *vmemmap_pages; -}; - -static int __split_vmemmap_huge_pmd(pmd_t *pmd, unsigned long start) -{ - pmd_t __pmd; - int i; - unsigned long addr = start; - struct page *page = pmd_page(*pmd); - pte_t *pgtable = pte_alloc_one_kernel(&init_mm); - - if (!pgtable) - return -ENOMEM; - - pmd_populate_kernel(&init_mm, &__pmd, pgtable); - - for (i = 0; i < PMD_SIZE / PAGE_SIZE; i++, addr += PAGE_SIZE) { - pte_t entry, *pte; - pgprot_t pgprot = PAGE_KERNEL; - - entry = mk_pte(page + i, pgprot); - pte = pte_offset_kernel(&__pmd, addr); - set_pte_at(&init_mm, addr, pte, entry); - } - - spin_lock(&init_mm.page_table_lock); - if (likely(pmd_leaf(*pmd))) { - /* - * Higher order allocations from buddy allocator must be able to - * be treated as indepdenent small pages (as they can be freed - * individually). - */ - if (!PageReserved(page)) - split_page(page, get_order(PMD_SIZE)); - - /* Make pte visible before pmd. See comment in pmd_install(). */ - smp_wmb(); - pmd_populate_kernel(&init_mm, pmd, pgtable); - flush_tlb_kernel_range(start, start + PMD_SIZE); - } else { - pte_free_kernel(&init_mm, pgtable); - } - spin_unlock(&init_mm.page_table_lock); - - return 0; -} - -static int split_vmemmap_huge_pmd(pmd_t *pmd, unsigned long start) -{ - int leaf; - - spin_lock(&init_mm.page_table_lock); - leaf = pmd_leaf(*pmd); - spin_unlock(&init_mm.page_table_lock); - - if (!leaf) - return 0; - - return __split_vmemmap_huge_pmd(pmd, start); -} - -static void vmemmap_pte_range(pmd_t *pmd, unsigned long addr, - unsigned long end, - struct vmemmap_remap_walk *walk) -{ - pte_t *pte = pte_offset_kernel(pmd, addr); - - /* - * The reuse_page is found 'first' in table walk before we start - * remapping (which is calling @walk->remap_pte). - */ - if (!walk->reuse_page) { - walk->reuse_page = pte_page(*pte); - /* - * Because the reuse address is part of the range that we are - * walking, skip the reuse address range. - */ - addr += PAGE_SIZE; - pte++; - walk->nr_walked++; - } - - for (; addr != end; addr += PAGE_SIZE, pte++) { - walk->remap_pte(pte, addr, walk); - walk->nr_walked++; - } -} - -static int vmemmap_pmd_range(pud_t *pud, unsigned long addr, - unsigned long end, - struct vmemmap_remap_walk *walk) -{ - pmd_t *pmd; - unsigned long next; - - pmd = pmd_offset(pud, addr); - do { - int ret; - - ret = split_vmemmap_huge_pmd(pmd, addr & PMD_MASK); - if (ret) - return ret; - - next = pmd_addr_end(addr, end); - vmemmap_pte_range(pmd, addr, next, walk); - } while (pmd++, addr = next, addr != end); - - return 0; -} - -static int vmemmap_pud_range(p4d_t *p4d, unsigned long addr, - unsigned long end, - struct vmemmap_remap_walk *walk) -{ - pud_t *pud; - unsigned long next; - - pud = pud_offset(p4d, addr); - do { - int ret; - - next = pud_addr_end(addr, end); - ret = vmemmap_pmd_range(pud, addr, next, walk); - if (ret) - return ret; - } while (pud++, addr = next, addr != end); - - return 0; -} - -static int vmemmap_p4d_range(pgd_t *pgd, unsigned long addr, - unsigned long end, - struct vmemmap_remap_walk *walk) -{ - p4d_t *p4d; - unsigned long next; - - p4d = p4d_offset(pgd, addr); - do { - int ret; - - next = p4d_addr_end(addr, end); - ret = vmemmap_pud_range(p4d, addr, next, walk); - if (ret) - return ret; - } while (p4d++, addr = next, addr != end); - - return 0; -} - -static int vmemmap_remap_range(unsigned long start, unsigned long end, - struct vmemmap_remap_walk *walk) -{ - unsigned long addr = start; - unsigned long next; - pgd_t *pgd; - - VM_BUG_ON(!PAGE_ALIGNED(start)); - VM_BUG_ON(!PAGE_ALIGNED(end)); - - pgd = pgd_offset_k(addr); - do { - int ret; - - next = pgd_addr_end(addr, end); - ret = vmemmap_p4d_range(pgd, addr, next, walk); - if (ret) - return ret; - } while (pgd++, addr = next, addr != end); - - /* - * We only change the mapping of the vmemmap virtual address range - * [@start + PAGE_SIZE, end), so we only need to flush the TLB which - * belongs to the range. - */ - flush_tlb_kernel_range(start + PAGE_SIZE, end); - - return 0; -} - -/* - * Free a vmemmap page. A vmemmap page can be allocated from the memblock - * allocator or buddy allocator. If the PG_reserved flag is set, it means - * that it allocated from the memblock allocator, just free it via the - * free_bootmem_page(). Otherwise, use __free_page(). - */ -static inline void free_vmemmap_page(struct page *page) -{ - if (PageReserved(page)) - free_bootmem_page(page); - else - __free_page(page); -} - -/* Free a list of the vmemmap pages */ -static void free_vmemmap_page_list(struct list_head *list) -{ - struct page *page, *next; - - list_for_each_entry_safe(page, next, list, lru) { - list_del(&page->lru); - free_vmemmap_page(page); - } -} - -static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, - struct vmemmap_remap_walk *walk) -{ - /* - * Remap the tail pages as read-only to catch illegal write operation - * to the tail pages. - */ - pgprot_t pgprot = PAGE_KERNEL_RO; - pte_t entry = mk_pte(walk->reuse_page, pgprot); - struct page *page = pte_page(*pte); - - list_add_tail(&page->lru, walk->vmemmap_pages); - set_pte_at(&init_mm, addr, pte, entry); -} - -/* - * How many struct page structs need to be reset. When we reuse the head - * struct page, the special metadata (e.g. page->flags or page->mapping) - * cannot copy to the tail struct page structs. The invalid value will be - * checked in the free_tail_pages_check(). In order to avoid the message - * of "corrupted mapping in tail page". We need to reset at least 3 (one - * head struct page struct and two tail struct page structs) struct page - * structs. - */ -#define NR_RESET_STRUCT_PAGE 3 - -static inline void reset_struct_pages(struct page *start) -{ - int i; - struct page *from = start + NR_RESET_STRUCT_PAGE; - - for (i = 0; i < NR_RESET_STRUCT_PAGE; i++) - memcpy(start + i, from, sizeof(*from)); -} - -static void vmemmap_restore_pte(pte_t *pte, unsigned long addr, - struct vmemmap_remap_walk *walk) -{ - pgprot_t pgprot = PAGE_KERNEL; - struct page *page; - void *to; - - BUG_ON(pte_page(*pte) != walk->reuse_page); - - page = list_first_entry(walk->vmemmap_pages, struct page, lru); - list_del(&page->lru); - to = page_to_virt(page); - copy_page(to, (void *)walk->reuse_addr); - reset_struct_pages(to); - - set_pte_at(&init_mm, addr, pte, mk_pte(page, pgprot)); -} - -/** - * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end) - * to the page which @reuse is mapped to, then free vmemmap - * which the range are mapped to. - * @start: start address of the vmemmap virtual address range that we want - * to remap. - * @end: end address of the vmemmap virtual address range that we want to - * remap. - * @reuse: reuse address. - * - * Return: %0 on success, negative error code otherwise. - */ -int vmemmap_remap_free(unsigned long start, unsigned long end, - unsigned long reuse) -{ - int ret; - LIST_HEAD(vmemmap_pages); - struct vmemmap_remap_walk walk = { - .remap_pte = vmemmap_remap_pte, - .reuse_addr = reuse, - .vmemmap_pages = &vmemmap_pages, - }; - - /* - * In order to make remapping routine most efficient for the huge pages, - * the routine of vmemmap page table walking has the following rules - * (see more details from the vmemmap_pte_range()): - * - * - The range [@start, @end) and the range [@reuse, @reuse + PAGE_SIZE) - * should be continuous. - * - The @reuse address is part of the range [@reuse, @end) that we are - * walking which is passed to vmemmap_remap_range(). - * - The @reuse address is the first in the complete range. - * - * So we need to make sure that @start and @reuse meet the above rules. - */ - BUG_ON(start - reuse != PAGE_SIZE); - - mmap_read_lock(&init_mm); - ret = vmemmap_remap_range(reuse, end, &walk); - if (ret && walk.nr_walked) { - end = reuse + walk.nr_walked * PAGE_SIZE; - /* - * vmemmap_pages contains pages from the previous - * vmemmap_remap_range call which failed. These - * are pages which were removed from the vmemmap. - * They will be restored in the following call. - */ - walk = (struct vmemmap_remap_walk) { - .remap_pte = vmemmap_restore_pte, - .reuse_addr = reuse, - .vmemmap_pages = &vmemmap_pages, - }; - - vmemmap_remap_range(reuse, end, &walk); - } - mmap_read_unlock(&init_mm); - - free_vmemmap_page_list(&vmemmap_pages); - - return ret; -} - -static int alloc_vmemmap_page_list(unsigned long start, unsigned long end, - gfp_t gfp_mask, struct list_head *list) -{ - unsigned long nr_pages = (end - start) >> PAGE_SHIFT; - int nid = page_to_nid((struct page *)start); - struct page *page, *next; - - while (nr_pages--) { - page = alloc_pages_node(nid, gfp_mask, 0); - if (!page) - goto out; - list_add_tail(&page->lru, list); - } - - return 0; -out: - list_for_each_entry_safe(page, next, list, lru) - __free_pages(page, 0); - return -ENOMEM; -} - -/** - * vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end) - * to the page which is from the @vmemmap_pages - * respectively. - * @start: start address of the vmemmap virtual address range that we want - * to remap. - * @end: end address of the vmemmap virtual address range that we want to - * remap. - * @reuse: reuse address. - * @gfp_mask: GFP flag for allocating vmemmap pages. - * - * Return: %0 on success, negative error code otherwise. - */ -int vmemmap_remap_alloc(unsigned long start, unsigned long end, - unsigned long reuse, gfp_t gfp_mask) -{ - LIST_HEAD(vmemmap_pages); - struct vmemmap_remap_walk walk = { - .remap_pte = vmemmap_restore_pte, - .reuse_addr = reuse, - .vmemmap_pages = &vmemmap_pages, - }; - - /* See the comment in the vmemmap_remap_free(). */ - BUG_ON(start - reuse != PAGE_SIZE); - - if (alloc_vmemmap_page_list(start, end, gfp_mask, &vmemmap_pages)) - return -ENOMEM; - - mmap_read_lock(&init_mm); - vmemmap_remap_range(reuse, end, &walk); - mmap_read_unlock(&init_mm); - - return 0; -} -#endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */ /* * Allocate a block of memory to be used to back the virtual memory map diff --git a/net/9p/client.c b/net/9p/client.c index 5bf4dfef0c70..0a6110e15d0f 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1495,7 +1495,7 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to, struct p9_client *clnt = fid->clnt; struct p9_req_t *req; int count = iov_iter_count(to); - int rsize, non_zc = 0; + int rsize, received, non_zc = 0; char *dataptr; *err = 0; @@ -1524,36 +1524,40 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to, } if (IS_ERR(req)) { *err = PTR_ERR(req); + if (!non_zc) + iov_iter_revert(to, count - iov_iter_count(to)); return 0; } *err = p9pdu_readf(&req->rc, clnt->proto_version, - "D", &count, &dataptr); + "D", &received, &dataptr); if (*err) { + if (!non_zc) + iov_iter_revert(to, count - iov_iter_count(to)); trace_9p_protocol_dump(clnt, &req->rc); p9_req_put(clnt, req); return 0; } - if (rsize < count) { - pr_err("bogus RREAD count (%d > %d)\n", count, rsize); - count = rsize; + if (rsize < received) { + pr_err("bogus RREAD count (%d > %d)\n", received, rsize); + received = rsize; } p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count); if (non_zc) { - int n = copy_to_iter(dataptr, count, to); + int n = copy_to_iter(dataptr, received, to); - if (n != count) { + if (n != received) { *err = -EFAULT; p9_req_put(clnt, req); return n; } } else { - iov_iter_advance(to, count); + iov_iter_revert(to, count - received - iov_iter_count(to)); } p9_req_put(clnt, req); - return count; + return received; } EXPORT_SYMBOL(p9_client_read_once); @@ -1571,6 +1575,7 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err) while (iov_iter_count(from)) { int count = iov_iter_count(from); int rsize = fid->iounit; + int written; if (!rsize || rsize > clnt->msize - P9_IOHDRSZ) rsize = clnt->msize - P9_IOHDRSZ; @@ -1588,27 +1593,29 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err) offset, rsize, from); } if (IS_ERR(req)) { + iov_iter_revert(from, count - iov_iter_count(from)); *err = PTR_ERR(req); break; } - *err = p9pdu_readf(&req->rc, clnt->proto_version, "d", &count); + *err = p9pdu_readf(&req->rc, clnt->proto_version, "d", &written); if (*err) { + iov_iter_revert(from, count - iov_iter_count(from)); trace_9p_protocol_dump(clnt, &req->rc); p9_req_put(clnt, req); break; } - if (rsize < count) { - pr_err("bogus RWRITE count (%d > %d)\n", count, rsize); - count = rsize; + if (rsize < written) { + pr_err("bogus RWRITE count (%d > %d)\n", written, rsize); + written = rsize; } p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", count); p9_req_put(clnt, req); - iov_iter_advance(from, count); - total += count; - offset += count; + iov_iter_revert(from, count - written - iov_iter_count(from)); + total += written; + offset += written; } return total; } diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 3754c33e2974..83694c631989 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -63,9 +63,8 @@ static size_t pdu_write_u(struct p9_fcall *pdu, struct iov_iter *from, size_t size) { size_t len = min(pdu->capacity - pdu->size, size); - struct iov_iter i = *from; - if (!copy_from_iter_full(&pdu->sdata[pdu->size], len, &i)) + if (!copy_from_iter_full(&pdu->sdata[pdu->size], len, from)) len = 0; pdu->size += len; diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 03770addaa5c..b84d35cf6899 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -331,7 +331,7 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, if (err == -ERESTARTSYS) return err; } - n = iov_iter_get_pages_alloc(data, pages, count, offs); + n = iov_iter_get_pages_alloc2(data, pages, count, offs); if (n < 0) return n; *need_drop = 1; @@ -373,6 +373,7 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, (*pages)[index] = kmap_to_page(p); p += PAGE_SIZE; } + iov_iter_advance(data, len); return len; } } diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index 85865ebfdfa2..9f7cb0a7c73f 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -108,10 +108,12 @@ int ax25_t1timer_running(ax25_cb *ax25) unsigned long ax25_display_timer(struct timer_list *timer) { + long delta = timer->expires - jiffies; + if (!timer_pending(timer)) return 0; - return timer->expires - jiffies; + return max(0L, delta); } EXPORT_SYMBOL(ax25_display_timer); diff --git a/net/bluetooth/aosp.c b/net/bluetooth/aosp.c index 432ae3aac9e3..1d67836e95e1 100644 --- a/net/bluetooth/aosp.c +++ b/net/bluetooth/aosp.c @@ -54,7 +54,10 @@ void aosp_do_open(struct hci_dev *hdev) /* LE Get Vendor Capabilities Command */ skb = __hci_cmd_sync(hdev, hci_opcode_pack(0x3f, 0x153), 0, NULL, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "AOSP get vendor capabilities (%ld)", PTR_ERR(skb)); return; @@ -152,7 +155,10 @@ static int enable_quality_report(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Enabling Android BQR failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -171,7 +177,10 @@ static int disable_quality_report(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Disabling Android BQR failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f54864e19866..9777e7b109ee 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1551,8 +1551,8 @@ static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) cis->cis_id = qos->cis; cis->c_sdu = cpu_to_le16(qos->out.sdu); cis->p_sdu = cpu_to_le16(qos->in.sdu); - cis->c_phy = qos->out.phy; - cis->p_phy = qos->in.phy; + cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy; + cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy; cis->c_rtn = qos->out.rtn; cis->p_rtn = qos->in.rtn; @@ -1735,13 +1735,6 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, if (!qos->in.latency) qos->in.latency = qos->out.latency; - /* Mirror PHYs that are disabled as SDU will be set to 0 */ - if (!qos->in.phy) - qos->in.phy = qos->out.phy; - - if (!qos->out.phy) - qos->out.phy = qos->in.phy; - if (!hci_le_set_cig_params(cis, qos)) { hci_conn_drop(cis); return ERR_PTR(-EINVAL); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea33dd0cd478..485c814cf44a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -328,14 +328,17 @@ static u8 hci_cc_delete_stored_link_key(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct hci_rp_delete_stored_link_key *rp = data; + u16 num_keys; bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); if (rp->status) return rp->status; - if (rp->num_keys <= hdev->stored_num_keys) - hdev->stored_num_keys -= le16_to_cpu(rp->num_keys); + num_keys = le16_to_cpu(rp->num_keys); + + if (num_keys <= hdev->stored_num_keys) + hdev->stored_num_keys -= num_keys; else hdev->stored_num_keys = 0; diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index ff09c353e64e..ced8ad4fed4f 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -44,6 +44,9 @@ static void iso_sock_kill(struct sock *sk); /* ----- ISO socket info ----- */ #define iso_pi(sk) ((struct iso_pinfo *)sk) +#define EIR_SERVICE_DATA_LENGTH 4 +#define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH) + struct iso_pinfo { struct bt_sock bt; bdaddr_t src; @@ -57,7 +60,7 @@ struct iso_pinfo { __u32 flags; struct bt_iso_qos qos; __u8 base_len; - __u8 base[HCI_MAX_PER_AD_LENGTH]; + __u8 base[BASE_MAX_LENGTH]; struct iso_conn *conn; }; @@ -370,15 +373,24 @@ done: return err; } +static struct bt_iso_qos *iso_sock_get_qos(struct sock *sk) +{ + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONNECT2) + return &iso_pi(sk)->conn->hcon->iso_qos; + + return &iso_pi(sk)->qos; +} + static int iso_send_frame(struct sock *sk, struct sk_buff *skb) { struct iso_conn *conn = iso_pi(sk)->conn; + struct bt_iso_qos *qos = iso_sock_get_qos(sk); struct hci_iso_data_hdr *hdr; int len = 0; BT_DBG("sk %p len %d", sk, skb->len); - if (skb->len > iso_pi(sk)->qos.out.sdu) + if (skb->len > qos->out.sdu) return -EMSGSIZE; len = skb->len; @@ -1177,8 +1189,10 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, } len = min_t(unsigned int, sizeof(qos), optlen); - if (len != sizeof(qos)) - return -EINVAL; + if (len != sizeof(qos)) { + err = -EINVAL; + break; + } memset(&qos, 0, sizeof(qos)); @@ -1233,7 +1247,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; int len, err = 0; - struct bt_iso_qos qos; + struct bt_iso_qos *qos; u8 base_len; u8 *base; @@ -1246,7 +1260,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + if (sk->sk_state == BT_CONNECTED) { err = -EINVAL; break; } @@ -1258,13 +1272,10 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_ISO_QOS: - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONNECT2) - qos = iso_pi(sk)->conn->hcon->iso_qos; - else - qos = iso_pi(sk)->qos; + qos = iso_sock_get_qos(sk); - len = min_t(unsigned int, len, sizeof(qos)); - if (copy_to_user(optval, (char *)&qos, len)) + len = min_t(unsigned int, len, sizeof(*qos)); + if (copy_to_user(optval, qos, len)) err = -EFAULT; break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 77c0aac14539..cbe0cae73434 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1970,11 +1970,11 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *dst, u8 link_type) { - struct l2cap_chan *c, *c1 = NULL; + struct l2cap_chan *c, *tmp, *c1 = NULL; read_lock(&chan_list_lock); - list_for_each_entry(c, &chan_list, global_l) { + list_for_each_entry_safe(c, tmp, &chan_list, global_l) { if (state && c->state != state) continue; @@ -1993,11 +1993,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, dst_match = !bacmp(&c->dst, dst); if (src_match && dst_match) { c = l2cap_chan_hold_unless_zero(c); - if (!c) - continue; - - read_unlock(&chan_list_lock); - return c; + if (c) { + read_unlock(&chan_list_lock); + return c; + } } /* Closest match */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 646d10401b80..6e31023b84f5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3819,7 +3819,7 @@ static int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_blocked_keys_clear(hdev); - for (i = 0; i < keys->key_count; ++i) { + for (i = 0; i < key_count; ++i) { struct blocked_key *b = kzalloc(sizeof(*b), GFP_KERNEL); if (!b) { @@ -4624,8 +4624,7 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data, u32 current_flags = __le32_to_cpu(cp->current_flags); bt_dev_dbg(hdev, "Set device flags %pMR (type 0x%x) = 0x%x", - &cp->addr.bdaddr, cp->addr.type, - __le32_to_cpu(current_flags)); + &cp->addr.bdaddr, cp->addr.type, current_flags); // We should take hci_dev_lock() early, I think.. conn_flags can change supported_flags = hdev->conn_flags; @@ -8936,6 +8935,8 @@ void mgmt_index_removed(struct hci_dev *hdev) HCI_MGMT_EXT_INDEX_EVENTS); /* Cancel any remaining timed work */ + if (!hci_dev_test_flag(hdev, HCI_MGMT)) + return; cancel_delayed_work_sync(&hdev->discov_off); cancel_delayed_work_sync(&hdev->service_cache); cancel_delayed_work_sync(&hdev->rpa_expired); diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 14975769f678..bee6a4c656be 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -120,7 +120,10 @@ static bool read_supported_features(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)", PTR_ERR(skb)); return false; @@ -319,8 +322,11 @@ static int msft_remove_monitor_sync(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + return -EIO; return PTR_ERR(skb); + } return msft_le_cancel_monitor_advertisement_cb(hdev, hdev->msft_opcode, monitor, skb); @@ -432,8 +438,11 @@ static int msft_add_monitor_sync(struct hci_dev *hdev, HCI_CMD_TIMEOUT); kfree(cp); - if (IS_ERR(skb)) + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + return -EIO; return PTR_ERR(skb); + } return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode, monitor, skb); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index cbc9cd5058cb..d11209367dd0 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -1628,6 +1628,7 @@ static int __init bpf_prog_test_run_init(void) int ret; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_prog_test_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc, ARRAY_SIZE(bpf_prog_test_dtor_kfunc), THIS_MODULE); diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index f5ecfdcf57b2..b670ba03a675 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -178,7 +178,10 @@ activate_next: if (!first) return; - if (WARN_ON_ONCE(j1939_session_activate(first))) { + if (j1939_session_activate(first)) { + netdev_warn_once(first->priv->ndev, + "%s: 0x%p: Identical session is already activated.\n", + __func__, first); first->err = -EBUSY; goto activate_next; } else { diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 307ee1174a6e..d7d86c944d76 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -260,6 +260,8 @@ static void __j1939_session_drop(struct j1939_session *session) static void j1939_session_destroy(struct j1939_session *session) { + struct sk_buff *skb; + if (session->transmission) { if (session->err) j1939_sk_errqueue(session, J1939_ERRQUEUE_TX_ABORT); @@ -274,7 +276,11 @@ static void j1939_session_destroy(struct j1939_session *session) WARN_ON_ONCE(!list_empty(&session->sk_session_queue_entry)); WARN_ON_ONCE(!list_empty(&session->active_session_list_entry)); - skb_queue_purge(&session->skb_queue); + while ((skb = skb_dequeue(&session->skb_queue)) != NULL) { + /* drop ref taken in j1939_session_skb_queue() */ + skb_unref(skb); + kfree_skb(skb); + } __j1939_session_drop(session); j1939_priv_put(session->priv); kfree(session); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 9d82bb42e958..87b883c7bfd6 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -4578,15 +4578,12 @@ bad: /* * Register request, send initial attempt. */ -int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail) +void ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) { down_read(&osdc->lock); submit_request(req, false); up_read(&osdc->lock); - - return 0; } EXPORT_SYMBOL(ceph_osdc_start_request); @@ -4756,7 +4753,7 @@ int ceph_osdc_unwatch(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); linger_cancel(lreq); linger_put(lreq); ret = wait_request_timeout(req, opts->mount_timeout); @@ -4827,7 +4824,7 @@ int ceph_osdc_notify_ack(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); out_put_req: @@ -5043,7 +5040,7 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) { void *p = page_address(pages[0]); @@ -5120,7 +5117,7 @@ int ceph_osdc_call(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) { ret = req->r_ops[0].rval; diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 2823bb3cff55..295098873861 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -11,6 +11,22 @@ #include <linux/crush/hash.h> #include <linux/crush/mapper.h> +static __printf(2, 3) +void osdmap_info(const struct ceph_osdmap *map, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_INFO "%s (%pU e%u): %pV", KBUILD_MODNAME, &map->fsid, + map->epoch, &vaf); + + va_end(args); +} + char *ceph_osdmap_state_str(char *str, int len, u32 state) { if (!len) @@ -571,10 +587,10 @@ static struct crush_map *crush_decode(void *pbyval, void *end) goto bad; #endif r = kmalloc(struct_size(r, steps, yes), GFP_NOFS); - c->rules[i] = r; if (r == NULL) goto badmem; dout(" rule %d is at %p\n", i, r); + c->rules[i] = r; r->len = yes; ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */ ceph_decode_need(p, end, r->len*3*sizeof(u32), bad); @@ -1566,7 +1582,7 @@ static int decode_new_primary_affinity(void **p, void *end, if (ret) return ret; - pr_info("osd%d primary-affinity 0x%x\n", osd, aff); + osdmap_info(map, "osd%d primary-affinity 0x%x\n", osd, aff); } return 0; @@ -1864,9 +1880,9 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, osd = ceph_decode_32(p); w = ceph_decode_32(p); BUG_ON(osd >= map->max_osd); - pr_info("osd%d weight 0x%x %s\n", osd, w, - w == CEPH_OSD_IN ? "(in)" : - (w == CEPH_OSD_OUT ? "(out)" : "")); + osdmap_info(map, "osd%d weight 0x%x %s\n", osd, w, + w == CEPH_OSD_IN ? "(in)" : + (w == CEPH_OSD_OUT ? "(out)" : "")); map->osd_weight[osd] = w; /* @@ -1898,10 +1914,10 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, BUG_ON(osd >= map->max_osd); if ((map->osd_state[osd] & CEPH_OSD_UP) && (xorstate & CEPH_OSD_UP)) - pr_info("osd%d down\n", osd); + osdmap_info(map, "osd%d down\n", osd); if ((map->osd_state[osd] & CEPH_OSD_EXISTS) && (xorstate & CEPH_OSD_EXISTS)) { - pr_info("osd%d does not exist\n", osd); + osdmap_info(map, "osd%d does not exist\n", osd); ret = set_primary_affinity(map, osd, CEPH_OSD_DEFAULT_PRIMARY_AFFINITY); if (ret) @@ -1931,7 +1947,7 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, dout("%s osd%d addr %s\n", __func__, osd, ceph_pr_addr(&addr)); - pr_info("osd%d up\n", osd); + osdmap_info(map, "osd%d up\n", osd); map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP; map->osd_addr[osd] = addr; } diff --git a/net/ceph/pagelist.c b/net/ceph/pagelist.c index 65e34f78b05d..74622b278d57 100644 --- a/net/ceph/pagelist.c +++ b/net/ceph/pagelist.c @@ -96,7 +96,7 @@ int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len) EXPORT_SYMBOL(ceph_pagelist_append); /* Allocate enough pages for a pagelist to append the given amount - * of data without without allocating. + * of data without allocating. * Returns: 0 on success, -ENOMEM on error. */ int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space) diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index a25ec93729b9..1b7f385643b4 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -875,10 +875,18 @@ static int bpf_iter_init_sk_storage_map(void *priv_data, { struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data; + bpf_map_inc_with_uref(aux->map); seq_info->map = aux->map; return 0; } +static void bpf_iter_fini_sk_storage_map(void *priv_data) +{ + struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data; + + bpf_map_put_with_uref(seq_info->map); +} + static int bpf_iter_attach_map(struct bpf_prog *prog, union bpf_iter_link_info *linfo, struct bpf_iter_aux_info *aux) @@ -896,7 +904,7 @@ static int bpf_iter_attach_map(struct bpf_prog *prog, if (map->map_type != BPF_MAP_TYPE_SK_STORAGE) goto put_map; - if (prog->aux->max_rdonly_access > map->value_size) { + if (prog->aux->max_rdwr_access > map->value_size) { err = -EACCES; goto put_map; } @@ -924,7 +932,7 @@ static const struct seq_operations bpf_sk_storage_map_seq_ops = { static const struct bpf_iter_seq_info iter_seq_info = { .seq_ops = &bpf_sk_storage_map_seq_ops, .init_seq_private = bpf_iter_init_sk_storage_map, - .fini_seq_private = NULL, + .fini_seq_private = bpf_iter_fini_sk_storage_map, .seq_priv_size = sizeof(struct bpf_iter_seq_sk_storage_map_info), }; diff --git a/net/core/datagram.c b/net/core/datagram.c index f3988ef8e9af..7255531f63ae 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -632,12 +632,11 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk, if (frag == MAX_SKB_FRAGS) return -EMSGSIZE; - copied = iov_iter_get_pages(from, pages, length, + copied = iov_iter_get_pages2(from, pages, length, MAX_SKB_FRAGS - frag, &start); if (copied < 0) return -EFAULT; - iov_iter_advance(from, copied); length -= copied; truesize = PAGE_ALIGN(copied + start); diff --git a/net/core/devlink.c b/net/core/devlink.c index 5da5c7cca98a..b50bcc18b8d9 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -5147,7 +5147,7 @@ static int devlink_param_get(struct devlink *devlink, const struct devlink_param *param, struct devlink_param_gset_ctx *ctx) { - if (!param->get) + if (!param->get || devlink->reload_failed) return -EOPNOTSUPP; return param->get(devlink, param->id, ctx); } @@ -5156,7 +5156,7 @@ static int devlink_param_set(struct devlink *devlink, const struct devlink_param *param, struct devlink_param_gset_ctx *ctx) { - if (!param->set) + if (!param->set || devlink->reload_failed) return -EOPNOTSUPP; return param->set(devlink, param->id, ctx); } diff --git a/net/core/filter.c b/net/core/filter.c index 5669248aff25..e8508aaafd27 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5063,7 +5063,10 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname, case SO_RCVLOWAT: if (val < 0) val = INT_MAX; - WRITE_ONCE(sk->sk_rcvlowat, val ? : 1); + if (sk->sk_socket && sk->sk_socket->ops->set_rcvlowat) + ret = sk->sk_socket->ops->set_rcvlowat(sk, val); + else + WRITE_ONCE(sk->sk_rcvlowat, val ? : 1); break; case SO_MARK: if (sk->sk_mark != val) { diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 81627892bdd4..f47338d89d5d 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -324,14 +324,13 @@ int sk_msg_zerocopy_from_iter(struct sock *sk, struct iov_iter *from, goto out; } - copied = iov_iter_get_pages(from, pages, bytes, maxpages, + copied = iov_iter_get_pages2(from, pages, bytes, maxpages, &offset); if (copied <= 0) { ret = -EFAULT; goto out; } - iov_iter_advance(from, copied); bytes -= copied; msg->sg.size += copied; @@ -739,7 +738,9 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node) sk_psock_set_state(psock, SK_PSOCK_TX_ENABLED); refcount_set(&psock->refcnt, 1); - rcu_assign_sk_user_data_nocopy(sk, psock); + __rcu_assign_sk_user_data_with_flags(sk, psock, + SK_USER_DATA_NOCOPY | + SK_USER_DATA_PSOCK); sock_hold(sk); out: diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 028813dfecb0..9a9fb9487d63 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -783,13 +783,22 @@ static int sock_map_init_seq_private(void *priv_data, { struct sock_map_seq_info *info = priv_data; + bpf_map_inc_with_uref(aux->map); info->map = aux->map; return 0; } +static void sock_map_fini_seq_private(void *priv_data) +{ + struct sock_map_seq_info *info = priv_data; + + bpf_map_put_with_uref(info->map); +} + static const struct bpf_iter_seq_info sock_map_iter_seq_info = { .seq_ops = &sock_map_seq_ops, .init_seq_private = sock_map_init_seq_private, + .fini_seq_private = sock_map_fini_seq_private, .seq_priv_size = sizeof(struct sock_map_seq_info), }; @@ -1369,18 +1378,27 @@ static const struct seq_operations sock_hash_seq_ops = { }; static int sock_hash_init_seq_private(void *priv_data, - struct bpf_iter_aux_info *aux) + struct bpf_iter_aux_info *aux) { struct sock_hash_seq_info *info = priv_data; + bpf_map_inc_with_uref(aux->map); info->map = aux->map; info->htab = container_of(aux->map, struct bpf_shtab, map); return 0; } +static void sock_hash_fini_seq_private(void *priv_data) +{ + struct sock_hash_seq_info *info = priv_data; + + bpf_map_put_with_uref(info->map); +} + static const struct bpf_iter_seq_info sock_hash_iter_seq_info = { .seq_ops = &sock_hash_seq_ops, .init_seq_private = sock_hash_init_seq_private, + .fini_seq_private = sock_hash_fini_seq_private, .seq_priv_size = sizeof(struct sock_hash_seq_info), }; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 897ca4f9b791..f152e51242cb 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1311,8 +1311,7 @@ struct dst_entry *ip6_dst_lookup_tunnel(struct sk_buff *skb, fl6.daddr = info->key.u.ipv6.dst; fl6.saddr = info->key.u.ipv6.src; prio = info->key.tos; - fl6.flowlabel = ip6_make_flowinfo(RT_TOS(prio), - info->key.label); + fl6.flowlabel = ip6_make_flowinfo(prio, info->key.label); dst = ipv6_stub->ipv6_dst_lookup_flow(net, sock->sk, &fl6, NULL); diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 2cd4a8d3b30a..b7de5e46fdd8 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -1614,7 +1614,7 @@ static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, * callback. If the callback is not available, then we skip to the next * attribute; otherwise, we call the destroy() callback. */ - for (i = 0; i < max_parsed; ++i) { + for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { if (!(parsed_attrs & SEG6_F_ATTR(i))) continue; @@ -1643,7 +1643,7 @@ static int parse_nla_optional_attrs(struct nlattr **attrs, struct seg6_action_param *param; int err, i; - for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; ++i) { if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) continue; @@ -1742,7 +1742,7 @@ static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) } /* parse the required attributes */ - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (desc->attrs & SEG6_F_ATTR(i)) { if (!attrs[i]) return -EINVAL; @@ -1847,7 +1847,7 @@ static int seg6_local_fill_encap(struct sk_buff *skb, attrs = slwt->desc->attrs | slwt->parsed_optattrs; - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (attrs & SEG6_F_ATTR(i)) { param = &seg6_action_params[i]; err = param->put(skb, slwt); @@ -1927,7 +1927,7 @@ static int seg6_local_cmp_encap(struct lwtunnel_state *a, if (attrs_a != attrs_b) return 1; - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (attrs_a & SEG6_F_ATTR(i)) { param = &seg6_action_params[i]; if (param->cmp(slwt_a, slwt_b)) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a3f1c1461874..da4257504fad 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1240,6 +1240,9 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk, info->limit > dfrag->data_len)) return 0; + if (unlikely(!__tcp_can_send(ssk))) + return -EAGAIN; + /* compute send limit */ info->mss_now = tcp_send_mss(ssk, &info->size_goal, info->flags); copy = info->size_goal; @@ -1413,7 +1416,8 @@ static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk) if (__mptcp_check_fallback(msk)) { if (!msk->first) return NULL; - return sk_stream_memory_free(msk->first) ? msk->first : NULL; + return __tcp_can_send(msk->first) && + sk_stream_memory_free(msk->first) ? msk->first : NULL; } /* re-use last subflow, if the burst allow that */ @@ -1564,6 +1568,8 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) ret = mptcp_sendmsg_frag(sk, ssk, dfrag, &info); if (ret <= 0) { + if (ret == -EAGAIN) + continue; mptcp_push_release(ssk, &info); goto out; } @@ -2769,30 +2775,16 @@ static void __mptcp_wr_shutdown(struct sock *sk) static void __mptcp_destroy_sock(struct sock *sk) { - struct mptcp_subflow_context *subflow, *tmp; struct mptcp_sock *msk = mptcp_sk(sk); - LIST_HEAD(conn_list); pr_debug("msk=%p", msk); might_sleep(); - /* join list will be eventually flushed (with rst) at sock lock release time*/ - list_splice_init(&msk->conn_list, &conn_list); - mptcp_stop_timer(sk); sk_stop_timer(sk, &sk->sk_timer); msk->pm.status = 0; - /* clears msk->subflow, allowing the following loop to close - * even the initial subflow - */ - mptcp_dispose_initial_subflow(msk); - list_for_each_entry_safe(subflow, tmp, &conn_list, node) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - __mptcp_close_ssk(sk, ssk, subflow, 0); - } - sk->sk_prot->destroy(sk); WARN_ON_ONCE(msk->rmem_fwd_alloc); @@ -2884,24 +2876,20 @@ static void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk) static int mptcp_disconnect(struct sock *sk, int flags) { - struct mptcp_subflow_context *subflow, *tmp; struct mptcp_sock *msk = mptcp_sk(sk); inet_sk_state_store(sk, TCP_CLOSE); - list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - - __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_FASTCLOSE); - } - mptcp_stop_timer(sk); sk_stop_timer(sk, &sk->sk_timer); if (mptcp_sk(sk)->token) mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); - mptcp_destroy_common(msk); + /* msk->subflow is still intact, the following will not free the first + * subflow + */ + mptcp_destroy_common(msk, MPTCP_CF_FASTCLOSE); msk->last_snd = NULL; WRITE_ONCE(msk->flags, 0); msk->cb_flags = 0; @@ -3051,12 +3039,17 @@ out: return newsk; } -void mptcp_destroy_common(struct mptcp_sock *msk) +void mptcp_destroy_common(struct mptcp_sock *msk, unsigned int flags) { + struct mptcp_subflow_context *subflow, *tmp; struct sock *sk = (struct sock *)msk; __mptcp_clear_xmit(sk); + /* join list will be eventually flushed (with rst) at sock lock release time */ + list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) + __mptcp_close_ssk(sk, mptcp_subflow_tcp_sock(subflow), subflow, flags); + /* move to sk_receive_queue, sk_stream_kill_queues will purge it */ mptcp_data_lock(sk); skb_queue_splice_tail_init(&msk->receive_queue, &sk->sk_receive_queue); @@ -3078,7 +3071,11 @@ static void mptcp_destroy(struct sock *sk) { struct mptcp_sock *msk = mptcp_sk(sk); - mptcp_destroy_common(msk); + /* clears msk->subflow, allowing the following to close + * even the initial subflow + */ + mptcp_dispose_initial_subflow(msk); + mptcp_destroy_common(msk, 0); sk_sockets_allocated_dec(sk); } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 5d6043c16b09..132d50833df1 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -624,16 +624,19 @@ void mptcp_info2sockaddr(const struct mptcp_addr_info *info, struct sockaddr_storage *addr, unsigned short family); -static inline bool __mptcp_subflow_active(struct mptcp_subflow_context *subflow) +static inline bool __tcp_can_send(const struct sock *ssk) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); + /* only send if our side has not closed yet */ + return ((1 << inet_sk_state_load(ssk)) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)); +} +static inline bool __mptcp_subflow_active(struct mptcp_subflow_context *subflow) +{ /* can't send if JOIN hasn't completed yet (i.e. is usable for mptcp) */ if (subflow->request_join && !subflow->fully_established) return false; - /* only send if our side has not closed yet */ - return ((1 << ssk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)); + return __tcp_can_send(mptcp_subflow_tcp_sock(subflow)); } void mptcp_subflow_set_active(struct mptcp_subflow_context *subflow); @@ -717,7 +720,7 @@ static inline void mptcp_write_space(struct sock *sk) } } -void mptcp_destroy_common(struct mptcp_sock *msk); +void mptcp_destroy_common(struct mptcp_sock *msk, unsigned int flags); #define MPTCP_TOKEN_MAX_RETRIES 4 diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 901c763dcdbb..c7d49fb6e7bd 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -621,7 +621,8 @@ static void mptcp_sock_destruct(struct sock *sk) sock_orphan(sk); } - mptcp_destroy_common(mptcp_sk(sk)); + /* We don't need to clear msk->subflow, as it's still NULL at this point */ + mptcp_destroy_common(mptcp_sk(sk), 0); inet_sock_destruct(sk); } diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index df6abbfe0079..22f15ebf6045 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -736,9 +736,8 @@ config NF_FLOW_TABLE config NF_FLOW_TABLE_PROCFS bool "Supply flow table statistics in procfs" - default y + depends on NF_FLOW_TABLE depends on PROC_FS - depends on SYSCTL help This option enables for the flow table offload statistics to be shown in procfs under net/netfilter/nf_flowtable. diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9f976b11d896..3cc88998b879 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, if (trans == NULL) return NULL; + INIT_LIST_HEAD(&trans->list); trans->msg_type = msg_type; trans->ctx = *ctx; @@ -2472,6 +2473,7 @@ err: } static struct nft_chain *nft_chain_lookup_byid(const struct net *net, + const struct nft_table *table, const struct nlattr *nla) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -2482,6 +2484,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net, struct nft_chain *chain = trans->ctx.chain; if (trans->msg_type == NFT_MSG_NEWCHAIN && + chain->table == table && id == nft_trans_chain_id(trans)) return chain; } @@ -3371,6 +3374,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) } static struct nft_rule *nft_rule_lookup_byid(const struct net *net, + const struct nft_chain *chain, const struct nlattr *nla); #define NFT_RULE_MAXEXPRS 128 @@ -3417,7 +3421,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, return -EOPNOTSUPP; } else if (nla[NFTA_RULE_CHAIN_ID]) { - chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]); + chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); if (IS_ERR(chain)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); return PTR_ERR(chain); @@ -3459,7 +3463,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, return PTR_ERR(old_rule); } } else if (nla[NFTA_RULE_POSITION_ID]) { - old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]); + old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]); if (IS_ERR(old_rule)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]); return PTR_ERR(old_rule); @@ -3604,6 +3608,7 @@ err_release_expr: } static struct nft_rule *nft_rule_lookup_byid(const struct net *net, + const struct nft_chain *chain, const struct nlattr *nla) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -3614,6 +3619,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net, struct nft_rule *rule = nft_trans_rule(trans); if (trans->msg_type == NFT_MSG_NEWRULE && + trans->ctx.chain == chain && id == nft_trans_rule_id(trans)) return rule; } @@ -3663,7 +3669,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info, err = nft_delrule(&ctx, rule); } else if (nla[NFTA_RULE_ID]) { - rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]); + rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]); if (IS_ERR(rule)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]); return PTR_ERR(rule); @@ -3842,6 +3848,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table, } static struct nft_set *nft_set_lookup_byid(const struct net *net, + const struct nft_table *table, const struct nlattr *nla, u8 genmask) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -3853,6 +3860,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net, struct nft_set *set = nft_trans_set(trans); if (id == nft_trans_set_id(trans) && + set->table == table && nft_active_genmask(set, genmask)) return set; } @@ -3873,7 +3881,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net, if (!nla_set_id) return set; - set = nft_set_lookup_byid(net, nla_set_id, genmask); + set = nft_set_lookup_byid(net, table, nla_set_id, genmask); } return set; } @@ -5195,19 +5203,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set, static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set, struct nft_data *key, struct nlattr *attr) { - struct nft_data_desc desc; - int err; - - err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr); - if (err < 0) - return err; - - if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) { - nft_data_release(key, desc.type); - return -EINVAL; - } + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = NFT_DATA_VALUE_MAXLEN, + .len = set->klen, + }; - return 0; + return nft_data_init(ctx, key, &desc, attr); } static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, @@ -5216,24 +5218,18 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, struct nlattr *attr) { u32 dtype; - int err; - - err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr); - if (err < 0) - return err; if (set->dtype == NFT_DATA_VERDICT) dtype = NFT_DATA_VERDICT; else dtype = NFT_DATA_VALUE; - if (dtype != desc->type || - set->dlen != desc->len) { - nft_data_release(data, desc->type); - return -EINVAL; - } + desc->type = dtype; + desc->size = NFT_DATA_VALUE_MAXLEN; + desc->len = set->dlen; + desc->flags = NFT_DATA_DESC_SETELEM; - return 0; + return nft_data_init(ctx, data, desc, attr); } static void *nft_setelem_catchall_get(const struct net *net, @@ -5467,6 +5463,27 @@ err_set_elem_expr: return ERR_PTR(err); } +static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len) +{ + len += nft_set_ext_types[id].len; + if (len > tmpl->ext_len[id] || + len > U8_MAX) + return -1; + + return 0; +} + +static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id, + void *to, const void *from, u32 len) +{ + if (nft_set_ext_check(tmpl, id, len) < 0) + return -1; + + memcpy(to, from, len); + + return 0; +} + void *nft_set_elem_init(const struct nft_set *set, const struct nft_set_ext_tmpl *tmpl, const u32 *key, const u32 *key_end, @@ -5477,17 +5494,26 @@ void *nft_set_elem_init(const struct nft_set *set, elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); if (elem == NULL) - return NULL; + return ERR_PTR(-ENOMEM); ext = nft_set_elem_ext(set, elem); nft_set_ext_init(ext, tmpl); - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY)) - memcpy(nft_set_ext_key(ext), key, set->klen); - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) - memcpy(nft_set_ext_key_end(ext), key_end, set->klen); - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) - memcpy(nft_set_ext_data(ext), data, set->dlen); + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY, + nft_set_ext_key(ext), key, set->klen) < 0) + goto err_ext_check; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END, + nft_set_ext_key_end(ext), key_end, set->klen) < 0) + goto err_ext_check; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA, + nft_set_ext_data(ext), data, set->dlen) < 0) + goto err_ext_check; + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration; if (expiration == 0) @@ -5497,6 +5523,11 @@ void *nft_set_elem_init(const struct nft_set *set, *nft_set_ext_timeout(ext) = timeout; return elem; + +err_ext_check: + kfree(elem); + + return ERR_PTR(-EINVAL); } static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, @@ -5584,14 +5615,25 @@ err_expr: } static int nft_set_elem_expr_setup(struct nft_ctx *ctx, + const struct nft_set_ext_tmpl *tmpl, const struct nft_set_ext *ext, struct nft_expr *expr_array[], u32 num_exprs) { struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + u32 len = sizeof(struct nft_set_elem_expr); struct nft_expr *expr; int i, err; + if (num_exprs == 0) + return 0; + + for (i = 0; i < num_exprs; i++) + len += expr_array[i]->ops->size; + + if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0) + return -EINVAL; + for (i = 0; i < num_exprs; i++) { expr = nft_setelem_expr_at(elem_expr, elem_expr->size); err = nft_expr_clone(expr, expr_array[i]); @@ -6054,17 +6096,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, } } - err = -ENOMEM; elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.key_end.val.data, elem.data.val.data, timeout, expiration, GFP_KERNEL_ACCOUNT); - if (elem.priv == NULL) + if (IS_ERR(elem.priv)) { + err = PTR_ERR(elem.priv); goto err_parse_data; + } ext = nft_set_elem_ext(set, elem.priv); if (flags) *nft_set_ext_flags(ext) = flags; + if (ulen > 0) { + if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) { + err = -EINVAL; + goto err_elem_userdata; + } udata = nft_set_ext_userdata(ext); udata->len = ulen - 1; nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); @@ -6073,14 +6121,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, *nft_set_ext_obj(ext) = obj; obj->use++; } - err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs); + err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs); if (err < 0) - goto err_elem_expr; + goto err_elem_free; trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) { err = -ENOMEM; - goto err_elem_expr; + goto err_elem_free; } ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; @@ -6126,10 +6174,10 @@ err_set_full: nft_setelem_remove(ctx->net, set, &elem); err_element_clash: kfree(trans); -err_elem_expr: +err_elem_free: if (obj) obj->use--; - +err_elem_userdata: nf_tables_set_elem_destroy(ctx, set, elem.priv); err_parse_data: if (nla[NFTA_SET_ELEM_DATA] != NULL) @@ -6311,8 +6359,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.key_end.val.data, NULL, 0, 0, GFP_KERNEL_ACCOUNT); - if (elem.priv == NULL) + if (IS_ERR(elem.priv)) { + err = PTR_ERR(elem.priv); goto fail_elem_key_end; + } ext = nft_set_elem_ext(set, elem.priv); if (flags) @@ -9605,7 +9655,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, tb[NFTA_VERDICT_CHAIN], genmask); } else if (tb[NFTA_VERDICT_CHAIN_ID]) { - chain = nft_chain_lookup_byid(ctx->net, + chain = nft_chain_lookup_byid(ctx->net, ctx->table, tb[NFTA_VERDICT_CHAIN_ID]); if (IS_ERR(chain)) return PTR_ERR(chain); @@ -9617,6 +9667,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, return PTR_ERR(chain); if (nft_is_base_chain(chain)) return -EOPNOTSUPP; + if (desc->flags & NFT_DATA_DESC_SETELEM && + chain->flags & NFT_CHAIN_BINDING) + return -EINVAL; chain->use++; data->verdict.chain = chain; @@ -9624,7 +9677,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, } desc->len = sizeof(data->verdict); - desc->type = NFT_DATA_VERDICT; + return 0; } @@ -9677,20 +9730,25 @@ nla_put_failure: } static int nft_value_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, - struct nft_data_desc *desc, const struct nlattr *nla) + struct nft_data *data, struct nft_data_desc *desc, + const struct nlattr *nla) { unsigned int len; len = nla_len(nla); if (len == 0) return -EINVAL; - if (len > size) + if (len > desc->size) return -EOVERFLOW; + if (desc->len) { + if (len != desc->len) + return -EINVAL; + } else { + desc->len = len; + } nla_memcpy(data->data, nla, len); - desc->type = NFT_DATA_VALUE; - desc->len = len; + return 0; } @@ -9710,7 +9768,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { * * @ctx: context of the expression using the data * @data: destination struct nft_data - * @size: maximum data length * @desc: data description * @nla: netlink attribute containing data * @@ -9720,24 +9777,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { * The caller can indicate that it only wants to accept data of type * NFT_DATA_VALUE by passing NULL for the ctx argument. */ -int nft_data_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, struct nft_data_desc *desc, const struct nlattr *nla) { struct nlattr *tb[NFTA_DATA_MAX + 1]; int err; + if (WARN_ON_ONCE(!desc->size)) + return -EINVAL; + err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla, nft_data_policy, NULL); if (err < 0) return err; - if (tb[NFTA_DATA_VALUE]) - return nft_value_init(ctx, data, size, desc, - tb[NFTA_DATA_VALUE]); - if (tb[NFTA_DATA_VERDICT] && ctx != NULL) - return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); - return -EINVAL; + if (tb[NFTA_DATA_VALUE]) { + if (desc->type != NFT_DATA_VALUE) + return -EINVAL; + + err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]); + } else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) { + if (desc->type != NFT_DATA_VERDICT) + return -EINVAL; + + err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); + } else { + err = -EINVAL; + } + + return err; } EXPORT_SYMBOL_GPL(nft_data_init); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 3ddce24ac76d..cee3e4e905ec 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -34,25 +34,23 @@ static noinline void __nft_trace_packet(struct nft_traceinfo *info, nft_trace_notify(info); } -static inline void nft_trace_packet(struct nft_traceinfo *info, +static inline void nft_trace_packet(const struct nft_pktinfo *pkt, + struct nft_traceinfo *info, const struct nft_chain *chain, const struct nft_rule_dp *rule, enum nft_trace_types type) { if (static_branch_unlikely(&nft_trace_enabled)) { - const struct nft_pktinfo *pkt = info->pkt; - info->nf_trace = pkt->skb->nf_trace; info->rule = rule; __nft_trace_packet(info, chain, type); } } -static inline void nft_trace_copy_nftrace(struct nft_traceinfo *info) +static inline void nft_trace_copy_nftrace(const struct nft_pktinfo *pkt, + struct nft_traceinfo *info) { if (static_branch_unlikely(&nft_trace_enabled)) { - const struct nft_pktinfo *pkt = info->pkt; - if (info->trace) info->nf_trace = pkt->skb->nf_trace; } @@ -96,7 +94,6 @@ static noinline void __nft_trace_verdict(struct nft_traceinfo *info, const struct nft_chain *chain, const struct nft_regs *regs) { - const struct nft_pktinfo *pkt = info->pkt; enum nft_trace_types type; switch (regs->verdict.code) { @@ -110,7 +107,9 @@ static noinline void __nft_trace_verdict(struct nft_traceinfo *info, break; default: type = NFT_TRACETYPE_RULE; - info->nf_trace = pkt->skb->nf_trace; + + if (info->trace) + info->nf_trace = info->pkt->skb->nf_trace; break; } @@ -271,10 +270,10 @@ next_rule: switch (regs.verdict.code) { case NFT_BREAK: regs.verdict.code = NFT_CONTINUE; - nft_trace_copy_nftrace(&info); + nft_trace_copy_nftrace(pkt, &info); continue; case NFT_CONTINUE: - nft_trace_packet(&info, chain, rule, + nft_trace_packet(pkt, &info, chain, rule, NFT_TRACETYPE_RULE); continue; } @@ -318,7 +317,7 @@ next_rule: goto next_rule; } - nft_trace_packet(&info, basechain, NULL, NFT_TRACETYPE_POLICY); + nft_trace_packet(pkt, &info, basechain, NULL, NFT_TRACETYPE_POLICY); if (static_branch_unlikely(&nft_counters_enabled)) nft_update_chain_stats(basechain, pkt); diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 83590afe3768..e6e402b247d0 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { static int nft_bitwise_init_bool(struct nft_bitwise *priv, const struct nlattr *const tb[]) { - struct nft_data_desc mask, xor; + struct nft_data_desc mask = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->mask), + .len = priv->len, + }; + struct nft_data_desc xor = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->xor), + .len = priv->len, + }; int err; if (tb[NFTA_BITWISE_DATA]) @@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv, !tb[NFTA_BITWISE_XOR]) return -EINVAL; - err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask, - tb[NFTA_BITWISE_MASK]); + err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]); if (err < 0) return err; - if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) { - err = -EINVAL; - goto err_mask_release; - } - err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor, - tb[NFTA_BITWISE_XOR]); + err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]); if (err < 0) - goto err_mask_release; - if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) { - err = -EINVAL; - goto err_xor_release; - } + goto err_xor_err; return 0; -err_xor_release: - nft_data_release(&priv->xor, xor.type); -err_mask_release: +err_xor_err: nft_data_release(&priv->mask, mask.type); + return err; } static int nft_bitwise_init_shift(struct nft_bitwise *priv, const struct nlattr *const tb[]) { - struct nft_data_desc d; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + .len = sizeof(u32), + }; int err; if (tb[NFTA_BITWISE_MASK] || @@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv, if (!tb[NFTA_BITWISE_DATA]) return -EINVAL; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d, - tb[NFTA_BITWISE_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]); if (err < 0) return err; - if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) || - priv->data.data[0] >= BITS_PER_TYPE(u32)) { - nft_data_release(&priv->data, d.type); + + if (priv->data.data[0] >= BITS_PER_TYPE(u32)) { + nft_data_release(&priv->data, desc.type); return -EINVAL; } @@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = { static int nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out) { - struct nft_data_desc desc; struct nft_data data; - int err = 0; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + .len = sizeof(u32), + }; + int err; - err = nft_data_init(NULL, &data, sizeof(data), &desc, tb); + err = nft_data_init(NULL, &data, &desc, tb); if (err < 0) return err; - if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) { - err = -EINVAL; - goto err; - } *out = data.data[0]; -err: - nft_data_release(&data, desc.type); - return err; + + return 0; } static int nft_bitwise_fast_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 777f09e4dc60..963cf831799c 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_cmp_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + }; int err; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; - if (desc.type != NFT_DATA_VALUE) { - err = -EINVAL; - nft_data_release(&priv->data, desc.type); - return err; - } - err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); if (err < 0) return err; @@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; struct nft_data data; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + }; int err; - err = nft_data_init(NULL, &data, sizeof(data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; @@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + }; int err; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; @@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = { static const struct nft_expr_ops * nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { - struct nft_data_desc desc; struct nft_data data; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + }; enum nft_cmp_ops op; u8 sreg; int err; @@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) return ERR_PTR(-EINVAL); } - err = nft_data_init(NULL, &data, sizeof(data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return ERR_PTR(err); - if (desc.type != NFT_DATA_VALUE) - goto err1; - sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) { @@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) return &nft_cmp16_fast_ops; } return &nft_cmp_ops; -err1: - nft_data_release(&data, desc.type); - return ERR_PTR(-EINVAL); } struct nft_expr_type nft_cmp_type __read_mostly = { diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 22f70b543fa2..6983e6ddeef9 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, ®s->data[priv->sreg_key], NULL, ®s->data[priv->sreg_data], timeout, 0, GFP_ATOMIC); - if (elem == NULL) + if (IS_ERR(elem)) goto err1; ext = nft_set_elem_ext(set, elem); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index b80f7b507349..5f28b21abc7d 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, }; +static enum nft_data_types nft_reg_to_type(const struct nlattr *nla) +{ + enum nft_data_types type; + u8 reg; + + reg = ntohl(nla_get_be32(nla)); + if (reg == NFT_REG_VERDICT) + type = NFT_DATA_VERDICT; + else + type = NFT_DATA_VALUE; + + return type; +} + static int nft_immediate_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_immediate_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .size = sizeof(priv->data), + }; int err; if (tb[NFTA_IMMEDIATE_DREG] == NULL || tb[NFTA_IMMEDIATE_DATA] == NULL) return -EINVAL; - err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_IMMEDIATE_DATA]); + desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]); + err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]); if (err < 0) return err; diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index 66f77484c227..832f0d725a9e 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr const struct nlattr * const tb[]) { struct nft_range_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc_from, desc_to; + struct nft_data_desc desc_from = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data_from), + }; + struct nft_data_desc desc_to = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data_to), + }; int err; u32 op; @@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr !tb[NFTA_RANGE_TO_DATA]) return -EINVAL; - err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from), - &desc_from, tb[NFTA_RANGE_FROM_DATA]); + err = nft_data_init(NULL, &priv->data_from, &desc_from, + tb[NFTA_RANGE_FROM_DATA]); if (err < 0) return err; - if (desc_from.type != NFT_DATA_VALUE) { - err = -EINVAL; - goto err1; - } - - err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to), - &desc_to, tb[NFTA_RANGE_TO_DATA]); + err = nft_data_init(NULL, &priv->data_to, &desc_to, + tb[NFTA_RANGE_TO_DATA]); if (err < 0) goto err1; - if (desc_to.type != NFT_DATA_VALUE) { - err = -EINVAL; - goto err2; - } - if (desc_from.len != desc_to.len) { err = -EINVAL; goto err2; diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 8490e46359ae..0555dffd80e0 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -885,7 +885,7 @@ static int netlbl_unlabel_staticadd(struct sk_buff *skb, /* Don't allow users to add both IPv4 and IPv6 addresses for a * single entry. However, allow users to create two entries, one each - * for IPv4 and IPv4, with the same LSM security context which should + * for IPv4 and IPv6, with the same LSM security context which should * achieve the same result. */ if (!info->attrs[NLBL_UNLABEL_A_SECCTX] || !info->attrs[NLBL_UNLABEL_A_IFACE] || diff --git a/net/rds/message.c b/net/rds/message.c index 799034e0f513..d74be4e3f3fa 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -391,7 +391,7 @@ static int rds_message_zcopy_from_user(struct rds_message *rm, struct iov_iter * size_t start; ssize_t copied; - copied = iov_iter_get_pages(from, &pages, PAGE_SIZE, + copied = iov_iter_get_pages2(from, &pages, PAGE_SIZE, 1, &start); if (copied < 0) { struct mmpin *mmp; @@ -405,7 +405,6 @@ static int rds_message_zcopy_from_user(struct rds_message *rm, struct iov_iter * goto err; } total_copied += copied; - iov_iter_advance(from, copied); length -= copied; sg_set_page(sg, pages, copied, start); rm->data.op_nents++; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index a35ab8c27866..3f935cbbaff6 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -526,7 +526,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, rcu_assign_pointer(f->next, f1); rcu_assign_pointer(*fp, f); - if (fold && fold->handle && f->handle != fold->handle) { + if (fold) { th = to_hash(fold->handle); h = from_hash(fold->handle >> 16); b = rtnl_dereference(head->table[th]); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index cc6eabee2830..d47b9689eba6 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -427,14 +427,10 @@ void __qdisc_run(struct Qdisc *q) unsigned long dev_trans_start(struct net_device *dev) { - unsigned long val, res; + unsigned long res = READ_ONCE(netdev_get_tx_queue(dev, 0)->trans_start); + unsigned long val; unsigned int i; - if (is_vlan_dev(dev)) - dev = vlan_dev_real_dev(dev); - else if (netif_is_macvlan(dev)) - dev = macvlan_dev_real_dev(dev); - res = READ_ONCE(netdev_get_tx_queue(dev, 0)->trans_start); for (i = 1; i < dev->num_tx_queues; i++) { val = READ_ONCE(netdev_get_tx_queue(dev, i)->trans_start); if (val && time_after(val, res)) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 04e7b55fe0d9..fb75a883503f 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -445,7 +445,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan) * Enforce a 60 second garbage collection moratorium * Note that the cred_unused list must be time-ordered. */ - if (!time_in_range(cred->cr_expire, expired, jiffies)) + if (time_in_range(cred->cr_expire, expired, jiffies)) continue; if (!rpcauth_unhash_cred(cred)) continue; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index de7e5b41ab8f..a31a27816cc0 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1340,14 +1340,11 @@ gss_hash_cred(struct auth_cred *acred, unsigned int hashbits) /* * Lookup RPCSEC_GSS cred for the current process */ -static struct rpc_cred * -gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) +static struct rpc_cred *gss_lookup_cred(struct rpc_auth *auth, + struct auth_cred *acred, int flags) { - gfp_t gfp = GFP_KERNEL; - - if (flags & RPCAUTH_LOOKUP_ASYNC) - gfp = GFP_NOWAIT | __GFP_NOWARN; - return rpcauth_lookup_credcache(auth, acred, flags, gfp); + return rpcauth_lookup_credcache(auth, acred, flags, + rpc_task_gfp_mask()); } static struct rpc_cred * diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 253a54c2fcfe..65a6c6429a53 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -50,6 +50,17 @@ static void xprt_free_allocation(struct rpc_rqst *req) kfree(req); } +static void xprt_bc_reinit_xdr_buf(struct xdr_buf *buf) +{ + buf->head[0].iov_len = PAGE_SIZE; + buf->tail[0].iov_len = 0; + buf->pages = NULL; + buf->page_len = 0; + buf->flags = 0; + buf->len = 0; + buf->buflen = PAGE_SIZE; +} + static int xprt_alloc_xdr_buf(struct xdr_buf *buf, gfp_t gfp_flags) { struct page *page; @@ -278,6 +289,9 @@ void xprt_free_bc_rqst(struct rpc_rqst *req) */ spin_lock_bh(&xprt->bc_pa_lock); if (xprt_need_to_requeue(xprt)) { + xprt_bc_reinit_xdr_buf(&req->rq_snd_buf); + xprt_bc_reinit_xdr_buf(&req->rq_rcv_buf); + req->rq_rcv_buf.len = PAGE_SIZE; list_add_tail(&req->rq_bc_pa_list, &xprt->bc_pa_list); xprt->bc_alloc_count++; atomic_inc(&xprt->bc_slot_count); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b6781ada3aa8..b098e707ad41 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -786,7 +786,8 @@ out_revert: EXPORT_SYMBOL_GPL(rpc_switch_client_transport); static -int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi) +int _rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi, + void func(struct rpc_xprt_iter *xpi, struct rpc_xprt_switch *xps)) { struct rpc_xprt_switch *xps; @@ -795,11 +796,24 @@ int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi) rcu_read_unlock(); if (xps == NULL) return -EAGAIN; - xprt_iter_init_listall(xpi, xps); + func(xpi, xps); xprt_switch_put(xps); return 0; } +static +int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi) +{ + return _rpc_clnt_xprt_iter_init(clnt, xpi, xprt_iter_init_listall); +} + +static +int rpc_clnt_xprt_iter_offline_init(struct rpc_clnt *clnt, + struct rpc_xprt_iter *xpi) +{ + return _rpc_clnt_xprt_iter_init(clnt, xpi, xprt_iter_init_listoffline); +} + /** * rpc_clnt_iterate_for_each_xprt - Apply a function to all transports * @clnt: pointer to client @@ -1856,7 +1870,6 @@ rpc_xdr_encode(struct rpc_task *task) req->rq_snd_buf.head[0].iov_len = 0; xdr_init_encode(&xdr, &req->rq_snd_buf, req->rq_snd_buf.head[0].iov_base, req); - xdr_free_bvec(&req->rq_snd_buf); if (rpc_encode_header(task, &xdr)) return; @@ -2137,7 +2150,8 @@ call_connect_status(struct rpc_task *task) xprt_release(task); value = atomic_long_dec_return(&xprt->queuelen); if (value == 0) - rpc_xprt_switch_remove_xprt(xps, saved); + rpc_xprt_switch_remove_xprt(xps, saved, + true); xprt_put(saved); task->tk_xprt = NULL; task->tk_action = call_start; @@ -2650,7 +2664,7 @@ out_unparsable: out_verifier: trace_rpc_bad_verifier(task); - goto out_garbage; + goto out_err; out_msg_denied: error = -EACCES; @@ -2866,6 +2880,30 @@ success: } EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt); +static int rpc_clnt_add_xprt_helper(struct rpc_clnt *clnt, + struct rpc_xprt *xprt, + struct rpc_add_xprt_test *data) +{ + struct rpc_task *task; + int status = -EADDRINUSE; + + /* Test the connection */ + task = rpc_call_null_helper(clnt, xprt, NULL, 0, NULL, NULL); + if (IS_ERR(task)) + return PTR_ERR(task); + + status = task->tk_status; + rpc_put_task(task); + + if (status < 0) + return status; + + /* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */ + data->add_xprt_test(clnt, xprt, data->data); + + return 0; +} + /** * rpc_clnt_setup_test_and_add_xprt() * @@ -2889,8 +2927,6 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *data) { - struct rpc_task *task; - struct rpc_add_xprt_test *xtest = (struct rpc_add_xprt_test *)data; int status = -EADDRINUSE; xprt = xprt_get(xprt); @@ -2899,31 +2935,19 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt, if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr)) goto out_err; - /* Test the connection */ - task = rpc_call_null_helper(clnt, xprt, NULL, 0, NULL, NULL); - if (IS_ERR(task)) { - status = PTR_ERR(task); - goto out_err; - } - status = task->tk_status; - rpc_put_task(task); - + status = rpc_clnt_add_xprt_helper(clnt, xprt, data); if (status < 0) goto out_err; - /* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */ - xtest->add_xprt_test(clnt, xprt, xtest->data); - - xprt_put(xprt); - xprt_switch_put(xps); - - /* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */ - return 1; + status = 1; out_err: xprt_put(xprt); xprt_switch_put(xps); - pr_info("RPC: rpc_clnt_test_xprt failed: %d addr %s not added\n", - status, xprt->address_strings[RPC_DISPLAY_ADDR]); + if (status < 0) + pr_info("RPC: rpc_clnt_test_xprt failed: %d addr %s not " + "added\n", status, + xprt->address_strings[RPC_DISPLAY_ADDR]); + /* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */ return status; } EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt); @@ -3000,6 +3024,110 @@ out_put_switch: } EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt); +static int rpc_xprt_probe_trunked(struct rpc_clnt *clnt, + struct rpc_xprt *xprt, + struct rpc_add_xprt_test *data) +{ + struct rpc_xprt_switch *xps; + struct rpc_xprt *main_xprt; + int status = 0; + + xprt_get(xprt); + + rcu_read_lock(); + main_xprt = xprt_get(rcu_dereference(clnt->cl_xprt)); + xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch)); + status = rpc_cmp_addr_port((struct sockaddr *)&xprt->addr, + (struct sockaddr *)&main_xprt->addr); + rcu_read_unlock(); + xprt_put(main_xprt); + if (status || !test_bit(XPRT_OFFLINE, &xprt->state)) + goto out; + + status = rpc_clnt_add_xprt_helper(clnt, xprt, data); +out: + xprt_put(xprt); + xprt_switch_put(xps); + return status; +} + +/* rpc_clnt_probe_trunked_xprt -- probe offlined transport for session trunking + * @clnt rpc_clnt structure + * + * For each offlined transport found in the rpc_clnt structure call + * the function rpc_xprt_probe_trunked() which will determine if this + * transport still belongs to the trunking group. + */ +void rpc_clnt_probe_trunked_xprts(struct rpc_clnt *clnt, + struct rpc_add_xprt_test *data) +{ + struct rpc_xprt_iter xpi; + int ret; + + ret = rpc_clnt_xprt_iter_offline_init(clnt, &xpi); + if (ret) + return; + for (;;) { + struct rpc_xprt *xprt = xprt_iter_get_next(&xpi); + + if (!xprt) + break; + ret = rpc_xprt_probe_trunked(clnt, xprt, data); + xprt_put(xprt); + if (ret < 0) + break; + xprt_iter_rewind(&xpi); + } + xprt_iter_destroy(&xpi); +} +EXPORT_SYMBOL_GPL(rpc_clnt_probe_trunked_xprts); + +static int rpc_xprt_offline(struct rpc_clnt *clnt, + struct rpc_xprt *xprt, + void *data) +{ + struct rpc_xprt *main_xprt; + struct rpc_xprt_switch *xps; + int err = 0; + + xprt_get(xprt); + + rcu_read_lock(); + main_xprt = xprt_get(rcu_dereference(clnt->cl_xprt)); + xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch)); + err = rpc_cmp_addr_port((struct sockaddr *)&xprt->addr, + (struct sockaddr *)&main_xprt->addr); + rcu_read_unlock(); + xprt_put(main_xprt); + if (err) + goto out; + + if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) { + err = -EINTR; + goto out; + } + xprt_set_offline_locked(xprt, xps); + + xprt_release_write(xprt, NULL); +out: + xprt_put(xprt); + xprt_switch_put(xps); + return err; +} + +/* rpc_clnt_manage_trunked_xprts -- offline trunked transports + * @clnt rpc_clnt structure + * + * For each active transport found in the rpc_clnt structure call + * the function rpc_xprt_offline() which will identify trunked transports + * and will mark them offline. + */ +void rpc_clnt_manage_trunked_xprts(struct rpc_clnt *clnt) +{ + rpc_clnt_iterate_for_each_xprt(clnt, rpc_xprt_offline, NULL); +} +EXPORT_SYMBOL_GPL(rpc_clnt_manage_trunked_xprts); + struct connect_timeout_data { unsigned long connect_timeout; unsigned long reconnect_timeout; @@ -3042,8 +3170,22 @@ void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt) } EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put); +void rpc_clnt_xprt_set_online(struct rpc_clnt *clnt, struct rpc_xprt *xprt) +{ + struct rpc_xprt_switch *xps; + + rcu_read_lock(); + xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch); + rcu_read_unlock(); + xprt_set_online_locked(xprt, xps); +} + void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) { + if (rpc_clnt_xprt_switch_has_addr(clnt, + (const struct sockaddr *)&xprt->addr)) { + return rpc_clnt_xprt_set_online(clnt, xprt); + } rcu_read_lock(); rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch), xprt); @@ -3051,6 +3193,19 @@ void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) } EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt); +void rpc_clnt_xprt_switch_remove_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) +{ + struct rpc_xprt_switch *xps; + + rcu_read_lock(); + xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch); + rpc_xprt_switch_remove_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch), + xprt, 0); + xps->xps_nunique_destaddr_xprts--; + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_remove_xprt); + bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, const struct sockaddr *sap) { diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 7f70c1e608b7..25b9221950ff 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -63,6 +63,7 @@ gfp_t rpc_task_gfp_mask(void) return GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN; return GFP_KERNEL; } +EXPORT_SYMBOL_GPL(rpc_task_gfp_mask); unsigned long rpc_task_timeout(const struct rpc_task *task) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 2c4dd7ca95b0..2106003645a7 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -691,7 +691,7 @@ static int svc_alloc_arg(struct svc_rqst *rqstp) set_current_state(TASK_RUNNING); return -EINTR; } - trace_svc_alloc_arg_err(pages); + trace_svc_alloc_arg_err(pages, ret); memalloc_retry_wait(GFP_KERNEL); } rqstp->rq_page_end = &rqstp->rq_pages[pages]; diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index a3a2f8aeb80e..7330eb9a70cf 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -314,32 +314,14 @@ static ssize_t rpc_sysfs_xprt_state_change(struct kobject *kobj, goto release_tasks; } if (offline) { - if (!test_and_set_bit(XPRT_OFFLINE, &xprt->state)) { - spin_lock(&xps->xps_lock); - xps->xps_nactive--; - spin_unlock(&xps->xps_lock); - } + xprt_set_offline_locked(xprt, xps); } else if (online) { - if (test_and_clear_bit(XPRT_OFFLINE, &xprt->state)) { - spin_lock(&xps->xps_lock); - xps->xps_nactive++; - spin_unlock(&xps->xps_lock); - } + xprt_set_online_locked(xprt, xps); } else if (remove) { - if (test_bit(XPRT_OFFLINE, &xprt->state)) { - if (!test_and_set_bit(XPRT_REMOVE, &xprt->state)) { - xprt_force_disconnect(xprt); - if (test_bit(XPRT_CONNECTED, &xprt->state)) { - if (!xprt->sending.qlen && - !xprt->pending.qlen && - !xprt->backlog.qlen && - !atomic_long_read(&xprt->queuelen)) - rpc_xprt_switch_remove_xprt(xps, xprt); - } - } - } else { + if (test_bit(XPRT_OFFLINE, &xprt->state)) + xprt_delete_locked(xprt, xps); + else count = -EINVAL; - } } release_tasks: diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 5d2b3e6979fb..482586c23fdd 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf, xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift); } +static void xdr_buf_head_shift_left(const struct xdr_buf *buf, + unsigned int base, unsigned int len, + unsigned int shift) +{ + const struct kvec *head = buf->head; + unsigned int bytes; + + if (!shift || !len) + return; + + if (shift > base) { + bytes = (shift - base); + if (bytes >= len) + return; + base += bytes; + len -= bytes; + } + + if (base < head->iov_len) { + bytes = min_t(unsigned int, len, head->iov_len - base); + memmove(head->iov_base + (base - shift), + head->iov_base + base, bytes); + base += bytes; + len -= bytes; + } + xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift); +} + /** * xdr_shrink_bufhead * @buf: xdr_buf @@ -1472,71 +1500,35 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) } EXPORT_SYMBOL_GPL(xdr_read_pages); -unsigned int xdr_align_data(struct xdr_stream *xdr, unsigned int offset, - unsigned int length) -{ - struct xdr_buf *buf = xdr->buf; - unsigned int from, bytes, len; - unsigned int shift; - - xdr_realign_pages(xdr); - from = xdr_page_pos(xdr); - - if (from >= buf->page_len + buf->tail->iov_len) - return 0; - if (from + buf->head->iov_len >= buf->len) - return 0; - - len = buf->len - buf->head->iov_len; - - /* We only shift data left! */ - if (WARN_ONCE(from < offset, "SUNRPC: misaligned data src=%u dst=%u\n", - from, offset)) - return 0; - if (WARN_ONCE(offset > buf->page_len, - "SUNRPC: buffer overflow. offset=%u, page_len=%u\n", - offset, buf->page_len)) - return 0; - - /* Move page data to the left */ - shift = from - offset; - xdr_buf_pages_shift_left(buf, from, len, shift); - - bytes = xdr_stream_remaining(xdr); - if (length > bytes) - length = bytes; - bytes -= length; - - xdr->buf->len -= shift; - xdr_set_page(xdr, offset + length, bytes); - return length; -} -EXPORT_SYMBOL_GPL(xdr_align_data); - -unsigned int xdr_expand_hole(struct xdr_stream *xdr, unsigned int offset, - unsigned int length) +/** + * xdr_set_pagelen - Sets the length of the XDR pages + * @xdr: pointer to xdr_stream struct + * @len: new length of the XDR page data + * + * Either grows or shrinks the length of the xdr pages by setting pagelen to + * @len bytes. When shrinking, any extra data is moved into buf->tail, whereas + * when growing any data beyond the current pointer is moved into the tail. + * + * Returns True if the operation was successful, and False otherwise. + */ +void xdr_set_pagelen(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; - unsigned int from, to, shift; - - xdr_realign_pages(xdr); - from = xdr_page_pos(xdr); - to = xdr_align_size(offset + length); - - /* Could the hole be behind us? */ - if (to > from) { - unsigned int buflen = buf->len - buf->head->iov_len; - shift = to - from; - xdr_buf_try_expand(buf, shift); - xdr_buf_pages_shift_right(buf, from, buflen, shift); - xdr_set_page(xdr, to, xdr_stream_remaining(xdr)); - } else if (to != from) - xdr_align_data(xdr, to, 0); - xdr_buf_pages_zero(buf, offset, length); + size_t remaining = xdr_stream_remaining(xdr); + size_t base = 0; - return length; + if (len < buf->page_len) { + base = buf->page_len - len; + xdr_shrink_pagelen(buf, len); + } else { + xdr_buf_head_shift_right(buf, xdr_stream_pos(xdr), + buf->page_len, remaining); + if (len > buf->page_len) + xdr_buf_try_expand(buf, len - buf->page_len); + } + xdr_set_tail_base(xdr, base, remaining); } -EXPORT_SYMBOL_GPL(xdr_expand_hole); +EXPORT_SYMBOL_GPL(xdr_set_pagelen); /** * xdr_enter_page - decode data from the XDR page @@ -1681,6 +1673,60 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, EXPORT_SYMBOL_GPL(xdr_stream_subsegment); /** + * xdr_stream_move_subsegment - Move part of a stream to another position + * @xdr: the source xdr_stream + * @offset: the source offset of the segment + * @target: the target offset of the segment + * @length: the number of bytes to move + * + * Moves @length bytes from @offset to @target in the xdr_stream, overwriting + * anything in its space. Returns the number of bytes in the segment. + */ +unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset, + unsigned int target, unsigned int length) +{ + struct xdr_buf buf; + unsigned int shift; + + if (offset < target) { + shift = target - offset; + if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0) + return 0; + xdr_buf_head_shift_right(&buf, 0, length, shift); + } else if (offset > target) { + shift = offset - target; + if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0) + return 0; + xdr_buf_head_shift_left(&buf, shift, length, shift); + } + return length; +} +EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment); + +/** + * xdr_stream_zero - zero out a portion of an xdr_stream + * @xdr: an xdr_stream to zero out + * @offset: the starting point in the stream + * @length: the number of bytes to zero + */ +unsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset, + unsigned int length) +{ + struct xdr_buf buf; + + if (xdr_buf_subsegment(xdr->buf, &buf, offset, length) < 0) + return 0; + if (buf.head[0].iov_len) + xdr_buf_iov_zero(buf.head, 0, buf.head[0].iov_len); + if (buf.page_len > 0) + xdr_buf_pages_zero(&buf, 0, buf.page_len); + if (buf.tail[0].iov_len) + xdr_buf_iov_zero(buf.tail, 0, buf.tail[0].iov_len); + return length; +} +EXPORT_SYMBOL_GPL(xdr_stream_zero); + +/** * xdr_buf_trim - lop at most "len" bytes off the end of "buf" * @buf: buf to be trimmed * @len: number of bytes to reduce "buf" by diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 86d62cffba0d..d71eec494826 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -73,7 +73,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net); static __be32 xprt_alloc_xid(struct rpc_xprt *xprt); static void xprt_destroy(struct rpc_xprt *xprt); static void xprt_request_init(struct rpc_task *task); -static int xprt_request_prepare(struct rpc_rqst *req); +static int xprt_request_prepare(struct rpc_rqst *req, struct xdr_buf *buf); static DEFINE_SPINLOCK(xprt_list_lock); static LIST_HEAD(xprt_list); @@ -1149,7 +1149,7 @@ xprt_request_enqueue_receive(struct rpc_task *task) if (!xprt_request_need_enqueue_receive(task, req)) return 0; - ret = xprt_request_prepare(task->tk_rqstp); + ret = xprt_request_prepare(task->tk_rqstp, &req->rq_rcv_buf); if (ret) return ret; spin_lock(&xprt->queue_lock); @@ -1179,8 +1179,11 @@ xprt_request_dequeue_receive_locked(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - if (test_and_clear_bit(RPC_TASK_NEED_RECV, &task->tk_runstate)) + if (test_and_clear_bit(RPC_TASK_NEED_RECV, &task->tk_runstate)) { xprt_request_rb_remove(req->rq_xprt, req); + xdr_free_bvec(&req->rq_rcv_buf); + req->rq_private_buf.bvec = NULL; + } } /** @@ -1336,8 +1339,14 @@ xprt_request_enqueue_transmit(struct rpc_task *task) { struct rpc_rqst *pos, *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; + int ret; if (xprt_request_need_enqueue_transmit(task, req)) { + ret = xprt_request_prepare(task->tk_rqstp, &req->rq_snd_buf); + if (ret) { + task->tk_status = ret; + return; + } req->rq_bytes_sent = 0; spin_lock(&xprt->queue_lock); /* @@ -1397,6 +1406,7 @@ xprt_request_dequeue_transmit_locked(struct rpc_task *task) } else list_del(&req->rq_xmit2); atomic_long_dec(&req->rq_xprt->xmit_queuelen); + xdr_free_bvec(&req->rq_snd_buf); } /** @@ -1433,8 +1443,6 @@ xprt_request_dequeue_xprt(struct rpc_task *task) test_bit(RPC_TASK_NEED_RECV, &task->tk_runstate) || xprt_is_pinned_rqst(req)) { spin_lock(&xprt->queue_lock); - xprt_request_dequeue_transmit_locked(task); - xprt_request_dequeue_receive_locked(task); while (xprt_is_pinned_rqst(req)) { set_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); spin_unlock(&xprt->queue_lock); @@ -1442,6 +1450,8 @@ xprt_request_dequeue_xprt(struct rpc_task *task) spin_lock(&xprt->queue_lock); clear_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); } + xprt_request_dequeue_transmit_locked(task); + xprt_request_dequeue_receive_locked(task); spin_unlock(&xprt->queue_lock); } } @@ -1449,18 +1459,19 @@ xprt_request_dequeue_xprt(struct rpc_task *task) /** * xprt_request_prepare - prepare an encoded request for transport * @req: pointer to rpc_rqst + * @buf: pointer to send/rcv xdr_buf * * Calls into the transport layer to do whatever is needed to prepare * the request for transmission or receive. * Returns error, or zero. */ static int -xprt_request_prepare(struct rpc_rqst *req) +xprt_request_prepare(struct rpc_rqst *req, struct xdr_buf *buf) { struct rpc_xprt *xprt = req->rq_xprt; if (xprt->ops->prepare_request) - return xprt->ops->prepare_request(req); + return xprt->ops->prepare_request(req, buf); return 0; } @@ -1961,8 +1972,6 @@ void xprt_release(struct rpc_task *task) spin_unlock(&xprt->transport_lock); if (req->rq_buffer) xprt->ops->buf_free(task); - xdr_free_bvec(&req->rq_rcv_buf); - xdr_free_bvec(&req->rq_snd_buf); if (req->rq_cred != NULL) put_rpccred(req->rq_cred); if (req->rq_release_snd_buf) @@ -2152,3 +2161,35 @@ void xprt_put(struct rpc_xprt *xprt) kref_put(&xprt->kref, xprt_destroy_kref); } EXPORT_SYMBOL_GPL(xprt_put); + +void xprt_set_offline_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps) +{ + if (!test_and_set_bit(XPRT_OFFLINE, &xprt->state)) { + spin_lock(&xps->xps_lock); + xps->xps_nactive--; + spin_unlock(&xps->xps_lock); + } +} + +void xprt_set_online_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps) +{ + if (test_and_clear_bit(XPRT_OFFLINE, &xprt->state)) { + spin_lock(&xps->xps_lock); + xps->xps_nactive++; + spin_unlock(&xps->xps_lock); + } +} + +void xprt_delete_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps) +{ + if (test_and_set_bit(XPRT_REMOVE, &xprt->state)) + return; + + xprt_force_disconnect(xprt); + if (!test_bit(XPRT_CONNECTED, &xprt->state)) + return; + + if (!xprt->sending.qlen && !xprt->pending.qlen && + !xprt->backlog.qlen && !atomic_long_read(&xprt->queuelen)) + rpc_xprt_switch_remove_xprt(xps, xprt, true); +} diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c index 1693f81aae37..685db598acbe 100644 --- a/net/sunrpc/xprtmultipath.c +++ b/net/sunrpc/xprtmultipath.c @@ -27,6 +27,7 @@ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct rpc_xprt_switch *xps, static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular; static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin; static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall; +static const struct rpc_xprt_iter_ops rpc_xprt_iter_listoffline; static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, struct rpc_xprt *xprt) @@ -61,11 +62,11 @@ void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps, } static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps, - struct rpc_xprt *xprt) + struct rpc_xprt *xprt, bool offline) { if (unlikely(xprt == NULL)) return; - if (!test_bit(XPRT_OFFLINE, &xprt->state)) + if (!test_bit(XPRT_OFFLINE, &xprt->state) && offline) xps->xps_nactive--; xps->xps_nxprts--; if (xps->xps_nxprts == 0) @@ -78,14 +79,15 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps, * rpc_xprt_switch_remove_xprt - Removes an rpc_xprt from a rpc_xprt_switch * @xps: pointer to struct rpc_xprt_switch * @xprt: pointer to struct rpc_xprt + * @offline: indicates if the xprt that's being removed is in an offline state * * Removes xprt from the list of struct rpc_xprt in xps. */ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps, - struct rpc_xprt *xprt) + struct rpc_xprt *xprt, bool offline) { spin_lock(&xps->xps_lock); - xprt_switch_remove_xprt_locked(xps, xprt); + xprt_switch_remove_xprt_locked(xps, xprt, offline); spin_unlock(&xps->xps_lock); xprt_put(xprt); } @@ -154,7 +156,7 @@ static void xprt_switch_free_entries(struct rpc_xprt_switch *xps) xprt = list_first_entry(&xps->xps_xprt_list, struct rpc_xprt, xprt_switch); - xprt_switch_remove_xprt_locked(xps, xprt); + xprt_switch_remove_xprt_locked(xps, xprt, true); spin_unlock(&xps->xps_lock); xprt_put(xprt); spin_lock(&xps->xps_lock); @@ -249,6 +251,18 @@ struct rpc_xprt *xprt_switch_find_first_entry(struct list_head *head) } static +struct rpc_xprt *xprt_switch_find_first_entry_offline(struct list_head *head) +{ + struct rpc_xprt *pos; + + list_for_each_entry_rcu(pos, head, xprt_switch) { + if (!xprt_is_active(pos)) + return pos; + } + return NULL; +} + +static struct rpc_xprt *xprt_iter_first_entry(struct rpc_xprt_iter *xpi) { struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch); @@ -259,8 +273,9 @@ struct rpc_xprt *xprt_iter_first_entry(struct rpc_xprt_iter *xpi) } static -struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head, - const struct rpc_xprt *cur) +struct rpc_xprt *_xprt_switch_find_current_entry(struct list_head *head, + const struct rpc_xprt *cur, + bool find_active) { struct rpc_xprt *pos; bool found = false; @@ -268,14 +283,25 @@ struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head, list_for_each_entry_rcu(pos, head, xprt_switch) { if (cur == pos) found = true; - if (found && xprt_is_active(pos)) + if (found && ((find_active && xprt_is_active(pos)) || + (!find_active && xprt_is_active(pos)))) return pos; } return NULL; } static -struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi) +struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head, + const struct rpc_xprt *cur) +{ + return _xprt_switch_find_current_entry(head, cur, true); +} + +static +struct rpc_xprt * _xprt_iter_current_entry(struct rpc_xprt_iter *xpi, + struct rpc_xprt *first_entry(struct list_head *head), + struct rpc_xprt *current_entry(struct list_head *head, + const struct rpc_xprt *cur)) { struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch); struct list_head *head; @@ -284,8 +310,30 @@ struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi) return NULL; head = &xps->xps_xprt_list; if (xpi->xpi_cursor == NULL || xps->xps_nxprts < 2) - return xprt_switch_find_first_entry(head); - return xprt_switch_find_current_entry(head, xpi->xpi_cursor); + return first_entry(head); + return current_entry(head, xpi->xpi_cursor); +} + +static +struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi) +{ + return _xprt_iter_current_entry(xpi, xprt_switch_find_first_entry, + xprt_switch_find_current_entry); +} + +static +struct rpc_xprt *xprt_switch_find_current_entry_offline(struct list_head *head, + const struct rpc_xprt *cur) +{ + return _xprt_switch_find_current_entry(head, cur, false); +} + +static +struct rpc_xprt *xprt_iter_current_entry_offline(struct rpc_xprt_iter *xpi) +{ + return _xprt_iter_current_entry(xpi, + xprt_switch_find_first_entry_offline, + xprt_switch_find_current_entry_offline); } bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps, @@ -310,7 +358,7 @@ bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps, static struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head, - const struct rpc_xprt *cur) + const struct rpc_xprt *cur, bool check_active) { struct rpc_xprt *pos, *prev = NULL; bool found = false; @@ -318,7 +366,12 @@ struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head, list_for_each_entry_rcu(pos, head, xprt_switch) { if (cur == prev) found = true; - if (found && xprt_is_active(pos)) + /* for request to return active transports return only + * active, for request to return offline transports + * return only offline + */ + if (found && ((check_active && xprt_is_active(pos)) || + (!check_active && !xprt_is_active(pos)))) return pos; prev = pos; } @@ -355,7 +408,7 @@ struct rpc_xprt *__xprt_switch_find_next_entry_roundrobin(struct list_head *head { struct rpc_xprt *ret; - ret = xprt_switch_find_next_entry(head, cur); + ret = xprt_switch_find_next_entry(head, cur, true); if (ret != NULL) return ret; return xprt_switch_find_first_entry(head); @@ -397,7 +450,14 @@ static struct rpc_xprt *xprt_switch_find_next_entry_all(struct rpc_xprt_switch *xps, const struct rpc_xprt *cur) { - return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur); + return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur, true); +} + +static +struct rpc_xprt *xprt_switch_find_next_entry_offline(struct rpc_xprt_switch *xps, + const struct rpc_xprt *cur) +{ + return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur, false); } static @@ -407,6 +467,13 @@ struct rpc_xprt *xprt_iter_next_entry_all(struct rpc_xprt_iter *xpi) xprt_switch_find_next_entry_all); } +static +struct rpc_xprt *xprt_iter_next_entry_offline(struct rpc_xprt_iter *xpi) +{ + return xprt_iter_next_entry_multiple(xpi, + xprt_switch_find_next_entry_offline); +} + /* * xprt_iter_rewind - Resets the xprt iterator * @xpi: pointer to rpc_xprt_iter @@ -414,7 +481,6 @@ struct rpc_xprt *xprt_iter_next_entry_all(struct rpc_xprt_iter *xpi) * Resets xpi to ensure that it points to the first entry in the list * of transports. */ -static void xprt_iter_rewind(struct rpc_xprt_iter *xpi) { rcu_read_lock(); @@ -460,6 +526,12 @@ void xprt_iter_init_listall(struct rpc_xprt_iter *xpi, __xprt_iter_init(xpi, xps, &rpc_xprt_iter_listall); } +void xprt_iter_init_listoffline(struct rpc_xprt_iter *xpi, + struct rpc_xprt_switch *xps) +{ + __xprt_iter_init(xpi, xps, &rpc_xprt_iter_listoffline); +} + /** * xprt_iter_xchg_switch - Atomically swap out the rpc_xprt_switch * @xpi: pointer to rpc_xprt_iter @@ -574,3 +646,10 @@ const struct rpc_xprt_iter_ops rpc_xprt_iter_listall = { .xpi_xprt = xprt_iter_current_entry, .xpi_next = xprt_iter_next_entry_all, }; + +static +const struct rpc_xprt_iter_ops rpc_xprt_iter_listoffline = { + .xpi_rewind = xprt_iter_default_rewind, + .xpi_xprt = xprt_iter_current_entry_offline, + .xpi_next = xprt_iter_next_entry_offline, +}; diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 6b7e10e5a141..bcb37b51adf6 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -571,11 +571,7 @@ xprt_rdma_allocate(struct rpc_task *task) struct rpc_rqst *rqst = task->tk_rqstp; struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt); struct rpcrdma_req *req = rpcr_to_rdmar(rqst); - gfp_t flags; - - flags = RPCRDMA_DEF_GFP; - if (RPC_IS_ASYNC(task)) - flags = GFP_NOWAIT | __GFP_NOWARN; + gfp_t flags = rpc_task_gfp_mask(); if (!rpcrdma_check_regbuf(r_xprt, req->rl_sendbuf, rqst->rq_callsize, flags)) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index fcdd0fca408e..e976007f4fd0 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -822,17 +822,9 @@ static int xs_stream_nospace(struct rpc_rqst *req, bool vm_wait) return ret; } -static int -xs_stream_prepare_request(struct rpc_rqst *req) +static int xs_stream_prepare_request(struct rpc_rqst *req, struct xdr_buf *buf) { - gfp_t gfp = rpc_task_gfp_mask(); - int ret; - - ret = xdr_alloc_bvec(&req->rq_snd_buf, gfp); - if (ret < 0) - return ret; - xdr_free_bvec(&req->rq_rcv_buf); - return xdr_alloc_bvec(&req->rq_rcv_buf, gfp); + return xdr_alloc_bvec(buf, rpc_task_gfp_mask()); } /* @@ -1378,7 +1370,7 @@ static void xs_udp_data_receive_workfn(struct work_struct *work) } /** - * xs_data_ready - "data ready" callback for UDP sockets + * xs_data_ready - "data ready" callback for sockets * @sk: socket with data to read * */ @@ -1386,11 +1378,13 @@ static void xs_data_ready(struct sock *sk) { struct rpc_xprt *xprt; - dprintk("RPC: xs_data_ready...\n"); xprt = xprt_from_sock(sk); if (xprt != NULL) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); + + trace_xs_data_ready(xprt); + transport->old_data_ready(sk); /* Any data means we had a useful conversation, so * then we don't need to delay the next reconnect diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index e3e6cf75aa03..0f983e5f7dde 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -71,7 +71,13 @@ static void tls_device_tx_del_task(struct work_struct *work) struct tls_offload_context_tx *offload_ctx = container_of(work, struct tls_offload_context_tx, destruct_work); struct tls_context *ctx = offload_ctx->ctx; - struct net_device *netdev = ctx->netdev; + struct net_device *netdev; + + /* Safe, because this is the destroy flow, refcount is 0, so + * tls_device_down can't store this field in parallel. + */ + netdev = rcu_dereference_protected(ctx->netdev, + !refcount_read(&ctx->refcount)); netdev->tlsdev_ops->tls_dev_del(netdev, ctx, TLS_OFFLOAD_CTX_DIR_TX); dev_put(netdev); @@ -81,6 +87,7 @@ static void tls_device_tx_del_task(struct work_struct *work) static void tls_device_queue_ctx_destruction(struct tls_context *ctx) { + struct net_device *netdev; unsigned long flags; bool async_cleanup; @@ -91,7 +98,14 @@ static void tls_device_queue_ctx_destruction(struct tls_context *ctx) } list_del(&ctx->list); /* Remove from tls_device_list / tls_device_down_list */ - async_cleanup = ctx->netdev && ctx->tx_conf == TLS_HW; + + /* Safe, because this is the destroy flow, refcount is 0, so + * tls_device_down can't store this field in parallel. + */ + netdev = rcu_dereference_protected(ctx->netdev, + !refcount_read(&ctx->refcount)); + + async_cleanup = netdev && ctx->tx_conf == TLS_HW; if (async_cleanup) { struct tls_offload_context_tx *offload_ctx = tls_offload_ctx_tx(ctx); @@ -229,7 +243,8 @@ static void tls_device_resync_tx(struct sock *sk, struct tls_context *tls_ctx, trace_tls_device_tx_resync_send(sk, seq, rcd_sn); down_read(&device_offload_lock); - netdev = tls_ctx->netdev; + netdev = rcu_dereference_protected(tls_ctx->netdev, + lockdep_is_held(&device_offload_lock)); if (netdev) err = netdev->tlsdev_ops->tls_dev_resync(netdev, sk, seq, rcd_sn, @@ -710,7 +725,7 @@ static void tls_device_resync_rx(struct tls_context *tls_ctx, trace_tls_device_rx_resync_send(sk, seq, rcd_sn, rx_ctx->resync_type); rcu_read_lock(); - netdev = READ_ONCE(tls_ctx->netdev); + netdev = rcu_dereference(tls_ctx->netdev); if (netdev) netdev->tlsdev_ops->tls_dev_resync(netdev, sk, seq, rcd_sn, TLS_OFFLOAD_CTX_DIR_RX); @@ -984,11 +999,17 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) int is_decrypted = skb->decrypted; int is_encrypted = !is_decrypted; struct sk_buff *skb_iter; + int left; + left = rxm->full_len - skb->len; /* Check if all the data is decrypted already */ - skb_walk_frags(skb, skb_iter) { + skb_iter = skb_shinfo(skb)->frag_list; + while (skb_iter && left > 0) { is_decrypted &= skb_iter->decrypted; is_encrypted &= !skb_iter->decrypted; + + left -= skb_iter->len; + skb_iter = skb_iter->next; } trace_tls_device_decrypted(sk, tcp_sk(sk)->copied_seq - rxm->full_len, @@ -1029,7 +1050,7 @@ static void tls_device_attach(struct tls_context *ctx, struct sock *sk, if (sk->sk_destruct != tls_device_sk_destruct) { refcount_set(&ctx->refcount, 1); dev_hold(netdev); - ctx->netdev = netdev; + RCU_INIT_POINTER(ctx->netdev, netdev); spin_lock_irq(&tls_device_lock); list_add_tail(&ctx->list, &tls_device_list); spin_unlock_irq(&tls_device_lock); @@ -1300,7 +1321,8 @@ void tls_device_offload_cleanup_rx(struct sock *sk) struct net_device *netdev; down_read(&device_offload_lock); - netdev = tls_ctx->netdev; + netdev = rcu_dereference_protected(tls_ctx->netdev, + lockdep_is_held(&device_offload_lock)); if (!netdev) goto out; @@ -1309,7 +1331,7 @@ void tls_device_offload_cleanup_rx(struct sock *sk) if (tls_ctx->tx_conf != TLS_HW) { dev_put(netdev); - tls_ctx->netdev = NULL; + rcu_assign_pointer(tls_ctx->netdev, NULL); } else { set_bit(TLS_RX_DEV_CLOSED, &tls_ctx->flags); } @@ -1329,7 +1351,11 @@ static int tls_device_down(struct net_device *netdev) spin_lock_irqsave(&tls_device_lock, flags); list_for_each_entry_safe(ctx, tmp, &tls_device_list, list) { - if (ctx->netdev != netdev || + struct net_device *ctx_netdev = + rcu_dereference_protected(ctx->netdev, + lockdep_is_held(&device_offload_lock)); + + if (ctx_netdev != netdev || !refcount_inc_not_zero(&ctx->refcount)) continue; @@ -1346,7 +1372,7 @@ static int tls_device_down(struct net_device *netdev) /* Stop the RX and TX resync. * tls_dev_resync must not be called after tls_dev_del. */ - WRITE_ONCE(ctx->netdev, NULL); + rcu_assign_pointer(ctx->netdev, NULL); /* Start skipping the RX resync logic completely. */ set_bit(TLS_RX_DEV_DEGRADED, &ctx->flags); diff --git a/net/tls/tls_device_fallback.c b/net/tls/tls_device_fallback.c index 618cee704217..7dfc8023e0f1 100644 --- a/net/tls/tls_device_fallback.c +++ b/net/tls/tls_device_fallback.c @@ -426,7 +426,8 @@ struct sk_buff *tls_validate_xmit_skb(struct sock *sk, struct net_device *dev, struct sk_buff *skb) { - if (dev == tls_get_ctx(sk)->netdev || netif_is_bond_master(dev)) + if (dev == rcu_dereference_bh(tls_get_ctx(sk)->netdev) || + netif_is_bond_master(dev)) return skb; return tls_sw_fallback(sk, skb); diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index f0b7c9122fba..9b79e334dbd9 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -41,7 +41,7 @@ static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) struct sk_buff *skb; int i, err, offset; - skb = alloc_skb_with_frags(0, strp->anchor->len, TLS_PAGE_ORDER, + skb = alloc_skb_with_frags(0, strp->stm.full_len, TLS_PAGE_ORDER, &err, strp->sk->sk_allocation); if (!skb) return NULL; diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 17db8c8811fa..f76119f62f1b 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1352,7 +1352,7 @@ static int tls_setup_from_iter(struct iov_iter *from, rc = -EFAULT; goto out; } - copied = iov_iter_get_pages(from, pages, + copied = iov_iter_get_pages2(from, pages, length, maxpages, &offset); if (copied <= 0) { @@ -1360,8 +1360,6 @@ static int tls_setup_from_iter(struct iov_iter *from, goto out; } - iov_iter_advance(from, copied); - length -= copied; size += copied; while (copied) { diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index f04abf662ec6..b4ee163154a6 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1286,6 +1286,7 @@ static void vsock_connect_timeout(struct work_struct *work) if (sk->sk_state == TCP_SYN_SENT && (sk->sk_shutdown != SHUTDOWN_MASK)) { sk->sk_state = TCP_CLOSE; + sk->sk_socket->state = SS_UNCONNECTED; sk->sk_err = ETIMEDOUT; sk_error_report(sk); vsock_transport_cancel_pkt(vsk); @@ -1391,7 +1392,14 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr, * timeout fires. */ sock_hold(sk); - schedule_delayed_work(&vsk->connect_work, timeout); + + /* If the timeout function is already scheduled, + * reschedule it, then ungrab the socket refcount to + * keep it balanced. + */ + if (mod_delayed_work(system_wq, &vsk->connect_work, + timeout)) + sock_put(sk); /* Skip ahead to preserve error code set above. */ goto out_wait; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 62c773cf1b8d..27fb2a0c4052 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -782,9 +782,11 @@ void __cfg80211_connect_result(struct net_device *dev, #endif if (cr->status == WLAN_STATUS_SUCCESS) { - for_each_valid_link(cr, link) { - if (WARN_ON_ONCE(!cr->links[link].bss)) - break; + if (!wiphy_to_rdev(wdev->wiphy)->ops->connect) { + for_each_valid_link(cr, link) { + if (WARN_ON_ONCE(!cr->links[link].bss)) + break; + } } for_each_valid_link(cr, link) { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 6bc2ac8d8146..3b55502b2965 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -719,6 +719,11 @@ static int x25_wait_for_connection_establishment(struct sock *sk) sk->sk_socket->state = SS_UNCONNECTED; break; } + rc = -ENOTCONN; + if (sk->sk_state == TCP_CLOSE) { + sk->sk_socket->state = SS_UNCONNECTED; + break; + } rc = 0; if (sk->sk_state != TCP_ESTABLISHED) { release_sock(sk); diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include index 0496efd6e117..a0ccceb22cf8 100644 --- a/scripts/Kconfig.include +++ b/scripts/Kconfig.include @@ -25,7 +25,7 @@ failure = $(if-success,$(1),n,y) # $(cc-option,<flag>) # Return y if the compiler supports <flag>, n otherwise -cc-option = $(success,mkdir .tmp_$$$$; trap "rm -rf .tmp_$$$$" EXIT; $(CC) -Werror $(CLANG_FLAGS) $(1) -c -x c /dev/null -o .tmp_$$$$/tmp.o) +cc-option = $(success,trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(CC) -Werror $(CLANG_FLAGS) $(1) -c -x c /dev/null -o .tmp_$$/tmp.o) # $(ld-option,<flag>) # Return y if the linker supports <flag>, n otherwise diff --git a/scripts/Makefile.build b/scripts/Makefile.build index cac070aee791..784f46d41959 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -358,9 +358,8 @@ $(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ; quiet_cmd_ar_builtin = AR $@ cmd_ar_builtin = rm -f $@; \ - echo $(patsubst $(obj)/%,%,$(real-prereqs)) | \ - sed -E 's:([^ ]+):$(obj)/\1:g' | \ - xargs $(AR) cDPrST $@ + $(if $(real-prereqs), printf "$(obj)/%s " $(patsubst $(obj)/%,%,$(real-prereqs)) | xargs) \ + $(AR) cDPrST $@ $(obj)/built-in.a: $(real-obj-y) FORCE $(call if_changed,ar_builtin) diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler index 86ecd2ac874c..94d0d40cddb3 100644 --- a/scripts/Makefile.compiler +++ b/scripts/Makefile.compiler @@ -21,8 +21,8 @@ TMPOUT = $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_$$$$ # automatically cleaned up. try-run = $(shell set -e; \ TMP=$(TMPOUT)/tmp; \ - mkdir -p $(TMPOUT); \ trap "rm -rf $(TMPOUT)" EXIT; \ + mkdir -p $(TMPOUT); \ if ($(1)) >/dev/null 2>&1; \ then echo "$(2)"; \ else echo "$(3)"; \ diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index 16a02e9237d3..a4c987c23750 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -18,6 +18,9 @@ INSTALL_MOD_DIR ?= extra dst := $(MODLIB)/$(INSTALL_MOD_DIR) endif +$(foreach x, % :, $(if $(findstring $x, $(dst)), \ + $(error module installation path cannot contain '$x'))) + suffix-y := suffix-$(CONFIG_MODULE_COMPRESS_GZIP) := .gz suffix-$(CONFIG_MODULE_COMPRESS_XZ) := .xz diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 77b612183c08..5017f6b2da80 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -56,7 +56,7 @@ rpm-pkg: $(MAKE) clean $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec $(call cmd,src_tar,$(KERNELPATH),kernel.spec) - +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz \ + +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE)-linux -ta $(KERNELPATH).tar.gz \ --define='_smp_mflags %{nil}' # binrpm-pkg @@ -66,7 +66,7 @@ binrpm-pkg: $(MAKE) -f $(srctree)/Makefile $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec +rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \ - $(UTS_MACHINE) -bb $(objtree)/binkernel.spec + $(UTS_MACHINE)-linux -bb $(objtree)/binkernel.spec PHONY += deb-pkg deb-pkg: diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index d2c38584ece6..d48dfed6d3db 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -16,6 +16,7 @@ # AArch64, PARISC ports by Kyle McMartin # sparc port by Martin Habets <errandir_news@mph.eclipse.co.uk> # ppc64le port by Breno Leitao <leitao@debian.org> +# riscv port by Wadim Mueller <wafgo01@gmail.com> # # Usage: # objdump -d vmlinux | scripts/checkstack.pl [arch] @@ -108,6 +109,9 @@ my (@stack, $re, $dre, $sub, $x, $xs, $funcre, $min_stack); } elsif ($arch eq 'sparc' || $arch eq 'sparc64') { # f0019d10: 9d e3 bf 90 save %sp, -112, %sp $re = qr/.*save.*%sp, -(([0-9]{2}|[3-9])[0-9]{2}), %sp/o; + } elsif ($arch =~ /^riscv(64)?$/) { + #ffffffff8036e868: c2010113 addi sp,sp,-992 + $re = qr/.*addi.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o; } else { print("wrong or unknown architecture \"$arch\"\n"); exit diff --git a/scripts/dummy-tools/dummy-plugin-dir/include/plugin-version.h b/scripts/dummy-tools/dummy-plugin-dir/include/plugin-version.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/scripts/dummy-tools/dummy-plugin-dir/include/plugin-version.h diff --git a/scripts/dummy-tools/gcc b/scripts/dummy-tools/gcc index b2483149bbe5..7db825843435 100755 --- a/scripts/dummy-tools/gcc +++ b/scripts/dummy-tools/gcc @@ -96,12 +96,8 @@ fi # To set GCC_PLUGINS if arg_contain -print-file-name=plugin "$@"; then - plugin_dir=$(mktemp -d) - - mkdir -p $plugin_dir/include - touch $plugin_dir/include/plugin-version.h - - echo $plugin_dir + # Use $0 to find the in-tree dummy directory + echo "$(dirname "$(readlink -f "$0")")/dummy-plugin-dir" exit 0 fi diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index dd554bd436cc..4041881746ad 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -70,7 +70,6 @@ configs=$(sed -e ' # # The format is <file-name>:<CONFIG-option> in each line. config_leak_ignores=" -arch/alpha/include/uapi/asm/setup.h:CONFIG_ALPHA_LEGACY_START_ADDRESS arch/arc/include/uapi/asm/page.h:CONFIG_ARC_PAGE_SIZE_16K arch/arc/include/uapi/asm/page.h:CONFIG_ARC_PAGE_SIZE_4K arch/arc/include/uapi/asm/swab.h:CONFIG_ARC_HAS_SWAPE @@ -84,7 +83,6 @@ arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_SUPPORT arch/x86/include/uapi/asm/auxvec.h:CONFIG_IA32_EMULATION arch/x86/include/uapi/asm/auxvec.h:CONFIG_X86_64 arch/x86/include/uapi/asm/mman.h:CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS -include/uapi/asm-generic/fcntl.h:CONFIG_64BIT include/uapi/linux/atmdev.h:CONFIG_COMPAT include/uapi/linux/eventpoll.h:CONFIG_PM_SLEEP include/uapi/linux/hw_breakpoint.h:CONFIG_HAVE_MIXED_BREAKPOINTS_REGS diff --git a/scripts/kconfig/qconf-cfg.sh b/scripts/kconfig/qconf-cfg.sh index 9b695e5cd9b3..ad652cb53947 100755 --- a/scripts/kconfig/qconf-cfg.sh +++ b/scripts/kconfig/qconf-cfg.sh @@ -20,5 +20,6 @@ fi echo >&2 "*" echo >&2 "* Could not find Qt5 via ${HOSTPKG_CONFIG}." echo >&2 "* Please install Qt5 and make sure it's in PKG_CONFIG_PATH" +echo >&2 "* You need $PKG" echo >&2 "*" exit 1 diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index cbd6b0f48b4e..80d973144fde 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1571,9 +1571,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, zeros = calloc(1, sym->st_size); symval = zeros; } else { - symval = (void *)info->hdr - + info->sechdrs[get_secindex(info, sym)].sh_offset - + sym->st_value; + symval = sym_get_data(info, sym); } /* First handle the "special" cases */ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 29474cee10b1..55e32af2e53f 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -321,13 +321,10 @@ static void *sym_get_data_by_offset(const struct elf_info *info, { Elf_Shdr *sechdr = &info->sechdrs[secindex]; - if (info->hdr->e_type != ET_REL) - offset -= sechdr->sh_addr; - return (void *)info->hdr + sechdr->sh_offset + offset; } -static void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) +void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) { return sym_get_data_by_offset(info, get_secindex(info, sym), sym->st_value); @@ -339,8 +336,16 @@ static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) sechdr->sh_name); } -static const char *sec_name(const struct elf_info *info, int secindex) +static const char *sec_name(const struct elf_info *info, unsigned int secindex) { + /* + * If sym->st_shndx is a special section index, there is no + * corresponding section header. + * Return "" if the index is out of range of info->sechdrs[] array. + */ + if (secindex >= info->num_sections) + return ""; + return sech_name(info, &info->sechdrs[secindex]); } @@ -466,6 +471,10 @@ static int parse_elf(struct elf_info *info, const char *filename) sechdrs = (void *)hdr + hdr->e_shoff; info->sechdrs = sechdrs; + /* modpost only works for relocatable objects */ + if (hdr->e_type != ET_REL) + fatal("%s: not relocatable object.", filename); + /* Check if file offset is correct */ if (hdr->e_shoff > info->size) { fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", @@ -737,12 +746,18 @@ static bool match(const char *string, const char *const patterns[]) return false; } +/* useful to pass patterns to match() directly */ +#define PATTERNS(...) \ + ({ \ + static const char *const patterns[] = {__VA_ARGS__, NULL}; \ + patterns; \ + }) + /* sections that we do not want to do full section mismatch check on */ static const char *const section_white_list[] = { ".comment*", ".debug*", - ".cranges", /* sh64 */ ".zdebug*", /* Compressed debug sections. */ ".GCC.command.line", /* record-gcc-switches */ ".mdebug*", /* alpha, score, mips etc. */ @@ -830,28 +845,12 @@ static const char *const init_data_sections[] = /* all init sections */ static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL }; -/* All init and exit sections (code + data) */ -static const char *const init_exit_sections[] = - {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }; - /* all text sections */ static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL }; /* data section */ static const char *const data_sections[] = { DATA_SECTIONS, NULL }; - -/* symbols in .data that may refer to init/exit sections */ -#define DEFAULT_SYMBOL_WHITE_LIST \ - "*driver", \ - "*_template", /* scsi uses *_template a lot */ \ - "*_timer", /* arm uses ops structures named _timer a lot */ \ - "*_sht", /* scsi also used *_sht to some extent */ \ - "*_ops", \ - "*_probe", \ - "*_probe_one", \ - "*_console" - static const char *const head_sections[] = { ".head.text*", NULL }; static const char *const linker_symbols[] = { "__init_begin", "_sinittext", "_einittext", NULL }; @@ -883,9 +882,6 @@ enum mismatch { * * @mismatch: Type of mismatch. * - * @symbol_white_list: Do not match a relocation to a symbol in this list - * even if it is targeting a section in @bad_to_sec. - * * @handler: Specific handler to call when a match is found. If NULL, * default_mismatch_handler() will be called. * @@ -895,7 +891,6 @@ struct sectioncheck { const char *bad_tosec[20]; const char *good_tosec[20]; enum mismatch mismatch; - const char *symbol_white_list[20]; void (*handler)(const char *modname, struct elf_info *elf, const struct sectioncheck* const mismatch, Elf_Rela *r, Elf_Sym *sym, const char *fromsec); @@ -915,75 +910,61 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { TEXT_SECTIONS, NULL }, .bad_tosec = { ALL_INIT_SECTIONS, NULL }, .mismatch = TEXT_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { ALL_XXXINIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_INIT, - .symbol_white_list = { - "*_template", "*_timer", "*_sht", "*_ops", - "*_probe", "*_probe_one", "*_console", NULL - }, }, { .fromsec = { TEXT_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = TEXT_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not reference init code/data from meminit code/data */ { .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = XXXINIT_TO_SOME_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not reference exit code/data from memexit code/data */ { .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, .bad_tosec = { EXIT_SECTIONS, NULL }, .mismatch = XXXEXIT_TO_SOME_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not use exit code/data from init code */ { .fromsec = { ALL_INIT_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = ANY_INIT_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not use init code/data from exit code */ { .fromsec = { ALL_EXIT_SECTIONS, NULL }, .bad_tosec = { ALL_INIT_SECTIONS, NULL }, .mismatch = ANY_EXIT_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = ANY_INIT_TO_ANY_EXIT, - .symbol_white_list = { NULL }, }, /* Do not export init/exit functions or data */ { .fromsec = { "___ksymtab*", NULL }, .bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, .mismatch = EXPORT_TO_INIT_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { "__ex_table", NULL }, @@ -1044,15 +1025,6 @@ static const struct sectioncheck *section_mismatch( * fromsec = .data* * atsym = __param_ops_* * - * Pattern 2: - * Many drivers utilise a *driver container with references to - * add, remove, probe functions etc. - * the pattern is identified by: - * tosec = init or exit section - * fromsec = data section - * atsym = *driver, *_template, *_sht, *_ops, *_probe, - * *probe_one, *_console, *_timer - * * Pattern 3: * Whitelist all references from .head.text to any init section * @@ -1101,10 +1073,22 @@ static int secref_whitelist(const struct sectioncheck *mismatch, strstarts(fromsym, "__param_ops_")) return 0; - /* Check for pattern 2 */ - if (match(tosec, init_exit_sections) && - match(fromsec, data_sections) && - match(fromsym, mismatch->symbol_white_list)) + /* symbols in data sections that may refer to any init/exit sections */ + if (match(fromsec, PATTERNS(DATA_SECTIONS)) && + match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && + match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot + "*_timer", // arm uses ops structures named _timer a lot + "*_sht", // scsi also used *_sht to some extent + "*_ops", + "*_probe", + "*_probe_one", + "*_console"))) + return 0; + + /* symbols in data sections that may refer to meminit/exit sections */ + if (match(fromsec, PATTERNS(DATA_SECTIONS)) && + match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_EXIT_SECTIONS)) && + match(fromsym, PATTERNS("*driver"))) return 0; /* Check for pattern 3 */ @@ -1230,42 +1214,6 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr, return near; } -/* - * Convert a section name to the function/data attribute - * .init.text => __init - * .memexitconst => __memconst - * etc. - * - * The memory of returned value has been allocated on a heap. The user of this - * method should free it after usage. -*/ -static char *sec2annotation(const char *s) -{ - if (match(s, init_exit_sections)) { - char *p = NOFAIL(malloc(20)); - char *r = p; - - *p++ = '_'; - *p++ = '_'; - if (*s == '.') - s++; - while (*s && *s != '.') - *p++ = *s++; - *p = '\0'; - if (*s == '.') - s++; - if (strstr(s, "rodata") != NULL) - strcat(p, "const "); - else if (strstr(s, "data") != NULL) - strcat(p, "data "); - else - strcat(p, " "); - return r; - } else { - return NOFAIL(strdup("")); - } -} - static int is_function(Elf_Sym *sym) { if (sym) @@ -1274,19 +1222,6 @@ static int is_function(Elf_Sym *sym) return -1; } -static void print_section_list(const char * const list[20]) -{ - const char *const *s = list; - - while (*s) { - fprintf(stderr, "%s", *s); - s++; - if (*s) - fprintf(stderr, ", "); - } - fprintf(stderr, "\n"); -} - static inline void get_pretty_name(int is_func, const char** name, const char** name_p) { switch (is_func) { @@ -1304,141 +1239,31 @@ static inline void get_pretty_name(int is_func, const char** name, const char** static void report_sec_mismatch(const char *modname, const struct sectioncheck *mismatch, const char *fromsec, - unsigned long long fromaddr, const char *fromsym, - int from_is_func, - const char *tosec, const char *tosym, - int to_is_func) + const char *tosec, const char *tosym) { - const char *from, *from_p; - const char *to, *to_p; - char *prl_from; - char *prl_to; - sec_mismatch_count++; - get_pretty_name(from_is_func, &from, &from_p); - get_pretty_name(to_is_func, &to, &to_p); - - warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s " - "to the %s %s:%s%s\n", - modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec, - tosym, to_p); - switch (mismatch->mismatch) { case TEXT_TO_ANY_INIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The function %s%s() references\n" - "the %s %s%s%s.\n" - "This is often because %s lacks a %s\n" - "annotation or the annotation of %s is wrong.\n", - prl_from, fromsym, - to, prl_to, tosym, to_p, - fromsym, prl_to, tosym); - free(prl_from); - free(prl_to); - break; - case DATA_TO_ANY_INIT: { - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The variable %s references\n" - "the %s %s%s%s\n" - "If the reference is valid then annotate the\n" - "variable with __init* or __refdata (see linux/init.h) " - "or name the variable:\n", - fromsym, to, prl_to, tosym, to_p); - print_section_list(mismatch->symbol_white_list); - free(prl_to); - break; - } + case DATA_TO_ANY_INIT: case TEXT_TO_ANY_EXIT: - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The function %s() references a %s in an exit section.\n" - "Often the %s %s%s has valid usage outside the exit section\n" - "and the fix is to remove the %sannotation of %s.\n", - fromsym, to, to, tosym, to_p, prl_to, tosym); - free(prl_to); - break; - case DATA_TO_ANY_EXIT: { - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The variable %s references\n" - "the %s %s%s%s\n" - "If the reference is valid then annotate the\n" - "variable with __exit* (see linux/init.h) or " - "name the variable:\n", - fromsym, to, prl_to, tosym, to_p); - print_section_list(mismatch->symbol_white_list); - free(prl_to); - break; - } + case DATA_TO_ANY_EXIT: case XXXINIT_TO_SOME_INIT: case XXXEXIT_TO_SOME_EXIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "If %s is only used by %s then\n" - "annotate %s with a matching annotation.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - tosym, fromsym, tosym); - free(prl_from); - free(prl_to); - break; case ANY_INIT_TO_ANY_EXIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "This is often seen when error handling " - "in the init function\n" - "uses functionality in the exit path.\n" - "The fix is often to remove the %sannotation of\n" - "%s%s so it may be used outside an exit section.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - prl_to, tosym, to_p); - free(prl_from); - free(prl_to); - break; case ANY_EXIT_TO_ANY_INIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "This is often seen when error handling " - "in the exit function\n" - "uses functionality in the init path.\n" - "The fix is often to remove the %sannotation of\n" - "%s%s so it may be used outside an init section.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - prl_to, tosym, to_p); - free(prl_from); - free(prl_to); + warn("%s: section mismatch in reference: %s (section: %s) -> %s (section: %s)\n", + modname, fromsym, fromsec, tosym, tosec); break; case EXPORT_TO_INIT_EXIT: - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The symbol %s is exported and annotated %s\n" - "Fix this by removing the %sannotation of %s " - "or drop the export.\n", - tosym, prl_to, prl_to, tosym); - free(prl_to); + warn("%s: EXPORT_SYMBOL used for init/exit symbol: %s (section: %s)\n", + modname, tosym, tosec); break; case EXTABLE_TO_NON_TEXT: - fatal("There's a special handler for this mismatch type, " - "we should never get here."); + fatal("There's a special handler for this mismatch type, we should never get here.\n"); break; } - fprintf(stderr, "\n"); } static void default_mismatch_handler(const char *modname, struct elf_info *elf, @@ -1454,9 +1279,6 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, from = find_elf_symbol2(elf, r->r_offset, fromsec); fromsym = sym_name(elf, from); - if (strstarts(fromsym, "reference___initcall")) - return; - tosec = sec_name(elf, get_secindex(elf, sym)); to = find_elf_symbol(elf, r->r_addend, sym); tosym = sym_name(elf, to); @@ -1465,9 +1287,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, if (secref_whitelist(mismatch, fromsec, fromsym, tosec, tosym)) { report_sec_mismatch(modname, mismatch, - fromsec, r->r_offset, fromsym, - is_function(from), tosec, tosym, - is_function(to)); + fromsec, fromsym, tosec, tosym); } } @@ -1623,9 +1443,6 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) break; case R_386_PC32: r->r_addend = TO_NATIVE(*location) + 4; - /* For CONFIG_RELOCATABLE=y */ - if (elf->hdr->e_type == ET_EXEC) - r->r_addend += r->r_offset; break; } return 0; @@ -1718,8 +1535,7 @@ static void section_rela(const char *modname, struct elf_info *elf, Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; Elf_Rela *stop = (void *)start + sechdr->sh_size; - fromsec = sech_name(elf, sechdr); - fromsec += strlen(".rela"); + fromsec = sec_name(elf, sechdr->sh_info); /* if from section (name) is know good then skip it */ if (match(fromsec, section_white_list)) return; @@ -1771,8 +1587,7 @@ static void section_rel(const char *modname, struct elf_info *elf, Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; Elf_Rel *stop = (void *)start + sechdr->sh_size; - fromsec = sech_name(elf, sechdr); - fromsec += strlen(".rel"); + fromsec = sec_name(elf, sechdr->sh_info); /* if from section (name) is know good then skip it */ if (match(fromsec, section_white_list)) return; diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 044bdfb894b7..1178f40a73f3 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -26,7 +26,6 @@ #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Addr Elf32_Addr -#define Elf_Sword Elf64_Sword #define Elf_Section Elf32_Half #define ELF_ST_BIND ELF32_ST_BIND #define ELF_ST_TYPE ELF32_ST_TYPE @@ -41,7 +40,6 @@ #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Addr Elf64_Addr -#define Elf_Sword Elf64_Sxword #define Elf_Section Elf64_Half #define ELF_ST_BIND ELF64_ST_BIND #define ELF_ST_TYPE ELF64_ST_TYPE @@ -158,22 +156,28 @@ static inline int is_shndx_special(unsigned int i) return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE; } -/* - * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of - * the way to -256..-1, to avoid conflicting with real section - * indices. - */ -#define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1)) - /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */ static inline unsigned int get_secindex(const struct elf_info *info, const Elf_Sym *sym) { - if (is_shndx_special(sym->st_shndx)) - return SPECIAL(sym->st_shndx); - if (sym->st_shndx != SHN_XINDEX) - return sym->st_shndx; - return info->symtab_shndx_start[sym - info->symtab_start]; + unsigned int index = sym->st_shndx; + + /* + * Elf{32,64}_Sym::st_shndx is 2 byte. Big section numbers are available + * in the .symtab_shndx section. + */ + if (index == SHN_XINDEX) + return info->symtab_shndx_start[sym - info->symtab_start]; + + /* + * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of + * the way to UINT_MAX-255..UINT_MAX, to avoid conflicting with real + * section indices. + */ + if (index >= SHN_LORESERVE && index <= SHN_HIRESERVE) + return index - SHN_HIRESERVE - 1; + + return index; } /* file2alias.c */ @@ -187,6 +191,7 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen); /* from modpost.c */ char *read_text_file(const char *filename); char *get_line(char **stringp); +void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym); enum loglevel { LOG_WARN, diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 1d0e1e4dc3d2..3a3aa2354ed8 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -27,6 +27,8 @@ SECTIONS { .ctors 0 : ALIGN(8) { *(SORT(.ctors.*)) *(.ctors) } .init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) } + .altinstructions 0 : ALIGN(8) { KEEP(*(.altinstructions)) } + __bug_table 0 : ALIGN(8) { KEEP(*(__bug_table)) } __jump_table 0 : ALIGN(8) { KEEP(*(__jump_table)) } __patchable_function_entries : { *(__patchable_function_entries) } diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 7c477ca7dc98..8fa7c5b8a1a1 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -49,6 +49,9 @@ sed -e '/^DEL/d' -e 's/^\t*//' <<EOF URL: https://www.kernel.org $S Source: kernel-$__KERNELRELEASE.tar.gz Provides: $PROVIDES + # $UTS_MACHINE as a fallback of _arch in case + # /usr/lib/rpm/platform/*/macros was not included. + %define _arch %{?_arch:$UTS_MACHINE} %define __spec_install_post /usr/lib/rpm/brp-compress || : %define debug_package %{nil} diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 348ed6cfa08a..cb3496e00d8a 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -6,8 +6,6 @@ config SECURITY_APPARMOR select SECURITY_PATH select SECURITYFS select SECURITY_NETWORK - select ZLIB_INFLATE - select ZLIB_DEFLATE default n help This enables the AppArmor security module. @@ -17,29 +15,6 @@ config SECURITY_APPARMOR If you are unsure how to answer this question, answer N. -config SECURITY_APPARMOR_HASH - bool "Enable introspection of sha1 hashes for loaded profiles" - depends on SECURITY_APPARMOR - select CRYPTO - select CRYPTO_SHA1 - default y - help - This option selects whether introspection of loaded policy - is available to userspace via the apparmor filesystem. - -config SECURITY_APPARMOR_HASH_DEFAULT - bool "Enable policy hash introspection by default" - depends on SECURITY_APPARMOR_HASH - default y - help - This option selects whether sha1 hashing of loaded policy - is enabled by default. The generation of sha1 hashes for - loaded policy provide system administrators a quick way - to verify that policy in the kernel matches what is expected, - however it can slow down policy load on some devices. In - these cases policy hashing can be disabled by default and - enabled only if needed. - config SECURITY_APPARMOR_DEBUG bool "Build AppArmor with debug code" depends on SECURITY_APPARMOR @@ -69,6 +44,67 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES When enabled, various debug messages will be logged to the kernel message buffer. +config SECURITY_APPARMOR_INTROSPECT_POLICY + bool "Allow loaded policy to be introspected" + depends on SECURITY_APPARMOR + default y + help + This option selects whether introspection of loaded policy + is available to userspace via the apparmor filesystem. This + adds to kernel memory usage. It is required for introspection + of loaded policy, and check point and restore support. It + can be disabled for embedded systems where reducing memory and + cpu is paramount. + +config SECURITY_APPARMOR_HASH + bool "Enable introspection of sha1 hashes for loaded profiles" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select CRYPTO + select CRYPTO_SHA1 + default y + help + This option selects whether introspection of loaded policy + hashes is available to userspace via the apparmor + filesystem. This option provides a light weight means of + checking loaded policy. This option adds to policy load + time and can be disabled for small embedded systems. + +config SECURITY_APPARMOR_HASH_DEFAULT + bool "Enable policy hash introspection by default" + depends on SECURITY_APPARMOR_HASH + default y + help + This option selects whether sha1 hashing of loaded policy + is enabled by default. The generation of sha1 hashes for + loaded policy provide system administrators a quick way + to verify that policy in the kernel matches what is expected, + however it can slow down policy load on some devices. In + these cases policy hashing can be disabled by default and + enabled only if needed. + +config SECURITY_APPARMOR_EXPORT_BINARY + bool "Allow exporting the raw binary policy" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select ZLIB_INFLATE + select ZLIB_DEFLATE + default y + help + This option allows reading back binary policy as it was loaded. + It increases the amount of kernel memory needed by policy and + also increases policy load time. This option is required for + checkpoint and restore support, and debugging of loaded policy. + +config SECURITY_APPARMOR_PARANOID_LOAD + bool "Perform full verification of loaded policy" + depends on SECURITY_APPARMOR + default y + help + This options allows controlling whether apparmor does a full + verification of loaded policy. This should not be disabled + except for embedded systems where the image is read only, + includes policy, and has some form of integrity check. + Disabling the check will speed up policy loads. + config SECURITY_APPARMOR_KUNIT_TEST bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS depends on KUNIT=y && SECURITY_APPARMOR diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0797edb2fb3d..d066ccc219e2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -36,6 +36,7 @@ #include "include/policy_ns.h" #include "include/resource.h" #include "include/policy_unpack.h" +#include "include/task.h" /* * The apparmor filesystem interface used for policy load and introspection @@ -70,6 +71,7 @@ struct rawdata_f_data { struct aa_loaddata *loaddata; }; +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY #define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) static void rawdata_f_data_free(struct rawdata_f_data *private) @@ -94,9 +96,10 @@ static struct rawdata_f_data *rawdata_f_data_alloc(size_t size) return ret; } +#endif /** - * aa_mangle_name - mangle a profile name to std profile layout form + * mangle_name - mangle a profile name to std profile layout form * @name: profile name to mangle (NOT NULL) * @target: buffer to store mangled name, same length as @name (MAYBE NULL) * @@ -401,7 +404,7 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, data->size = copy_size; if (copy_from_user(data->data, userbuf, copy_size)) { - kvfree(data); + aa_put_loaddata(data); return ERR_PTR(-EFAULT); } @@ -1201,7 +1204,7 @@ SEQ_NS_FOPS(name); /* policy/raw_data/ * file ops */ - +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY #define SEQ_RAWDATA_FOPS(NAME) \ static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\ { \ @@ -1294,44 +1297,47 @@ SEQ_RAWDATA_FOPS(compressed_size); static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen) { - int error; - struct z_stream_s strm; +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY + if (aa_g_rawdata_compression_level != 0) { + int error = 0; + struct z_stream_s strm; - if (aa_g_rawdata_compression_level == 0) { - if (dlen < slen) - return -EINVAL; - memcpy(dst, src, slen); - return 0; - } + memset(&strm, 0, sizeof(strm)); - memset(&strm, 0, sizeof(strm)); + strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); + if (!strm.workspace) + return -ENOMEM; - strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); - if (!strm.workspace) - return -ENOMEM; - - strm.next_in = src; - strm.avail_in = slen; + strm.next_in = src; + strm.avail_in = slen; - error = zlib_inflateInit(&strm); - if (error != Z_OK) { - error = -ENOMEM; - goto fail_inflate_init; - } + error = zlib_inflateInit(&strm); + if (error != Z_OK) { + error = -ENOMEM; + goto fail_inflate_init; + } - strm.next_out = dst; - strm.avail_out = dlen; + strm.next_out = dst; + strm.avail_out = dlen; - error = zlib_inflate(&strm, Z_FINISH); - if (error != Z_STREAM_END) - error = -EINVAL; - else - error = 0; + error = zlib_inflate(&strm, Z_FINISH); + if (error != Z_STREAM_END) + error = -EINVAL; + else + error = 0; - zlib_inflateEnd(&strm); + zlib_inflateEnd(&strm); fail_inflate_init: - kvfree(strm.workspace); - return error; + kvfree(strm.workspace); + + return error; + } +#endif + + if (dlen < slen) + return -EINVAL; + memcpy(dst, src, slen); + return 0; } static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, @@ -1492,10 +1498,12 @@ fail: return PTR_ERR(dent); } +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + /** fns to setup dynamic per profile/namespace files **/ -/** +/* * * Requires: @profile->ns->lock held */ @@ -1522,7 +1530,7 @@ void __aafs_profile_rmdir(struct aa_profile *profile) } } -/** +/* * * Requires: @old->ns->lock held */ @@ -1557,6 +1565,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, return dent; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY static int profile_depth(struct aa_profile *profile) { int depth = 0; @@ -1658,7 +1667,7 @@ static const struct inode_operations rawdata_link_abi_iops = { static const struct inode_operations rawdata_link_data_iops = { .get_link = rawdata_get_link_data, }; - +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ /* * Requires: @profile->ns->lock held @@ -1729,15 +1738,17 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY if (profile->rawdata) { - dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, - profile->label.proxy, NULL, NULL, - &rawdata_link_sha1_iops); - if (IS_ERR(dent)) - goto fail; - aa_get_proxy(profile->label.proxy); - profile->dents[AAFS_PROF_RAW_HASH] = dent; - + if (aa_g_hash_policy) { + dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, + profile->label.proxy, NULL, NULL, + &rawdata_link_sha1_iops); + if (IS_ERR(dent)) + goto fail; + aa_get_proxy(profile->label.proxy); + profile->dents[AAFS_PROF_RAW_HASH] = dent; + } dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, profile->label.proxy, NULL, NULL, &rawdata_link_abi_iops); @@ -1754,6 +1765,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) aa_get_proxy(profile->label.proxy); profile->dents[AAFS_PROF_RAW_DATA] = dent; } +#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aafs_profile_mkdir(child, prof_child_dir(profile)); @@ -1880,7 +1892,7 @@ static void __aa_fs_list_remove_rawdata(struct aa_ns *ns) __aa_fs_remove_rawdata(ent); } -/** +/* * * Requires: @ns->lock held */ @@ -2323,6 +2335,7 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = { AA_SFS_FILE_BOOLEAN("v6", 1), AA_SFS_FILE_BOOLEAN("v7", 1), AA_SFS_FILE_BOOLEAN("v8", 1), + AA_SFS_FILE_BOOLEAN("v9", 1), { } }; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index f7e97c7e80f3..704b0c895605 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -137,7 +137,7 @@ int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, } if (AUDIT_MODE(profile) == AUDIT_QUIET || (type == AUDIT_APPARMOR_DENIED && - AUDIT_MODE(profile) == AUDIT_QUIET)) + AUDIT_MODE(profile) == AUDIT_QUIET_DENIED)) return aad(sa)->error; if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index a29e69d2c300..91689d34d281 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -119,7 +119,7 @@ static inline unsigned int match_component(struct aa_profile *profile, * @profile: profile to find perms for * @label: label to check access permissions for * @stack: whether this is a stacking request - * @start: state to start match in + * @state: state to start match in * @subns: whether to do permission checks on components in a subns * @request: permissions to request * @perms: perms struct to set @@ -466,7 +466,7 @@ restart: * xattrs, or a longer match */ candidate = profile; - candidate_len = profile->xmatch_len; + candidate_len = max(count, profile->xmatch_len); candidate_xattrs = ret; conflict = false; } @@ -1279,7 +1279,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name, /** * aa_change_profile - perform a one-way profile transition * @fqname: name of profile may include namespace (NOT NULL) - * @onexec: whether this transition is to take place immediately or at exec * @flags: flags affecting change behavior * * Change to new profile @name. Unlike with hats, there is no way diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1fbabdb565a8..9c3fc36a0702 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; extern bool aa_g_debug; extern bool aa_g_hash_policy; +extern bool aa_g_export_binary; extern int aa_g_rawdata_compression_level; extern bool aa_g_lock_policy; extern bool aa_g_logsyscall; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 6e14f6cecdb9..1e94904f68d9 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name, struct dentry *dent); struct aa_loaddata; + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +#else +static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) +{ + /* empty stub */ +} + +static inline int __aa_fs_create_rawdata(struct aa_ns *ns, + struct aa_loaddata *rawdata) +{ + return 0; +} +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 7517605a183d..029cb20e322d 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -142,6 +142,7 @@ static inline u16 dfa_map_xindex(u16 mask) */ #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f) #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) #define dfa_user_xindex(dfa, state) \ @@ -150,6 +151,8 @@ static inline u16 dfa_map_xindex(u16 mask) #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ 0x7f) | \ ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_other_xbits(dfa, state) \ + ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f) #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) #define dfa_other_quiet(dfa, state) \ ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 9cafd80f7731..a1ac6ffb95e9 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -13,24 +13,6 @@ #include <linux/sched.h> -struct aa_profile; - -#define AA_PTRACE_TRACE MAY_WRITE -#define AA_PTRACE_READ MAY_READ -#define AA_MAY_BE_TRACED AA_MAY_APPEND -#define AA_MAY_BE_READ AA_MAY_CREATE -#define PTRACE_PERM_SHIFT 2 - -#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ - AA_MAY_BE_READ | AA_MAY_BE_TRACED) -#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) - -#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ - "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ - "xcpu xfsz vtalrm prof winch io pwr sys emt lost" - -int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, - u32 request); int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); #endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 9101c2c76d9e..860484c6f99a 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -92,6 +92,8 @@ enum label_flags { FLAG_STALE = 0x800, /* replaced/removed */ FLAG_RENAMED = 0x1000, /* label has renaming in it */ FLAG_REVOKED = 0x2000, /* label has revocation in it */ + FLAG_DEBUG1 = 0x4000, + FLAG_DEBUG2 = 0x8000, /* These flags must correspond with PATH_flags */ /* TODO: add new path flags */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index e2e8df0c6f1c..f42359f58eb5 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -22,6 +22,11 @@ */ #define DEBUG_ON (aa_g_debug) +/* + * split individual debug cases out in preparation for finer grained + * debug controls in the future. + */ +#define AA_DEBUG_LABEL DEBUG_ON #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) #define AA_DEBUG(fmt, args...) \ do { \ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 44a7945fbe3c..343189903dba 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -17,8 +17,8 @@ enum path_flags { PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ - PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ - PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ + PATH_DELEGATE_DELETED = 0x10000, /* delegate deleted files */ + PATH_MEDIATE_DELETED = 0x20000, /* mediate deleted paths */ }; int aa_path_name(const struct path *path, int flags, char *buffer, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index cb5ef21991b7..639b5b248e63 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -48,6 +48,10 @@ extern const char *const aa_profile_mode_names[]; #define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) +#define CHECK_DEBUG1(_profile) ((_profile)->label.flags & FLAG_DEBUG1) + +#define CHECK_DEBUG2(_profile) ((_profile)->label.flags & FLAG_DEBUG2) + #define profile_is_stale(_profile) (label_is_stale(&(_profile)->label)) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) @@ -135,7 +139,7 @@ struct aa_profile { const char *attach; struct aa_dfa *xmatch; - int xmatch_len; + unsigned int xmatch_len; enum audit_mode audit; long mode; u32 path_flags; diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 3df6f804922d..33d665516fc1 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -74,6 +74,7 @@ struct aa_ns { struct dentry *dents[AAFS_NS_SIZEOF]; }; +extern struct aa_label *kernel_t; extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index e0e1ca7ebc38..eb5f7d7f132b 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -28,6 +28,8 @@ void aa_load_ent_free(struct aa_load_ent *ent); struct aa_load_ent *aa_load_ent_alloc(void); #define PACKED_FLAG_HAT 1 +#define PACKED_FLAG_DEBUG1 2 +#define PACKED_FLAG_DEBUG2 4 #define PACKED_MODE_ENFORCE 0 #define PACKED_MODE_COMPLAIN 1 diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h index 48ff1ddecad5..a912a5d5d04f 100644 --- a/security/apparmor/include/secid.h +++ b/security/apparmor/include/secid.h @@ -21,6 +21,9 @@ struct aa_label; /* secid value that matches any other secid */ #define AA_SECID_WILDCARD 1 +/* sysctl to enable displaying mode when converting secid to secctx */ +extern int apparmor_display_secid_mode; + struct aa_label *aa_secid_to_label(u32 secid); int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); @@ -31,6 +34,4 @@ int aa_alloc_secid(struct aa_label *label, gfp_t gfp); void aa_free_secid(u32 secid); void aa_secid_update(u32 secid, struct aa_label *label); -void aa_secids_init(void); - #endif /* __AA_SECID_H */ diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index f13d12373b25..13437d62c70f 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -77,4 +77,22 @@ static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) ctx->token = 0; } +#define AA_PTRACE_TRACE MAY_WRITE +#define AA_PTRACE_READ MAY_READ +#define AA_MAY_BE_TRACED AA_MAY_APPEND +#define AA_MAY_BE_READ AA_MAY_CREATE +#define PTRACE_PERM_SHIFT 2 + +#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) +#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) + +#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ + "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ + "xcpu xfsz vtalrm prof winch io pwr sys emt lost" + +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request); + + #endif /* __AA_TASK_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index fe36d112aad9..3dbbc59d440d 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -9,7 +9,6 @@ */ #include <linux/gfp.h> -#include <linux/ptrace.h> #include "include/audit.h" #include "include/capability.h" @@ -18,115 +17,6 @@ #include "include/ipc.h" #include "include/sig_names.h" -/** - * audit_ptrace_mask - convert mask to permission string - * @mask: permission mask to convert - * - * Returns: pointer to static string - */ -static const char *audit_ptrace_mask(u32 mask) -{ - switch (mask) { - case MAY_READ: - return "read"; - case MAY_WRITE: - return "trace"; - case AA_MAY_BE_READ: - return "readby"; - case AA_MAY_BE_TRACED: - return "tracedby"; - } - return ""; -} - -/* call back to audit ptrace fields */ -static void audit_ptrace_cb(struct audit_buffer *ab, void *va) -{ - struct common_audit_data *sa = va; - - if (aad(sa)->request & AA_PTRACE_PERM_MASK) { - audit_log_format(ab, " requested_mask=\"%s\"", - audit_ptrace_mask(aad(sa)->request)); - - if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { - audit_log_format(ab, " denied_mask=\"%s\"", - audit_ptrace_mask(aad(sa)->denied)); - } - } - audit_log_format(ab, " peer="); - aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, - FLAGS_NONE, GFP_ATOMIC); -} - -/* assumes check for PROFILE_MEDIATES is already done */ -/* TODO: conditionals */ -static int profile_ptrace_perm(struct aa_profile *profile, - struct aa_label *peer, u32 request, - struct common_audit_data *sa) -{ - struct aa_perms perms = { }; - - aad(sa)->peer = peer; - aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, - &perms); - aa_apply_modes_to_perms(profile, &perms); - return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); -} - -static int profile_tracee_perm(struct aa_profile *tracee, - struct aa_label *tracer, u32 request, - struct common_audit_data *sa) -{ - if (profile_unconfined(tracee) || unconfined(tracer) || - !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) - return 0; - - return profile_ptrace_perm(tracee, tracer, request, sa); -} - -static int profile_tracer_perm(struct aa_profile *tracer, - struct aa_label *tracee, u32 request, - struct common_audit_data *sa) -{ - if (profile_unconfined(tracer)) - return 0; - - if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) - return profile_ptrace_perm(tracer, tracee, request, sa); - - /* profile uses the old style capability check for ptrace */ - if (&tracer->label == tracee) - return 0; - - aad(sa)->label = &tracer->label; - aad(sa)->peer = tracee; - aad(sa)->request = 0; - aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, - CAP_OPT_NONE); - - return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); -} - -/** - * aa_may_ptrace - test if tracer task can trace the tracee - * @tracer: label of the task doing the tracing (NOT NULL) - * @tracee: task label to be traced - * @request: permission request - * - * Returns: %0 else error code if permission denied or error - */ -int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, - u32 request) -{ - struct aa_profile *profile; - u32 xrequest = request << PTRACE_PERM_SHIFT; - DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); - - return xcheck_labels(tracer, tracee, profile, - profile_tracer_perm(profile, tracee, request, &sa), - profile_tracee_perm(profile, tracer, xrequest, &sa)); -} - static inline int map_signal_num(int sig) { diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 0b0265da1926..0f36ee907438 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -197,18 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n) return false; } -static bool vec_unconfined(struct aa_profile **vec, int n) +static long union_vec_flags(struct aa_profile **vec, int n, long mask) { + long u = 0; int i; AA_BUG(!vec); for (i = 0; i < n; i++) { - if (!profile_unconfined(vec[i])) - return false; + u |= vec[i]->label.flags & mask; } - return true; + return u; } static int sort_cmp(const void *a, const void *b) @@ -485,7 +485,7 @@ int aa_label_next_confined(struct aa_label *label, int i) } /** - * aa_label_next_not_in_set - return the next profile of @sub not in @set + * __aa_label_next_not_in_set - return the next profile of @sub not in @set * @I: label iterator * @set: label to test against * @sub: label to if is subset of @set @@ -1097,8 +1097,8 @@ static struct aa_label *label_merge_insert(struct aa_label *new, else if (k == b->size) return aa_get_label(b); } - if (vec_unconfined(new->vec, new->size)) - new->flags |= FLAG_UNCONFINED; + new->flags |= union_vec_flags(new->vec, new->size, FLAG_UNCONFINED | + FLAG_DEBUG1 | FLAG_DEBUG2); ls = labels_set(new); write_lock_irqsave(&ls->lock, flags); label = __label_insert(labels_set(new), new, false); @@ -1631,9 +1631,9 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, AA_BUG(!str && size != 0); AA_BUG(!label); - if (flags & FLAG_ABS_ROOT) { + if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) { ns = root_ns; - len = snprintf(str, size, "="); + len = snprintf(str, size, "_"); update_for_len(total, len, size, str); } else if (!ns) { ns = labels_ns(label); @@ -1744,7 +1744,7 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, if (!use_label_hname(ns, label, flags) || display_mode(ns, label, flags)) { len = aa_label_asxprint(&name, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1772,7 +1772,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, int len; len = aa_label_asxprint(&str, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1795,7 +1795,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, int len; len = aa_label_asxprint(&str, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1895,7 +1895,8 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, AA_BUG(!str); str = skipn_spaces(str, n); - if (str == NULL || (*str == '=' && base != &root_ns->unconfined->label)) + if (str == NULL || (AA_DEBUG_LABEL && *str == '_' && + base != &root_ns->unconfined->label)) return ERR_PTR(-EINVAL); len = label_count_strn_entries(str, end - str); @@ -2136,7 +2137,7 @@ static void __labelset_update(struct aa_ns *ns) } /** - * __aa_labelset_udate_subtree - update all labels with a stale component + * __aa_labelset_update_subtree - update all labels with a stale component * @ns: ns to start update at (NOT NULL) * * Requires: @ns lock be held diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fa49b81eb54c..1c72a61108d3 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -136,7 +136,7 @@ __counted char *aa_str_alloc(int size, gfp_t gfp) { struct counted_str *str; - str = kmalloc(sizeof(struct counted_str) + size, gfp); + str = kmalloc(struct_size(str, name, size), gfp); if (!str) return NULL; @@ -322,22 +322,39 @@ static u32 map_other(u32 x) ((x & 0x60) << 19); /* SETOPT/GETOPT */ } +static u32 map_xbits(u32 x) +{ + return ((x & 0x1) << 7) | + ((x & 0x7e) << 9); +} + void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, struct aa_perms *perms) { + /* This mapping is convulated due to history. + * v1-v4: only file perms + * v5: added policydb which dropped in perm user conditional to + * gain new perm bits, but had to map around the xbits because + * the userspace compiler was still munging them. + * v9: adds using the xbits in policydb because the compiler now + * supports treating policydb permission bits different. + * Unfortunately there is not way to force auditing on the + * perms represented by the xbits + */ *perms = (struct aa_perms) { - .allow = dfa_user_allow(dfa, state), + .allow = dfa_user_allow(dfa, state) | + map_xbits(dfa_user_xbits(dfa, state)), .audit = dfa_user_audit(dfa, state), - .quiet = dfa_user_quiet(dfa, state), + .quiet = dfa_user_quiet(dfa, state) | + map_xbits(dfa_other_xbits(dfa, state)), }; - /* for v5 perm mapping in the policydb, the other set is used + /* for v5-v9 perm mapping in the policydb, the other set is used * to extend the general perm set */ perms->allow |= map_other(dfa_other_allow(dfa, state)); perms->audit |= map_other(dfa_other_audit(dfa, state)); perms->quiet |= map_other(dfa_other_quiet(dfa, state)); -// perms->xindex = dfa_user_xindex(dfa, state); } /** diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 900bc540656a..e29cade7b662 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -832,7 +832,7 @@ static void apparmor_sk_free_security(struct sock *sk) } /** - * apparmor_clone_security - clone the sk_security field + * apparmor_sk_clone_security - clone the sk_security field */ static void apparmor_sk_clone_security(const struct sock *sk, struct sock *newsk) @@ -886,10 +886,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, struct aa_label *label; if (kern) { - struct aa_ns *ns = aa_get_current_ns(); - - label = aa_get_label(ns_unconfined(ns)); - aa_put_ns(ns); + label = aa_get_label(kernel_t); } else label = aa_get_current_label(); @@ -937,7 +934,7 @@ static int apparmor_socket_connect(struct socket *sock, } /** - * apparmor_socket_list - check perms before allowing listen + * apparmor_socket_listen - check perms before allowing listen */ static int apparmor_socket_listen(struct socket *sock, int backlog) { @@ -1041,7 +1038,7 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, } /** - * apparmor_getsockopt - check perms before getting socket options + * apparmor_socket_getsockopt - check perms before getting socket options */ static int apparmor_socket_getsockopt(struct socket *sock, int level, int optname) @@ -1051,7 +1048,7 @@ static int apparmor_socket_getsockopt(struct socket *sock, int level, } /** - * apparmor_setsockopt - check perms before setting socket options + * apparmor_socket_setsockopt - check perms before setting socket options */ static int apparmor_socket_setsockopt(struct socket *sock, int level, int optname) @@ -1070,7 +1067,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) #ifdef CONFIG_NETWORK_SECMARK /** - * apparmor_socket_sock_recv_skb - check perms before associating skb to sk + * apparmor_socket_sock_rcv_skb - check perms before associating skb to sk * * Note: can not sleep may be called with locks held * @@ -1357,6 +1354,12 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif +/* whether policy exactly as loaded is retained for debug and checkpointing */ +bool aa_g_export_binary = IS_ENABLED(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY); +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY +module_param_named(export_binary, aa_g_export_binary, aabool, 0600); +#endif + /* policy loaddata compression level */ int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION; module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, @@ -1399,7 +1402,7 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR); * DEPRECATED: read only as strict checking of load is always done now * that none root users (user namespaces) can load policy. */ -bool aa_g_paranoid_load = true; +bool aa_g_paranoid_load = IS_ENABLED(CONFIG_SECURITY_APPARMOR_PARANOID_LOAD); module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); static int param_get_aaintbool(char *buffer, const struct kernel_param *kp); @@ -1761,6 +1764,14 @@ static struct ctl_table apparmor_sysctl_table[] = { .mode = 0600, .proc_handler = apparmor_dointvec, }, + { + .procname = "apparmor_display_secid_mode", + .data = &apparmor_display_secid_mode, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = apparmor_dointvec, + }, + { } }; @@ -1819,11 +1830,8 @@ static const struct nf_hook_ops apparmor_nf_ops[] = { static int __net_init apparmor_nf_register(struct net *net) { - int ret; - - ret = nf_register_net_hooks(net, apparmor_nf_ops, + return nf_register_net_hooks(net, apparmor_nf_ops, ARRAY_SIZE(apparmor_nf_ops)); - return ret; } static void __net_exit apparmor_nf_unregister(struct net *net) @@ -1857,8 +1865,6 @@ static int __init apparmor_init(void) { int error; - aa_secids_init(); - error = aa_setup_dfa_engine(); if (error) { AA_ERROR("Unable to setup dfa engine\n"); diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index aa6fcfde3051..f61247241803 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -217,7 +217,6 @@ static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa, .allow = dfa_user_allow(dfa, state), .audit = dfa_user_audit(dfa, state), .quiet = dfa_user_quiet(dfa, state), - .xindex = dfa_user_xindex(dfa, state), }; return perms; @@ -229,7 +228,8 @@ static const char * const mnt_info_table[] = { "failed srcname match", "failed type match", "failed flags match", - "failed data match" + "failed data match", + "failed perms check" }; /* @@ -284,8 +284,8 @@ static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, return 0; } - /* failed at end of flags match */ - return 4; + /* failed at perms check, don't confuse with flags match */ + return 6; } @@ -303,7 +303,7 @@ static int path_flags(struct aa_profile *profile, const struct path *path) * @profile: the confining profile * @mntpath: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath - * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) + * @devname: string for the devname/src_name (MAY BE NULL OR ERRPTR) * @type: string for the dev type (MAYBE NULL) * @flags: mount flags to match * @data: fs mount data (MAYBE NULL) @@ -358,7 +358,7 @@ audit: /** * match_mnt - handle path matching for mount * @profile: the confining profile - * @mntpath: for the mntpnt (NOT NULL) + * @path: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath * @devpath: path devname/src_name (MAYBE NULL) * @devbuffer: buffer to be used to lookup devname/src_name @@ -718,6 +718,7 @@ int aa_pivotroot(struct aa_label *label, const struct path *old_path, aa_put_label(target); goto out; } + aa_put_label(target); } else /* already audited error */ error = PTR_ERR(target); diff --git a/security/apparmor/net.c b/security/apparmor/net.c index e0c1b50d6edd..7efe4d17273d 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -145,12 +145,13 @@ int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, struct sock *sk) { + struct aa_sk_ctx *ctx = SK_CTX(sk); int error = 0; AA_BUG(!label); AA_BUG(!sk); - if (!unconfined(label)) { + if (ctx->label != kernel_t && !unconfined(label)) { struct aa_profile *profile; DEFINE_AUDIT_SK(sa, op, sk); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b0cbc4906cb3..499c0209b6a4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -422,7 +422,7 @@ static struct aa_profile *__lookup_profile(struct aa_policy *base, } /** - * aa_lookup_profile - find a profile by its full or partial name + * aa_lookupn_profile - find a profile by its full or partial name * @ns: the namespace to start from (NOT NULL) * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) * @n: size of @hname @@ -952,16 +952,18 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_lock_nested(&ns->lock, ns->level); /* check for duplicate rawdata blobs: space and file dedup */ - list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { - if (aa_rawdata_eq(rawdata_ent, udata)) { - struct aa_loaddata *tmp; - - tmp = __aa_get_loaddata(rawdata_ent); - /* check we didn't fail the race */ - if (tmp) { - aa_put_loaddata(udata); - udata = tmp; - break; + if (!list_empty(&ns->rawdata_list)) { + list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { + if (aa_rawdata_eq(rawdata_ent, udata)) { + struct aa_loaddata *tmp; + + tmp = __aa_get_loaddata(rawdata_ent); + /* check we didn't fail the race */ + if (tmp) { + aa_put_loaddata(udata); + udata = tmp; + break; + } } } } @@ -969,7 +971,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - ent->new->rawdata = aa_get_loaddata(udata); + if (aa_g_export_binary) + ent->new->rawdata = aa_get_loaddata(udata); error = __lookup_replace(ns, ent->new->base.hname, !(mask & AA_MAY_REPLACE_POLICY), &ent->old, &info); @@ -1009,7 +1012,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, } /* create new fs entries for introspection if needed */ - if (!udata->dents[AAFS_LOADDATA_DIR]) { + if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) { error = __aa_fs_create_rawdata(ns, udata); if (error) { info = "failed to create raw_data dir and files"; @@ -1037,12 +1040,14 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, /* Done with checks that may fail - do actual replacement */ __aa_bump_ns_revision(ns); - __aa_loaddata_update(udata, ns->revision); + if (aa_g_export_binary) + __aa_loaddata_update(udata, ns->revision); list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - if (ent->old && ent->old->rawdata == ent->new->rawdata) { + if (ent->old && ent->old->rawdata == ent->new->rawdata && + ent->new->rawdata) { /* dedup actual profile replacement */ audit_policy(label, op, ns_name, ent->new->base.hname, "same as current profile, skipping", diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 70921d95fb40..43beaad083fe 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -22,6 +22,9 @@ #include "include/label.h" #include "include/policy.h" +/* kernel label */ +struct aa_label *kernel_t; + /* root profile namespace */ struct aa_ns *root_ns; const char *aa_hidden_ns_name = "---"; @@ -51,10 +54,10 @@ bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) } /** - * aa_na_name - Find the ns name to display for @view from @curr - * @curr - current namespace (NOT NULL) - * @view - namespace attempting to view (NOT NULL) - * @subns - are subns visible + * aa_ns_name - Find the ns name to display for @view from @curr + * @curr: current namespace (NOT NULL) + * @view: namespace attempting to view (NOT NULL) + * @subns: are subns visible * * Returns: name of @view visible from @curr */ @@ -77,6 +80,23 @@ const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) return aa_hidden_ns_name; } +static struct aa_profile *alloc_unconfined(const char *name) +{ + struct aa_profile *profile; + + profile = aa_alloc_profile(name, NULL, GFP_KERNEL); + if (!profile) + return NULL; + + profile->label.flags |= FLAG_IX_ON_NAME_ERROR | + FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; + profile->mode = APPARMOR_UNCONFINED; + profile->file.dfa = aa_get_dfa(nulldfa); + profile->policy.dfa = aa_get_dfa(nulldfa); + + return profile; +} + /** * alloc_ns - allocate, initialize and return a new namespace * @prefix: parent namespace name (MAYBE NULL) @@ -101,16 +121,9 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) init_waitqueue_head(&ns->wait); /* released by aa_free_ns() */ - ns->unconfined = aa_alloc_profile("unconfined", NULL, GFP_KERNEL); + ns->unconfined = alloc_unconfined("unconfined"); if (!ns->unconfined) goto fail_unconfined; - - ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | - FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; - ns->unconfined->mode = APPARMOR_UNCONFINED; - ns->unconfined->file.dfa = aa_get_dfa(nulldfa); - ns->unconfined->policy.dfa = aa_get_dfa(nulldfa); - /* ns and ns->unconfined share ns->unconfined refcount */ ns->unconfined->ns = ns; @@ -187,7 +200,7 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) /** * __aa_lookupn_ns - lookup the namespace matching @hname - * @base: base list to start looking up profile name from (NOT NULL) + * @view: namespace to search in (NOT NULL) * @hname: hierarchical ns name (NOT NULL) * @n: length of @hname * @@ -272,7 +285,7 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, } /** - * aa_create_ns - create an ns, fail if it already exists + * __aa_find_or_create_ns - create an ns, fail if it already exists * @parent: the parent of the namespace being created * @name: the name of the namespace * @dir: if not null the dir to put the ns entries in @@ -388,11 +401,22 @@ static void __ns_list_release(struct list_head *head) */ int __init aa_alloc_root_ns(void) { + struct aa_profile *kernel_p; + /* released by aa_free_root_ns - used as list ref*/ root_ns = alloc_ns(NULL, "root"); if (!root_ns) return -ENOMEM; + kernel_p = alloc_unconfined("kernel_t"); + if (!kernel_p) { + destroy_ns(root_ns); + aa_free_ns(root_ns); + return -ENOMEM; + } + kernel_t = &kernel_p->label; + root_ns->unconfined->ns = aa_get_ns(root_ns); + return 0; } @@ -405,6 +429,7 @@ void __init aa_free_root_ns(void) root_ns = NULL; + aa_label_free(kernel_t); destroy_ns(ns); aa_put_ns(ns); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0acca6f2a93f..55d31bac4f35 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision) { AA_BUG(!data); AA_BUG(!data->ns); - AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]); AA_BUG(!mutex_is_locked(&data->ns->lock)); AA_BUG(data->revision > revision); data->revision = revision; - d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); - d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); + if ((data->dents[AAFS_LOADDATA_REVISION])) { + d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = + current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); + d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = + current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); + } } bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r) @@ -213,7 +214,7 @@ static void *kvmemdup(const void *src, size_t len) } /** - * aa_u16_chunck - test and do bounds checking for a u16 size based chunk + * unpack_u16_chunk - test and do bounds checking for a u16 size based chunk * @e: serialized data read head (NOT NULL) * @chunk: start address for chunk of data (NOT NULL) * @@ -456,7 +457,9 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ((e->pos - e->start) & 7); size_t pad = ALIGN(sz, 8) - sz; int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | - TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES; + TO_ACCEPT2_FLAG(YYTD_DATA32); + if (aa_g_paranoid_load) + flags |= DFA_FLAG_VERIFY_STATES; dfa = aa_dfa_unpack(blob + pad, size - pad, flags); if (IS_ERR(dfa)) @@ -668,6 +671,7 @@ static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) /** * unpack_profile - unpack a serialized profile * @e: serialized data extent information (NOT NULL) + * @ns_name: pointer of newly allocated copy of %NULL in case of error * * NOTE: unpack profile sets audit struct if there is a failure */ @@ -744,18 +748,24 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; if (tmp & PACKED_FLAG_HAT) profile->label.flags |= FLAG_HAT; + if (tmp & PACKED_FLAG_DEBUG1) + profile->label.flags |= FLAG_DEBUG1; + if (tmp & PACKED_FLAG_DEBUG2) + profile->label.flags |= FLAG_DEBUG2; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) + if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) { profile->mode = APPARMOR_COMPLAIN; - else if (tmp == PACKED_MODE_ENFORCE) + } else if (tmp == PACKED_MODE_ENFORCE) { profile->mode = APPARMOR_ENFORCE; - else if (tmp == PACKED_MODE_KILL) + } else if (tmp == PACKED_MODE_KILL) { profile->mode = APPARMOR_KILL; - else if (tmp == PACKED_MODE_UNCONFINED) + } else if (tmp == PACKED_MODE_UNCONFINED) { profile->mode = APPARMOR_UNCONFINED; - else + profile->label.flags |= FLAG_UNCONFINED; + } else { goto fail; + } if (!unpack_u32(e, &tmp, NULL)) goto fail; if (tmp) @@ -936,7 +946,7 @@ fail: } /** - * verify_head - unpack serialized stream header + * verify_header - unpack serialized stream header * @e: serialized data read head (NOT NULL) * @required: whether the header is required or optional * @ns: Returns - namespace if one is specified else NULL (NOT NULL) @@ -1052,6 +1062,7 @@ struct aa_load_ent *aa_load_ent_alloc(void) static int deflate_compress(const char *src, size_t slen, char **dst, size_t *dlen) { +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY int error; struct z_stream_s strm; void *stgbuf, *dstbuf; @@ -1123,6 +1134,10 @@ fail_deflate_init: fail_deflate: kvfree(stgbuf); goto fail_stg_alloc; +#else + *dlen = slen; + return 0; +#endif } static int compress_loaddata(struct aa_loaddata *data) @@ -1141,7 +1156,8 @@ static int compress_loaddata(struct aa_loaddata *data) if (error) return error; - kvfree(udata); + if (udata != data->data) + kvfree(udata); } else data->compressed_size = data->size; @@ -1216,9 +1232,12 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, goto fail; } } - error = compress_loaddata(udata); - if (error) - goto fail; + + if (aa_g_export_binary) { + error = compress_loaddata(udata); + if (error) + goto fail; + } return 0; fail_profile: diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index 7954cb23d5f2..0a969b2e03db 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -48,8 +48,8 @@ struct policy_unpack_fixture { size_t e_size; }; -struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, - struct kunit *test, size_t buf_size) +static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, + struct kunit *test, size_t buf_size) { char *buf; struct aa_ext *e; @@ -439,7 +439,7 @@ static void policy_unpack_test_unpack_u32_with_null_name(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_U32_BUF_OFFSET; @@ -456,7 +456,7 @@ static void policy_unpack_test_unpack_u32_with_name(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U32_NAME; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; @@ -473,7 +473,7 @@ static void policy_unpack_test_unpack_u32_out_of_bounds(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U32_NAME; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32); @@ -489,7 +489,7 @@ static void policy_unpack_test_unpack_u64_with_null_name(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_U64_BUF_OFFSET; @@ -506,7 +506,7 @@ static void policy_unpack_test_unpack_u64_with_name(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U64_NAME; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; @@ -523,7 +523,7 @@ static void policy_unpack_test_unpack_u64_out_of_bounds(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U64_NAME; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64); diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index fde332e0ea7d..86ad26ef72ed 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -90,7 +90,7 @@ static char *split_token_from_name(const char *op, char *args, u64 *token) } /** - * aa_setprocattr_chagnehat - handle procattr interface to change_hat + * aa_setprocattr_changehat - handle procattr interface to change_hat * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) * @size: size of the args * @flags: set of flags governing behavior diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index ce545f99259e..24a0e23f1b2b 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -13,9 +13,9 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/gfp.h> -#include <linux/idr.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/xarray.h> #include "include/cred.h" #include "include/lib.h" @@ -29,8 +29,9 @@ */ #define AA_FIRST_SECID 2 -static DEFINE_IDR(aa_secids); -static DEFINE_SPINLOCK(secid_lock); +static DEFINE_XARRAY_FLAGS(aa_secids, XA_FLAGS_LOCK_IRQ | XA_FLAGS_TRACK_FREE); + +int apparmor_display_secid_mode; /* * TODO: allow policy to reserve a secid range? @@ -47,9 +48,9 @@ void aa_secid_update(u32 secid, struct aa_label *label) { unsigned long flags; - spin_lock_irqsave(&secid_lock, flags); - idr_replace(&aa_secids, label, secid); - spin_unlock_irqrestore(&secid_lock, flags); + xa_lock_irqsave(&aa_secids, flags); + __xa_store(&aa_secids, secid, label, 0); + xa_unlock_irqrestore(&aa_secids, flags); } /** @@ -58,19 +59,14 @@ void aa_secid_update(u32 secid, struct aa_label *label) */ struct aa_label *aa_secid_to_label(u32 secid) { - struct aa_label *label; - - rcu_read_lock(); - label = idr_find(&aa_secids, secid); - rcu_read_unlock(); - - return label; + return xa_load(&aa_secids, secid); } int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { /* TODO: cache secctx and ref count so we don't have to recreate */ struct aa_label *label = aa_secid_to_label(secid); + int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT; int len; AA_BUG(!seclen); @@ -78,15 +74,15 @@ int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) if (!label) return -EINVAL; + if (apparmor_display_secid_mode) + flags |= FLAG_SHOW_MODE; + if (secdata) len = aa_label_asxprint(secdata, root_ns, label, - FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | - FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT, - GFP_ATOMIC); + flags, GFP_ATOMIC); else - len = aa_label_snxprint(NULL, 0, root_ns, label, - FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | - FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT); + len = aa_label_snxprint(NULL, 0, root_ns, label, flags); + if (len < 0) return -ENOMEM; @@ -126,19 +122,16 @@ int aa_alloc_secid(struct aa_label *label, gfp_t gfp) unsigned long flags; int ret; - idr_preload(gfp); - spin_lock_irqsave(&secid_lock, flags); - ret = idr_alloc(&aa_secids, label, AA_FIRST_SECID, 0, GFP_ATOMIC); - spin_unlock_irqrestore(&secid_lock, flags); - idr_preload_end(); + xa_lock_irqsave(&aa_secids, flags); + ret = __xa_alloc(&aa_secids, &label->secid, label, + XA_LIMIT(AA_FIRST_SECID, INT_MAX), gfp); + xa_unlock_irqrestore(&aa_secids, flags); if (ret < 0) { label->secid = AA_SECID_INVALID; return ret; } - AA_BUG(ret == AA_SECID_INVALID); - label->secid = ret; return 0; } @@ -150,12 +143,7 @@ void aa_free_secid(u32 secid) { unsigned long flags; - spin_lock_irqsave(&secid_lock, flags); - idr_remove(&aa_secids, secid); - spin_unlock_irqrestore(&secid_lock, flags); -} - -void aa_secids_init(void) -{ - idr_init_base(&aa_secids, AA_FIRST_SECID); + xa_lock_irqsave(&aa_secids, flags); + __xa_erase(&aa_secids, secid); + xa_unlock_irqrestore(&aa_secids, flags); } diff --git a/security/apparmor/task.c b/security/apparmor/task.c index d17130ee6795..503dc0877fb1 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -12,7 +12,12 @@ * should return to the previous cred if it has not been modified. */ +#include <linux/gfp.h> +#include <linux/ptrace.h> + +#include "include/audit.h" #include "include/cred.h" +#include "include/policy.h" #include "include/task.h" /** @@ -177,3 +182,112 @@ int aa_restore_previous_label(u64 token) return 0; } + +/** + * audit_ptrace_mask - convert mask to permission string + * @mask: permission mask to convert + * + * Returns: pointer to static string + */ +static const char *audit_ptrace_mask(u32 mask) +{ + switch (mask) { + case MAY_READ: + return "read"; + case MAY_WRITE: + return "trace"; + case AA_MAY_BE_READ: + return "readby"; + case AA_MAY_BE_TRACED: + return "tracedby"; + } + return ""; +} + +/* call back to audit ptrace fields */ +static void audit_ptrace_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->request & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " requested_mask=\"%s\"", + audit_ptrace_mask(aad(sa)->request)); + + if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " denied_mask=\"%s\"", + audit_ptrace_mask(aad(sa)->denied)); + } + } + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); +} + +/* assumes check for PROFILE_MEDIATES is already done */ +/* TODO: conditionals */ +static int profile_ptrace_perm(struct aa_profile *profile, + struct aa_label *peer, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms = { }; + + aad(sa)->peer = peer; + aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, + &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); +} + +static int profile_tracee_perm(struct aa_profile *tracee, + struct aa_label *tracer, u32 request, + struct common_audit_data *sa) +{ + if (profile_unconfined(tracee) || unconfined(tracer) || + !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) + return 0; + + return profile_ptrace_perm(tracee, tracer, request, sa); +} + +static int profile_tracer_perm(struct aa_profile *tracer, + struct aa_label *tracee, u32 request, + struct common_audit_data *sa) +{ + if (profile_unconfined(tracer)) + return 0; + + if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) + return profile_ptrace_perm(tracer, tracee, request, sa); + + /* profile uses the old style capability check for ptrace */ + if (&tracer->label == tracee) + return 0; + + aad(sa)->label = &tracer->label; + aad(sa)->peer = tracee; + aad(sa)->request = 0; + aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, + CAP_OPT_NONE); + + return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); +} + +/** + * aa_may_ptrace - test if tracer task can trace the tracee + * @tracer: label of the task doing the tracing (NOT NULL) + * @tracee: task label to be traced + * @request: permission request + * + * Returns: %0 else error code if permission denied or error + */ +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request) +{ + struct aa_profile *profile; + u32 xrequest = request << PTRACE_PERM_SHIFT; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); + + return xcheck_labels(tracer, tracee, profile, + profile_tracer_perm(profile, tracee, request, &sa), + profile_tracee_perm(profile, tracer, xrequest, &sa)); +} diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index a77b915d36a8..8323ac5b7eee 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -303,6 +303,7 @@ #define X86_FEATURE_RETHUNK (11*32+14) /* "" Use REturn THUNK */ #define X86_FEATURE_UNRET (11*32+15) /* "" AMD BTB untrain return */ #define X86_FEATURE_USE_IBPB_FW (11*32+16) /* "" Use IBPB during runtime firmware calls */ +#define X86_FEATURE_RSB_VMEXIT_LITE (11*32+17) /* "" Fill RSB on VM-Exit when EIBRS is enabled */ /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index cc615be27a54..e057e039173c 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -150,6 +150,10 @@ * are restricted to targets in * kernel. */ +#define ARCH_CAP_PBRSB_NO BIT(24) /* + * Not susceptible to Post-Barrier + * Return Stack Buffer Predictions. + */ #define MSR_IA32_FLUSH_CMD 0x0000010b #define L1D_FLUSH BIT(0) /* diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h index bd6f4505e7b1..70adf7b119b9 100644 --- a/tools/lib/bpf/skel_internal.h +++ b/tools/lib/bpf/skel_internal.h @@ -66,13 +66,13 @@ struct bpf_load_and_run_opts { const char *errstr; }; -long bpf_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); +long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { #ifdef __KERNEL__ - return bpf_sys_bpf(cmd, attr, size); + return kern_sys_bpf(cmd, attr, size); #else return syscall(__NR_bpf, cmd, attr, size); #endif diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 33543231d453..500be85729cc 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -47,6 +47,7 @@ cxl_core-y += $(CXL_CORE_SRC)/memdev.o cxl_core-y += $(CXL_CORE_SRC)/mbox.o cxl_core-y += $(CXL_CORE_SRC)/pci.o cxl_core-y += $(CXL_CORE_SRC)/hdm.o +cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o cxl_core-y += config_check.o obj-m += test/ diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 431f2bddf6c8..a072b2d3e726 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -14,7 +14,7 @@ #define NR_CXL_HOST_BRIDGES 2 #define NR_CXL_ROOT_PORTS 2 #define NR_CXL_SWITCH_PORTS 2 -#define NR_CXL_PORT_DECODERS 2 +#define NR_CXL_PORT_DECODERS 8 static struct platform_device *cxl_acpi; static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES]; @@ -118,7 +118,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = 0, - .window_size = SZ_256M, + .window_size = SZ_256M * 4UL, }, .target = { 0 }, }, @@ -133,7 +133,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = 1, - .window_size = SZ_256M * 2, + .window_size = SZ_256M * 8UL, }, .target = { 0, 1, }, }, @@ -148,7 +148,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | ACPI_CEDT_CFMWS_RESTRICT_PMEM, .qtg_id = 2, - .window_size = SZ_256M, + .window_size = SZ_256M * 4UL, }, .target = { 0 }, }, @@ -163,7 +163,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | ACPI_CEDT_CFMWS_RESTRICT_PMEM, .qtg_id = 3, - .window_size = SZ_256M * 2, + .window_size = SZ_256M * 8UL, }, .target = { 0, 1, }, }, @@ -429,6 +429,50 @@ static int map_targets(struct device *dev, void *data) return 0; } +static int mock_decoder_commit(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + int id = cxld->id; + + if (cxld->flags & CXL_DECODER_F_ENABLE) + return 0; + + dev_dbg(&port->dev, "%s commit\n", dev_name(&cxld->dev)); + if (port->commit_end + 1 != id) { + dev_dbg(&port->dev, + "%s: out of order commit, expected decoder%d.%d\n", + dev_name(&cxld->dev), port->id, port->commit_end + 1); + return -EBUSY; + } + + port->commit_end++; + cxld->flags |= CXL_DECODER_F_ENABLE; + + return 0; +} + +static int mock_decoder_reset(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + int id = cxld->id; + + if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0) + return 0; + + dev_dbg(&port->dev, "%s reset\n", dev_name(&cxld->dev)); + if (port->commit_end != id) { + dev_dbg(&port->dev, + "%s: out of order reset, expected decoder%d.%d\n", + dev_name(&cxld->dev), port->id, port->commit_end); + return -EBUSY; + } + + port->commit_end--; + cxld->flags &= ~CXL_DECODER_F_ENABLE; + + return 0; +} + static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm) { struct cxl_port *port = cxlhdm->port; @@ -451,25 +495,39 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm) struct cxl_decoder *cxld; int rc; - if (target_count) - cxld = cxl_switch_decoder_alloc(port, target_count); - else - cxld = cxl_endpoint_decoder_alloc(port); - if (IS_ERR(cxld)) { - dev_warn(&port->dev, - "Failed to allocate the decoder\n"); - return PTR_ERR(cxld); + if (target_count) { + struct cxl_switch_decoder *cxlsd; + + cxlsd = cxl_switch_decoder_alloc(port, target_count); + if (IS_ERR(cxlsd)) { + dev_warn(&port->dev, + "Failed to allocate the decoder\n"); + return PTR_ERR(cxlsd); + } + cxld = &cxlsd->cxld; + } else { + struct cxl_endpoint_decoder *cxled; + + cxled = cxl_endpoint_decoder_alloc(port); + + if (IS_ERR(cxled)) { + dev_warn(&port->dev, + "Failed to allocate the decoder\n"); + return PTR_ERR(cxled); + } + cxld = &cxled->cxld; } - cxld->decoder_range = (struct range) { + cxld->hpa_range = (struct range) { .start = 0, .end = -1, }; - cxld->flags = CXL_DECODER_F_ENABLE; cxld->interleave_ways = min_not_zero(target_count, 1); cxld->interleave_granularity = SZ_4K; cxld->target_type = CXL_DECODER_EXPANDER; + cxld->commit = mock_decoder_commit; + cxld->reset = mock_decoder_reset; if (target_count) { rc = device_for_each_child(port->uport, &ctx, @@ -569,44 +627,6 @@ static void mock_companion(struct acpi_device *adev, struct device *dev) #define SZ_512G (SZ_64G * 8) #endif -static struct platform_device *alloc_memdev(int id) -{ - struct resource res[] = { - [0] = { - .flags = IORESOURCE_MEM, - }, - [1] = { - .flags = IORESOURCE_MEM, - .desc = IORES_DESC_PERSISTENT_MEMORY, - }, - }; - struct platform_device *pdev; - int i, rc; - - for (i = 0; i < ARRAY_SIZE(res); i++) { - struct cxl_mock_res *r = alloc_mock_res(SZ_256M); - - if (!r) - return NULL; - res[i].start = r->range.start; - res[i].end = r->range.end; - } - - pdev = platform_device_alloc("cxl_mem", id); - if (!pdev) - return NULL; - - rc = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); - if (rc) - goto err; - - return pdev; - -err: - platform_device_put(pdev); - return NULL; -} - static __init int cxl_test_init(void) { int rc, i; @@ -619,7 +639,8 @@ static __init int cxl_test_init(void) goto err_gen_pool_create; } - rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE); + rc = gen_pool_add(cxl_mock_pool, iomem_resource.end + 1 - SZ_64G, + SZ_64G, NUMA_NO_NODE); if (rc) goto err_gen_pool_add; @@ -708,7 +729,7 @@ static __init int cxl_test_init(void) struct platform_device *dport = cxl_switch_dport[i]; struct platform_device *pdev; - pdev = alloc_memdev(i); + pdev = platform_device_alloc("cxl_mem", i); if (!pdev) goto err_mem; pdev->dev.parent = &dport->dev; diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 6b9239b2afd4..aa2df3a15051 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -10,6 +10,7 @@ #include <cxlmem.h> #define LSA_SIZE SZ_128K +#define DEV_SIZE SZ_2G #define EFFECT(x) (1U << x) static struct cxl_cel_entry mock_cel[] = { @@ -26,6 +27,10 @@ static struct cxl_cel_entry mock_cel[] = { .effect = cpu_to_le16(0), }, { + .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO), + .effect = cpu_to_le16(0), + }, + { .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA), .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)), }, @@ -97,42 +102,37 @@ static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) { - struct platform_device *pdev = to_platform_device(cxlds->dev); struct cxl_mbox_identify id = { .fw_revision = { "mock fw v1 " }, .lsa_size = cpu_to_le32(LSA_SIZE), - /* FIXME: Add partition support */ - .partition_align = cpu_to_le64(0), + .partition_align = + cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER), + .total_capacity = + cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER), }; - u64 capacity = 0; - int i; if (cmd->size_out < sizeof(id)) return -EINVAL; - for (i = 0; i < 2; i++) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) - break; - - capacity += resource_size(res) / CXL_CAPACITY_MULTIPLIER; + memcpy(cmd->payload_out, &id, sizeof(id)); - if (le64_to_cpu(id.partition_align)) - continue; + return 0; +} - if (res->desc == IORES_DESC_PERSISTENT_MEMORY) - id.persistent_capacity = cpu_to_le64( - resource_size(res) / CXL_CAPACITY_MULTIPLIER); - else - id.volatile_capacity = cpu_to_le64( - resource_size(res) / CXL_CAPACITY_MULTIPLIER); - } +static int mock_partition_info(struct cxl_dev_state *cxlds, + struct cxl_mbox_cmd *cmd) +{ + struct cxl_mbox_get_partition_info pi = { + .active_volatile_cap = + cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER), + .active_persistent_cap = + cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER), + }; - id.total_capacity = cpu_to_le64(capacity); + if (cmd->size_out < sizeof(pi)) + return -EINVAL; - memcpy(cmd->payload_out, &id, sizeof(id)); + memcpy(cmd->payload_out, &pi, sizeof(pi)); return 0; } @@ -221,6 +221,9 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd * case CXL_MBOX_OP_GET_LSA: rc = mock_get_lsa(cxlds, cmd); break; + case CXL_MBOX_OP_GET_PARTITION_INFO: + rc = mock_partition_info(cxlds, cmd); + break; case CXL_MBOX_OP_SET_LSA: rc = mock_set_lsa(cxlds, cmd); break; @@ -282,7 +285,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); - if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM)) + if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) rc = devm_cxl_add_nvdimm(dev, cxlmd); return 0; diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c index f1f8c40948c5..bce6a21df0d5 100644 --- a/tools/testing/cxl/test/mock.c +++ b/tools/testing/cxl/test/mock.c @@ -208,13 +208,15 @@ int __wrap_cxl_await_media_ready(struct cxl_dev_state *cxlds) } EXPORT_SYMBOL_NS_GPL(__wrap_cxl_await_media_ready, CXL); -bool __wrap_cxl_hdm_decode_init(struct cxl_dev_state *cxlds, - struct cxl_hdm *cxlhdm) +int __wrap_cxl_hdm_decode_init(struct cxl_dev_state *cxlds, + struct cxl_hdm *cxlhdm) { int rc = 0, index; struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); - if (!ops || !ops->is_mock_dev(cxlds->dev)) + if (ops && ops->is_mock_dev(cxlds->dev)) + rc = 0; + else rc = cxl_hdm_decode_init(cxlds, cxlhdm); put_cxl_mock_ops(index); diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile index a698e24b35e7..246f7ac8489b 100644 --- a/tools/testing/memblock/Makefile +++ b/tools/testing/memblock/Makefile @@ -45,9 +45,8 @@ help: @echo ' clean - Remove generated files and symlinks in the directory' @echo '' @echo 'Configuration:' + @echo ' make MEMBLOCK_DEBUG=1 - enable memblock_dbg() messages' @echo ' make NUMA=1 - simulate enabled NUMA' - @echo ' make MOVABLE_NODE=1 - override `movable_node_is_enabled`' - @echo ' definition to simulate movable NUMA nodes' @echo ' make 32BIT_PHYS_ADDR_T=1 - Use 32 bit physical addresses' vpath %.c ../../lib diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README index ca6afcff013a..7ca437d81806 100644 --- a/tools/testing/memblock/README +++ b/tools/testing/memblock/README @@ -33,12 +33,23 @@ To run the tests, build the main target and run it: $ make && ./main -A successful run produces no output. It is also possible to override different -configuration parameters. For example, to simulate enabled NUMA, use: +A successful run produces no output. It is possible to control the behavior +by passing options from command line. For example, to include verbose output, +append the `-v` options when you run the tests: + +$ ./main -v + +This will print information about which functions are being tested and the +number of test cases that passed. + +For the full list of options from command line, see `./main --help`. + +It is also possible to override different configuration parameters to change +the test functions. For example, to simulate enabled NUMA, use: $ make NUMA=1 -For the full list of options, see `make help`. +For the full list of build options, see `make help`. Project structure ================= diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO index cd1a30d5acc9..33044c634ea7 100644 --- a/tools/testing/memblock/TODO +++ b/tools/testing/memblock/TODO @@ -1,25 +1,17 @@ TODO ===== -1. Add verbose output (e.g., what is being tested and how many tests cases are - passing) - -2. Add flags to Makefile: - + verbosity level - + enable memblock_dbg() messages (i.e. pass "-D CONFIG_DEBUG_MEMORY_INIT" - flag) - -3. Add tests trying to memblock_add() or memblock_reserve() 129th region. +1. Add tests trying to memblock_add() or memblock_reserve() 129th region. This will trigger memblock_double_array(), make sure it succeeds. *Important:* These tests require valid memory ranges, use dummy physical memory block from common.c to implement them. It is also very likely that the current MEM_SIZE won't be enough for these test cases. Use realloc to adjust the size accordingly. -4. Add test cases using this functions (implement them for both directions): +2. Add test cases using this functions (implement them for both directions): + memblock_alloc_raw() + memblock_alloc_exact_nid_raw() + memblock_alloc_try_nid_raw() -5. Add tests for memblock_alloc_node() to check if the correct NUMA node is set +3. Add tests for memblock_alloc_node() to check if the correct NUMA node is set for the new region diff --git a/tools/testing/memblock/internal.h b/tools/testing/memblock/internal.h index 94b52a8718b5..fdb7f5db7308 100644 --- a/tools/testing/memblock/internal.h +++ b/tools/testing/memblock/internal.h @@ -2,6 +2,17 @@ #ifndef _MM_INTERNAL_H #define _MM_INTERNAL_H +/* + * Enable memblock_dbg() messages + */ +#ifdef MEMBLOCK_DEBUG +static int memblock_debug = 1; +#endif + +#define pr_warn_ratelimited(fmt, ...) printf(fmt, ##__VA_ARGS__) + +bool mirrored_kernelcore = false; + struct page {}; void memblock_free_pages(struct page *page, unsigned long pfn, diff --git a/tools/testing/memblock/linux/memory_hotplug.h b/tools/testing/memblock/linux/memory_hotplug.h index 47988765a219..dabe2c556858 100644 --- a/tools/testing/memblock/linux/memory_hotplug.h +++ b/tools/testing/memblock/linux/memory_hotplug.h @@ -7,13 +7,11 @@ #include <linux/cache.h> #include <linux/types.h> +extern bool movable_node_enabled; + static inline bool movable_node_is_enabled(void) { -#ifdef MOVABLE_NODE - return true; -#else - return false; -#endif + return movable_node_enabled; } #endif diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c index fb183c9e76d1..4ca1024342b1 100644 --- a/tools/testing/memblock/main.c +++ b/tools/testing/memblock/main.c @@ -3,9 +3,11 @@ #include "tests/alloc_api.h" #include "tests/alloc_helpers_api.h" #include "tests/alloc_nid_api.h" +#include "tests/common.h" int main(int argc, char **argv) { + parse_args(argc, argv); memblock_basic_checks(); memblock_alloc_checks(); memblock_alloc_helpers_checks(); diff --git a/tools/testing/memblock/scripts/Makefile.include b/tools/testing/memblock/scripts/Makefile.include index 641569ccb7b0..aa6d82d56a23 100644 --- a/tools/testing/memblock/scripts/Makefile.include +++ b/tools/testing/memblock/scripts/Makefile.include @@ -6,14 +6,14 @@ ifeq ($(NUMA), 1) CFLAGS += -D CONFIG_NUMA endif -# Simulate movable NUMA memory regions -ifeq ($(MOVABLE_NODE), 1) - CFLAGS += -D MOVABLE_NODE -endif - # Use 32 bit physical addresses. # Remember to install 32-bit version of dependencies. ifeq ($(32BIT_PHYS_ADDR_T), 1) CFLAGS += -m32 -U CONFIG_PHYS_ADDR_T_64BIT LDFLAGS += -m32 endif + +# Enable memblock_dbg() messages +ifeq ($(MEMBLOCK_DEBUG), 1) + CFLAGS += -D MEMBLOCK_DEBUG +endif diff --git a/tools/testing/memblock/tests/alloc_api.c b/tools/testing/memblock/tests/alloc_api.c index d1aa7e15c18d..a14f38eb8a89 100644 --- a/tools/testing/memblock/tests/alloc_api.c +++ b/tools/testing/memblock/tests/alloc_api.c @@ -10,6 +10,8 @@ static int alloc_top_down_simple_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t size = SZ_2; phys_addr_t expected_start; @@ -19,12 +21,14 @@ static int alloc_top_down_simple_check(void) allocated_ptr = memblock_alloc(size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == size); - assert(rgn->base == expected_start); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, expected_start); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -55,6 +59,8 @@ static int alloc_top_down_disjoint_check(void) struct region r1; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r2_size = SZ_16; /* Use custom alignment */ phys_addr_t alignment = SMP_CACHE_BYTES * 2; @@ -73,15 +79,17 @@ static int alloc_top_down_disjoint_check(void) allocated_ptr = memblock_alloc(r2_size, alignment); - assert(allocated_ptr); - assert(rgn1->size == r1.size); - assert(rgn1->base == r1.base); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn1->size, r1.size); + ASSERT_EQ(rgn1->base, r1.base); - assert(rgn2->size == r2_size); - assert(rgn2->base == expected_start); + ASSERT_EQ(rgn2->size, r2_size); + ASSERT_EQ(rgn2->base, expected_start); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -101,6 +109,8 @@ static int alloc_top_down_before_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + /* * The first region ends at the aligned address to test region merging */ @@ -114,12 +124,14 @@ static int alloc_top_down_before_check(void) allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == total_size); - assert(rgn->base == memblock_end_of_DRAM() - total_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, memblock_end_of_DRAM() - total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -141,6 +153,8 @@ static int alloc_top_down_after_check(void) struct region r1; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r2_size = SZ_512; phys_addr_t total_size; @@ -158,12 +172,14 @@ static int alloc_top_down_after_check(void) allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == total_size); - assert(rgn->base == r1.base - r2_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, r1.base - r2_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -186,6 +202,8 @@ static int alloc_top_down_second_fit_check(void) struct region r1, r2; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_1K; phys_addr_t total_size; @@ -204,12 +222,14 @@ static int alloc_top_down_second_fit_check(void) allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == r2.size + r3_size); - assert(rgn->base == r2.base - r3_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, r2.size + r3_size); + ASSERT_EQ(rgn->base, r2.base - r3_size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -231,6 +251,8 @@ static int alloc_in_between_generic_check(void) struct region r1, r2; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t r3_size = SZ_64; /* @@ -254,12 +276,14 @@ static int alloc_in_between_generic_check(void) allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == total_size); - assert(rgn->base == r1.base - r2.size - r3_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, r1.base - r2.size - r3_size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -281,6 +305,8 @@ static int alloc_small_gaps_generic_check(void) { void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t region_size = SZ_1K; phys_addr_t gap_size = SZ_256; phys_addr_t region_end; @@ -296,7 +322,9 @@ static int alloc_small_gaps_generic_check(void) allocated_ptr = memblock_alloc(region_size, SMP_CACHE_BYTES); - assert(!allocated_ptr); + ASSERT_EQ(allocated_ptr, NULL); + + test_pass_pop(); return 0; } @@ -309,6 +337,8 @@ static int alloc_all_reserved_generic_check(void) { void *allocated_ptr = NULL; + PREFIX_PUSH(); + setup_memblock(); /* Simulate full memory */ @@ -316,7 +346,9 @@ static int alloc_all_reserved_generic_check(void) allocated_ptr = memblock_alloc(SZ_256, SMP_CACHE_BYTES); - assert(!allocated_ptr); + ASSERT_EQ(allocated_ptr, NULL); + + test_pass_pop(); return 0; } @@ -338,6 +370,8 @@ static int alloc_no_space_generic_check(void) { void *allocated_ptr = NULL; + PREFIX_PUSH(); + setup_memblock(); phys_addr_t available_size = SZ_256; @@ -348,7 +382,9 @@ static int alloc_no_space_generic_check(void) allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES); - assert(!allocated_ptr); + ASSERT_EQ(allocated_ptr, NULL); + + test_pass_pop(); return 0; } @@ -369,6 +405,8 @@ static int alloc_limited_space_generic_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t available_size = SZ_256; phys_addr_t reserved_size = MEM_SIZE - available_size; @@ -379,12 +417,14 @@ static int alloc_limited_space_generic_check(void) allocated_ptr = memblock_alloc(available_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == MEM_SIZE); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, MEM_SIZE); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, MEM_SIZE); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == MEM_SIZE); + test_pass_pop(); return 0; } @@ -399,14 +439,18 @@ static int alloc_no_memory_generic_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + reset_memblock_regions(); allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES); - assert(!allocated_ptr); - assert(rgn->size == 0); - assert(rgn->base == 0); - assert(memblock.reserved.total_size == 0); + ASSERT_EQ(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, 0); + ASSERT_EQ(rgn->base, 0); + ASSERT_EQ(memblock.reserved.total_size, 0); + + test_pass_pop(); return 0; } @@ -421,16 +465,20 @@ static int alloc_bottom_up_simple_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + setup_memblock(); allocated_ptr = memblock_alloc(SZ_2, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == SZ_2); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, SZ_2); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == SZ_2); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, SZ_2); + + test_pass_pop(); return 0; } @@ -459,6 +507,8 @@ static int alloc_bottom_up_disjoint_check(void) struct region r1; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r2_size = SZ_16; /* Use custom alignment */ phys_addr_t alignment = SMP_CACHE_BYTES * 2; @@ -477,16 +527,18 @@ static int alloc_bottom_up_disjoint_check(void) allocated_ptr = memblock_alloc(r2_size, alignment); - assert(allocated_ptr); + ASSERT_NE(allocated_ptr, NULL); - assert(rgn1->size == r1.size); - assert(rgn1->base == r1.base); + ASSERT_EQ(rgn1->size, r1.size); + ASSERT_EQ(rgn1->base, r1.base); - assert(rgn2->size == r2_size); - assert(rgn2->base == expected_start); + ASSERT_EQ(rgn2->size, r2_size); + ASSERT_EQ(rgn2->base, expected_start); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -506,6 +558,8 @@ static int alloc_bottom_up_before_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_512; phys_addr_t r2_size = SZ_128; phys_addr_t total_size = r1_size + r2_size; @@ -516,12 +570,14 @@ static int alloc_bottom_up_before_check(void) allocated_ptr = memblock_alloc(r1_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == total_size); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -542,6 +598,8 @@ static int alloc_bottom_up_after_check(void) struct region r1; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r2_size = SZ_512; phys_addr_t total_size; @@ -559,12 +617,14 @@ static int alloc_bottom_up_after_check(void) allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == total_size); - assert(rgn->base == r1.base); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, r1.base); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -588,6 +648,8 @@ static int alloc_bottom_up_second_fit_check(void) struct region r1, r2; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_1K; phys_addr_t total_size; @@ -606,12 +668,14 @@ static int alloc_bottom_up_second_fit_check(void) allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES); - assert(allocated_ptr); - assert(rgn->size == r2.size + r3_size); - assert(rgn->base == r2.base); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, r2.size + r3_size); + ASSERT_EQ(rgn->base, r2.base); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -619,6 +683,7 @@ static int alloc_bottom_up_second_fit_check(void) /* Test case wrappers */ static int alloc_simple_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_top_down_simple_check(); memblock_set_bottom_up(true); @@ -629,6 +694,7 @@ static int alloc_simple_check(void) static int alloc_disjoint_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_top_down_disjoint_check(); memblock_set_bottom_up(true); @@ -639,6 +705,7 @@ static int alloc_disjoint_check(void) static int alloc_before_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_top_down_before_check(); memblock_set_bottom_up(true); @@ -649,6 +716,7 @@ static int alloc_before_check(void) static int alloc_after_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_top_down_after_check(); memblock_set_bottom_up(true); @@ -659,6 +727,7 @@ static int alloc_after_check(void) static int alloc_in_between_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_in_between_generic_check(); memblock_set_bottom_up(true); @@ -669,6 +738,7 @@ static int alloc_in_between_check(void) static int alloc_second_fit_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_top_down_second_fit_check(); memblock_set_bottom_up(true); @@ -679,6 +749,7 @@ static int alloc_second_fit_check(void) static int alloc_small_gaps_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_small_gaps_generic_check(); memblock_set_bottom_up(true); @@ -689,6 +760,7 @@ static int alloc_small_gaps_check(void) static int alloc_all_reserved_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_all_reserved_generic_check(); memblock_set_bottom_up(true); @@ -699,6 +771,7 @@ static int alloc_all_reserved_check(void) static int alloc_no_space_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_no_space_generic_check(); memblock_set_bottom_up(true); @@ -709,6 +782,7 @@ static int alloc_no_space_check(void) static int alloc_limited_space_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_limited_space_generic_check(); memblock_set_bottom_up(true); @@ -719,6 +793,7 @@ static int alloc_limited_space_check(void) static int alloc_no_memory_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_no_memory_generic_check(); memblock_set_bottom_up(true); @@ -729,6 +804,12 @@ static int alloc_no_memory_check(void) int memblock_alloc_checks(void) { + const char *func_testing = "memblock_alloc"; + + prefix_reset(); + prefix_push(func_testing); + test_print("Running %s tests...\n", func_testing); + reset_memblock_attributes(); dummy_physical_memory_init(); @@ -746,5 +827,7 @@ int memblock_alloc_checks(void) dummy_physical_memory_cleanup(); + prefix_pop(); + return 0; } diff --git a/tools/testing/memblock/tests/alloc_helpers_api.c b/tools/testing/memblock/tests/alloc_helpers_api.c index 963a966db461..1069b4bdd5fd 100644 --- a/tools/testing/memblock/tests/alloc_helpers_api.c +++ b/tools/testing/memblock/tests/alloc_helpers_api.c @@ -21,6 +21,8 @@ static int alloc_from_simple_generic_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_16; phys_addr_t min_addr; @@ -31,14 +33,16 @@ static int alloc_from_simple_generic_check(void) allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, min_addr); - assert(rgn->size == size); - assert(rgn->base == min_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -64,6 +68,8 @@ static int alloc_from_misaligned_generic_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_32; phys_addr_t min_addr; @@ -75,14 +81,16 @@ static int alloc_from_misaligned_generic_check(void) allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == size); - assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_end_of_DRAM() - SMP_CACHE_BYTES); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); return 0; } @@ -110,6 +118,8 @@ static int alloc_from_top_down_high_addr_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t size = SZ_32; phys_addr_t min_addr; @@ -120,12 +130,14 @@ static int alloc_from_top_down_high_addr_check(void) allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->size == size); - assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_end_of_DRAM() - SMP_CACHE_BYTES); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); return 0; } @@ -151,6 +163,8 @@ static int alloc_from_top_down_no_space_above_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_64; phys_addr_t r2_size = SZ_2; phys_addr_t total_size = r1_size + r2_size; @@ -165,12 +179,14 @@ static int alloc_from_top_down_no_space_above_check(void) allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->base == min_addr - r1_size); - assert(rgn->size == total_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->base, min_addr - r1_size); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -186,6 +202,8 @@ static int alloc_from_top_down_min_addr_cap_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_64; phys_addr_t min_addr; phys_addr_t start_addr; @@ -199,12 +217,14 @@ static int alloc_from_top_down_min_addr_cap_check(void) allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->base == start_addr); - assert(rgn->size == MEM_SIZE); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->base, start_addr); + ASSERT_EQ(rgn->size, MEM_SIZE); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, MEM_SIZE); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == MEM_SIZE); + test_pass_pop(); return 0; } @@ -230,6 +250,8 @@ static int alloc_from_bottom_up_high_addr_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t size = SZ_32; phys_addr_t min_addr; @@ -240,12 +262,14 @@ static int alloc_from_bottom_up_high_addr_check(void) allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->size == size); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -270,6 +294,8 @@ static int alloc_from_bottom_up_no_space_above_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_64; phys_addr_t min_addr; phys_addr_t r2_size; @@ -284,12 +310,14 @@ static int alloc_from_bottom_up_no_space_above_check(void) allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->base == memblock_start_of_DRAM()); - assert(rgn->size == r1_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); + ASSERT_EQ(rgn->size, r1_size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == r1_size + r2_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, r1_size + r2_size); + + test_pass_pop(); return 0; } @@ -304,6 +332,8 @@ static int alloc_from_bottom_up_min_addr_cap_check(void) struct memblock_region *rgn = &memblock.reserved.regions[0]; void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_64; phys_addr_t min_addr; phys_addr_t start_addr; @@ -315,12 +345,14 @@ static int alloc_from_bottom_up_min_addr_cap_check(void) allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr); - assert(allocated_ptr); - assert(rgn->base == start_addr); - assert(rgn->size == r1_size); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(rgn->base, start_addr); + ASSERT_EQ(rgn->size, r1_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == r1_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, r1_size); + + test_pass_pop(); return 0; } @@ -328,6 +360,7 @@ static int alloc_from_bottom_up_min_addr_cap_check(void) /* Test case wrappers */ static int alloc_from_simple_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_from_simple_generic_check(); memblock_set_bottom_up(true); @@ -338,6 +371,7 @@ static int alloc_from_simple_check(void) static int alloc_from_misaligned_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_from_misaligned_generic_check(); memblock_set_bottom_up(true); @@ -348,6 +382,7 @@ static int alloc_from_misaligned_check(void) static int alloc_from_high_addr_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_from_top_down_high_addr_check(); memblock_set_bottom_up(true); @@ -358,6 +393,7 @@ static int alloc_from_high_addr_check(void) static int alloc_from_no_space_above_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_from_top_down_no_space_above_check(); memblock_set_bottom_up(true); @@ -368,6 +404,7 @@ static int alloc_from_no_space_above_check(void) static int alloc_from_min_addr_cap_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_from_top_down_min_addr_cap_check(); memblock_set_bottom_up(true); @@ -378,6 +415,12 @@ static int alloc_from_min_addr_cap_check(void) int memblock_alloc_helpers_checks(void) { + const char *func_testing = "memblock_alloc_from"; + + prefix_reset(); + prefix_push(func_testing); + test_print("Running %s tests...\n", func_testing); + reset_memblock_attributes(); dummy_physical_memory_init(); @@ -389,5 +432,7 @@ int memblock_alloc_helpers_checks(void) dummy_physical_memory_cleanup(); + prefix_pop(); + return 0; } diff --git a/tools/testing/memblock/tests/alloc_nid_api.c b/tools/testing/memblock/tests/alloc_nid_api.c index 6390206e50e1..255fd514e9f5 100644 --- a/tools/testing/memblock/tests/alloc_nid_api.c +++ b/tools/testing/memblock/tests/alloc_nid_api.c @@ -21,6 +21,8 @@ static int alloc_try_nid_top_down_simple_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_128; phys_addr_t min_addr; phys_addr_t max_addr; @@ -36,15 +38,17 @@ static int alloc_try_nid_top_down_simple_check(void) b = (char *)allocated_ptr; rgn_end = rgn->base + rgn->size; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, max_addr - size); + ASSERT_EQ(rgn_end, max_addr); - assert(rgn->size == size); - assert(rgn->base == max_addr - size); - assert(rgn_end == max_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -72,6 +76,8 @@ static int alloc_try_nid_top_down_end_misaligned_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_128; phys_addr_t misalign = SZ_2; phys_addr_t min_addr; @@ -88,15 +94,17 @@ static int alloc_try_nid_top_down_end_misaligned_check(void) b = (char *)allocated_ptr; rgn_end = rgn->base + rgn->size; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == size); - assert(rgn->base == max_addr - size - misalign); - assert(rgn_end < max_addr); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, max_addr - size - misalign); + ASSERT_LT(rgn_end, max_addr); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); return 0; } @@ -122,6 +130,8 @@ static int alloc_try_nid_exact_address_generic_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_1K; phys_addr_t min_addr; phys_addr_t max_addr; @@ -137,15 +147,17 @@ static int alloc_try_nid_exact_address_generic_check(void) b = (char *)allocated_ptr; rgn_end = rgn->base + rgn->size; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, min_addr); + ASSERT_EQ(rgn_end, max_addr); - assert(rgn->size == size); - assert(rgn->base == min_addr); - assert(rgn_end == max_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -173,6 +185,8 @@ static int alloc_try_nid_top_down_narrow_range_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_256; phys_addr_t min_addr; phys_addr_t max_addr; @@ -186,14 +200,16 @@ static int alloc_try_nid_top_down_narrow_range_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, max_addr - size); - assert(rgn->size == size); - assert(rgn->base == max_addr - size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -222,6 +238,8 @@ static int alloc_try_nid_low_max_generic_check(void) { void *allocated_ptr = NULL; + PREFIX_PUSH(); + phys_addr_t size = SZ_1K; phys_addr_t min_addr; phys_addr_t max_addr; @@ -234,7 +252,9 @@ static int alloc_try_nid_low_max_generic_check(void) allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES, min_addr, max_addr, NUMA_NO_NODE); - assert(!allocated_ptr); + ASSERT_EQ(allocated_ptr, NULL); + + test_pass_pop(); return 0; } @@ -259,6 +279,8 @@ static int alloc_try_nid_min_reserved_generic_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_128; phys_addr_t r2_size = SZ_64; phys_addr_t total_size = r1_size + r2_size; @@ -278,14 +300,16 @@ static int alloc_try_nid_min_reserved_generic_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == total_size); - assert(rgn->base == reserved_base); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, reserved_base); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -310,6 +334,8 @@ static int alloc_try_nid_max_reserved_generic_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t r1_size = SZ_64; phys_addr_t r2_size = SZ_128; phys_addr_t total_size = r1_size + r2_size; @@ -327,14 +353,16 @@ static int alloc_try_nid_max_reserved_generic_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, min_addr); - assert(rgn->size == total_size); - assert(rgn->base == min_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -364,6 +392,8 @@ static int alloc_try_nid_top_down_reserved_with_space_check(void) char *b; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_64; phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t total_size; @@ -389,17 +419,19 @@ static int alloc_try_nid_top_down_reserved_with_space_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn1->size, r1.size + r3_size); + ASSERT_EQ(rgn1->base, max_addr - r3_size); - assert(rgn1->size == r1.size + r3_size); - assert(rgn1->base == max_addr - r3_size); + ASSERT_EQ(rgn2->size, r2.size); + ASSERT_EQ(rgn2->base, r2.base); - assert(rgn2->size == r2.size); - assert(rgn2->base == r2.base); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -427,6 +459,8 @@ static int alloc_try_nid_reserved_full_merge_generic_check(void) char *b; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_64; phys_addr_t total_size; phys_addr_t max_addr; @@ -451,14 +485,16 @@ static int alloc_try_nid_reserved_full_merge_generic_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == total_size); - assert(rgn->base == r2.base); + ASSERT_EQ(rgn->size, total_size); + ASSERT_EQ(rgn->base, r2.base); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -489,6 +525,8 @@ static int alloc_try_nid_top_down_reserved_no_space_check(void) char *b; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_256; phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t total_size; @@ -514,17 +552,19 @@ static int alloc_try_nid_top_down_reserved_no_space_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn1->size, r1.size); + ASSERT_EQ(rgn1->base, r1.base); - assert(rgn1->size == r1.size); - assert(rgn1->base == r1.base); + ASSERT_EQ(rgn2->size, r2.size + r3_size); + ASSERT_EQ(rgn2->base, r2.base - r3_size); - assert(rgn2->size == r2.size + r3_size); - assert(rgn2->base == r2.base - r3_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -554,6 +594,8 @@ static int alloc_try_nid_reserved_all_generic_check(void) void *allocated_ptr = NULL; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_256; phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t max_addr; @@ -576,7 +618,9 @@ static int alloc_try_nid_reserved_all_generic_check(void) allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES, min_addr, max_addr, NUMA_NO_NODE); - assert(!allocated_ptr); + ASSERT_EQ(allocated_ptr, NULL); + + test_pass_pop(); return 0; } @@ -592,6 +636,8 @@ static int alloc_try_nid_top_down_cap_max_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_256; phys_addr_t min_addr; phys_addr_t max_addr; @@ -605,14 +651,16 @@ static int alloc_try_nid_top_down_cap_max_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_end_of_DRAM() - size); - assert(rgn->size == size); - assert(rgn->base == memblock_end_of_DRAM() - size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -628,6 +676,8 @@ static int alloc_try_nid_top_down_cap_min_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_1K; phys_addr_t min_addr; phys_addr_t max_addr; @@ -641,14 +691,16 @@ static int alloc_try_nid_top_down_cap_min_check(void) min_addr, max_addr, NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == size); - assert(rgn->base == memblock_end_of_DRAM() - size); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_end_of_DRAM() - size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); return 0; } @@ -673,6 +725,8 @@ static int alloc_try_nid_bottom_up_simple_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_128; phys_addr_t min_addr; phys_addr_t max_addr; @@ -689,15 +743,17 @@ static int alloc_try_nid_bottom_up_simple_check(void) b = (char *)allocated_ptr; rgn_end = rgn->base + rgn->size; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, min_addr); + ASSERT_LT(rgn_end, max_addr); - assert(rgn->size == size); - assert(rgn->base == min_addr); - assert(rgn_end < max_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -725,6 +781,8 @@ static int alloc_try_nid_bottom_up_start_misaligned_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_128; phys_addr_t misalign = SZ_2; phys_addr_t min_addr; @@ -742,15 +800,17 @@ static int alloc_try_nid_bottom_up_start_misaligned_check(void) b = (char *)allocated_ptr; rgn_end = rgn->base + rgn->size; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, min_addr + (SMP_CACHE_BYTES - misalign)); + ASSERT_LT(rgn_end, max_addr); - assert(rgn->size == size); - assert(rgn->base == min_addr + (SMP_CACHE_BYTES - misalign)); - assert(rgn_end < max_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -778,6 +838,8 @@ static int alloc_try_nid_bottom_up_narrow_range_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_256; phys_addr_t min_addr; phys_addr_t max_addr; @@ -792,14 +854,16 @@ static int alloc_try_nid_bottom_up_narrow_range_check(void) NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn->size == size); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); + + test_pass_pop(); return 0; } @@ -829,6 +893,8 @@ static int alloc_try_nid_bottom_up_reserved_with_space_check(void) char *b; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_64; phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t total_size; @@ -855,17 +921,19 @@ static int alloc_try_nid_bottom_up_reserved_with_space_check(void) NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); - assert(rgn1->size == r1.size); - assert(rgn1->base == max_addr); + ASSERT_EQ(rgn1->size, r1.size); + ASSERT_EQ(rgn1->base, max_addr); - assert(rgn2->size == r2.size + r3_size); - assert(rgn2->base == r2.base); + ASSERT_EQ(rgn2->size, r2.size + r3_size); + ASSERT_EQ(rgn2->base, r2.base); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -899,6 +967,8 @@ static int alloc_try_nid_bottom_up_reserved_no_space_check(void) char *b; struct region r1, r2; + PREFIX_PUSH(); + phys_addr_t r3_size = SZ_256; phys_addr_t gap_size = SMP_CACHE_BYTES; phys_addr_t total_size; @@ -925,20 +995,22 @@ static int alloc_try_nid_bottom_up_reserved_no_space_check(void) NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn3->size, r3_size); + ASSERT_EQ(rgn3->base, memblock_start_of_DRAM()); - assert(rgn3->size == r3_size); - assert(rgn3->base == memblock_start_of_DRAM()); + ASSERT_EQ(rgn2->size, r2.size); + ASSERT_EQ(rgn2->base, r2.base); - assert(rgn2->size == r2.size); - assert(rgn2->base == r2.base); + ASSERT_EQ(rgn1->size, r1.size); + ASSERT_EQ(rgn1->base, r1.base); - assert(rgn1->size == r1.size); - assert(rgn1->base == r1.base); + ASSERT_EQ(memblock.reserved.cnt, 3); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 3); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -954,6 +1026,8 @@ static int alloc_try_nid_bottom_up_cap_max_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_256; phys_addr_t min_addr; phys_addr_t max_addr; @@ -968,14 +1042,16 @@ static int alloc_try_nid_bottom_up_cap_max_check(void) NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, min_addr); - assert(rgn->size == size); - assert(rgn->base == min_addr); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -991,6 +1067,8 @@ static int alloc_try_nid_bottom_up_cap_min_check(void) void *allocated_ptr = NULL; char *b; + PREFIX_PUSH(); + phys_addr_t size = SZ_1K; phys_addr_t min_addr; phys_addr_t max_addr; @@ -1005,14 +1083,16 @@ static int alloc_try_nid_bottom_up_cap_min_check(void) NUMA_NO_NODE); b = (char *)allocated_ptr; - assert(allocated_ptr); - assert(*b == 0); + ASSERT_NE(allocated_ptr, NULL); + ASSERT_EQ(*b, 0); + + ASSERT_EQ(rgn->size, size); + ASSERT_EQ(rgn->base, memblock_start_of_DRAM()); - assert(rgn->size == size); - assert(rgn->base == memblock_start_of_DRAM()); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == size); + test_pass_pop(); return 0; } @@ -1020,6 +1100,7 @@ static int alloc_try_nid_bottom_up_cap_min_check(void) /* Test case wrappers */ static int alloc_try_nid_simple_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_simple_check(); memblock_set_bottom_up(true); @@ -1030,6 +1111,7 @@ static int alloc_try_nid_simple_check(void) static int alloc_try_nid_misaligned_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_end_misaligned_check(); memblock_set_bottom_up(true); @@ -1040,6 +1122,7 @@ static int alloc_try_nid_misaligned_check(void) static int alloc_try_nid_narrow_range_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_narrow_range_check(); memblock_set_bottom_up(true); @@ -1050,6 +1133,7 @@ static int alloc_try_nid_narrow_range_check(void) static int alloc_try_nid_reserved_with_space_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_reserved_with_space_check(); memblock_set_bottom_up(true); @@ -1060,6 +1144,7 @@ static int alloc_try_nid_reserved_with_space_check(void) static int alloc_try_nid_reserved_no_space_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_reserved_no_space_check(); memblock_set_bottom_up(true); @@ -1070,6 +1155,7 @@ static int alloc_try_nid_reserved_no_space_check(void) static int alloc_try_nid_cap_max_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_cap_max_check(); memblock_set_bottom_up(true); @@ -1080,6 +1166,7 @@ static int alloc_try_nid_cap_max_check(void) static int alloc_try_nid_cap_min_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_top_down_cap_min_check(); memblock_set_bottom_up(true); @@ -1090,6 +1177,7 @@ static int alloc_try_nid_cap_min_check(void) static int alloc_try_nid_min_reserved_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_min_reserved_generic_check(); memblock_set_bottom_up(true); @@ -1100,6 +1188,7 @@ static int alloc_try_nid_min_reserved_check(void) static int alloc_try_nid_max_reserved_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_max_reserved_generic_check(); memblock_set_bottom_up(true); @@ -1110,6 +1199,7 @@ static int alloc_try_nid_max_reserved_check(void) static int alloc_try_nid_exact_address_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_exact_address_generic_check(); memblock_set_bottom_up(true); @@ -1120,6 +1210,7 @@ static int alloc_try_nid_exact_address_check(void) static int alloc_try_nid_reserved_full_merge_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_reserved_full_merge_generic_check(); memblock_set_bottom_up(true); @@ -1130,6 +1221,7 @@ static int alloc_try_nid_reserved_full_merge_check(void) static int alloc_try_nid_reserved_all_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_reserved_all_generic_check(); memblock_set_bottom_up(true); @@ -1140,6 +1232,7 @@ static int alloc_try_nid_reserved_all_check(void) static int alloc_try_nid_low_max_check(void) { + test_print("\tRunning %s...\n", __func__); memblock_set_bottom_up(false); alloc_try_nid_low_max_generic_check(); memblock_set_bottom_up(true); @@ -1150,6 +1243,12 @@ static int alloc_try_nid_low_max_check(void) int memblock_alloc_nid_checks(void) { + const char *func_testing = "memblock_alloc_try_nid"; + + prefix_reset(); + prefix_push(func_testing); + test_print("Running %s tests...\n", func_testing); + reset_memblock_attributes(); dummy_physical_memory_init(); @@ -1170,5 +1269,7 @@ int memblock_alloc_nid_checks(void) dummy_physical_memory_cleanup(); + prefix_pop(); + return 0; } diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index a7bc180316d6..66f46f261e66 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -4,21 +4,29 @@ #include "basic_api.h" #define EXPECTED_MEMBLOCK_REGIONS 128 +#define FUNC_ADD "memblock_add" +#define FUNC_RESERVE "memblock_reserve" +#define FUNC_REMOVE "memblock_remove" +#define FUNC_FREE "memblock_free" static int memblock_initialization_check(void) { - assert(memblock.memory.regions); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.max == EXPECTED_MEMBLOCK_REGIONS); - assert(strcmp(memblock.memory.name, "memory") == 0); + PREFIX_PUSH(); - assert(memblock.reserved.regions); - assert(memblock.reserved.cnt == 1); - assert(memblock.memory.max == EXPECTED_MEMBLOCK_REGIONS); - assert(strcmp(memblock.reserved.name, "reserved") == 0); + ASSERT_NE(memblock.memory.regions, NULL); + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.max, EXPECTED_MEMBLOCK_REGIONS); + ASSERT_EQ(strcmp(memblock.memory.name, "memory"), 0); - assert(!memblock.bottom_up); - assert(memblock.current_limit == MEMBLOCK_ALLOC_ANYWHERE); + ASSERT_NE(memblock.reserved.regions, NULL); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.memory.max, EXPECTED_MEMBLOCK_REGIONS); + ASSERT_EQ(strcmp(memblock.reserved.name, "reserved"), 0); + + ASSERT_EQ(memblock.bottom_up, false); + ASSERT_EQ(memblock.current_limit, MEMBLOCK_ALLOC_ANYWHERE); + + test_pass_pop(); return 0; } @@ -40,14 +48,18 @@ static int memblock_add_simple_check(void) .size = SZ_4M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r.base, r.size); - assert(rgn->base == r.base); - assert(rgn->size == r.size); + ASSERT_EQ(rgn->base, r.base); + ASSERT_EQ(rgn->size, r.size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r.size); + test_pass_pop(); return 0; } @@ -69,18 +81,22 @@ static int memblock_add_node_simple_check(void) .size = SZ_16M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add_node(r.base, r.size, 1, MEMBLOCK_HOTPLUG); - assert(rgn->base == r.base); - assert(rgn->size == r.size); + ASSERT_EQ(rgn->base, r.base); + ASSERT_EQ(rgn->size, r.size); #ifdef CONFIG_NUMA - assert(rgn->nid == 1); + ASSERT_EQ(rgn->nid, 1); #endif - assert(rgn->flags == MEMBLOCK_HOTPLUG); + ASSERT_EQ(rgn->flags, MEMBLOCK_HOTPLUG); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r.size); + test_pass_pop(); return 0; } @@ -113,18 +129,22 @@ static int memblock_add_disjoint_check(void) .size = SZ_8K }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_add(r2.base, r2.size); - assert(rgn1->base == r1.base); - assert(rgn1->size == r1.size); + ASSERT_EQ(rgn1->base, r1.base); + ASSERT_EQ(rgn1->size, r1.size); + + ASSERT_EQ(rgn2->base, r2.base); + ASSERT_EQ(rgn2->size, r2.size); - assert(rgn2->base == r2.base); - assert(rgn2->size == r2.size); + ASSERT_EQ(memblock.memory.cnt, 2); + ASSERT_EQ(memblock.memory.total_size, r1.size + r2.size); - assert(memblock.memory.cnt == 2); - assert(memblock.memory.total_size == r1.size + r2.size); + test_pass_pop(); return 0; } @@ -162,17 +182,21 @@ static int memblock_add_overlap_top_check(void) .size = SZ_512M }; + PREFIX_PUSH(); + total_size = (r1.base - r2.base) + r1.size; reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_add(r2.base, r2.size); - assert(rgn->base == r2.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r2.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, total_size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == total_size); + test_pass_pop(); return 0; } @@ -210,17 +234,21 @@ static int memblock_add_overlap_bottom_check(void) .size = SZ_1G }; + PREFIX_PUSH(); + total_size = (r2.base - r1.base) + r2.size; reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_add(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, total_size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == total_size); + test_pass_pop(); return 0; } @@ -255,15 +283,19 @@ static int memblock_add_within_check(void) .size = SZ_1M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_add(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == r1.size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, r1.size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r1.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r1.size); + test_pass_pop(); return 0; } @@ -279,19 +311,27 @@ static int memblock_add_twice_check(void) .size = SZ_2M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r.base, r.size); memblock_add(r.base, r.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r.size); + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r.size); + + test_pass_pop(); return 0; } static int memblock_add_checks(void) { + prefix_reset(); + prefix_push(FUNC_ADD); + test_print("Running %s tests...\n", FUNC_ADD); + memblock_add_simple_check(); memblock_add_node_simple_check(); memblock_add_disjoint_check(); @@ -300,6 +340,8 @@ static int memblock_add_checks(void) memblock_add_within_check(); memblock_add_twice_check(); + prefix_pop(); + return 0; } @@ -320,11 +362,15 @@ static int memblock_reserve_simple_check(void) .size = SZ_128M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r.base, r.size); - assert(rgn->base == r.base); - assert(rgn->size == r.size); + ASSERT_EQ(rgn->base, r.base); + ASSERT_EQ(rgn->size, r.size); + + test_pass_pop(); return 0; } @@ -356,18 +402,22 @@ static int memblock_reserve_disjoint_check(void) .size = SZ_512M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - assert(rgn1->base == r1.base); - assert(rgn1->size == r1.size); + ASSERT_EQ(rgn1->base, r1.base); + ASSERT_EQ(rgn1->size, r1.size); + + ASSERT_EQ(rgn2->base, r2.base); + ASSERT_EQ(rgn2->size, r2.size); - assert(rgn2->base == r2.base); - assert(rgn2->size == r2.size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, r1.size + r2.size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == r1.size + r2.size); + test_pass_pop(); return 0; } @@ -406,17 +456,21 @@ static int memblock_reserve_overlap_top_check(void) .size = SZ_1G }; + PREFIX_PUSH(); + total_size = (r1.base - r2.base) + r1.size; reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - assert(rgn->base == r2.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r2.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -455,17 +509,21 @@ static int memblock_reserve_overlap_bottom_check(void) .size = SZ_128K }; + PREFIX_PUSH(); + total_size = (r2.base - r1.base) + r2.size; reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + test_pass_pop(); return 0; } @@ -502,15 +560,19 @@ static int memblock_reserve_within_check(void) .size = SZ_64K }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == r1.size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, r1.size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, r1.size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == r1.size); + test_pass_pop(); return 0; } @@ -527,19 +589,27 @@ static int memblock_reserve_twice_check(void) .size = SZ_2M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r.base, r.size); memblock_reserve(r.base, r.size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == r.size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, r.size); + + test_pass_pop(); return 0; } static int memblock_reserve_checks(void) { + prefix_reset(); + prefix_push(FUNC_RESERVE); + test_print("Running %s tests...\n", FUNC_RESERVE); + memblock_reserve_simple_check(); memblock_reserve_disjoint_check(); memblock_reserve_overlap_top_check(); @@ -547,6 +617,8 @@ static int memblock_reserve_checks(void) memblock_reserve_within_check(); memblock_reserve_twice_check(); + prefix_pop(); + return 0; } @@ -581,16 +653,20 @@ static int memblock_remove_simple_check(void) .size = SZ_4M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_add(r2.base, r2.size); memblock_remove(r1.base, r1.size); - assert(rgn->base == r2.base); - assert(rgn->size == r2.size); + ASSERT_EQ(rgn->base, r2.base); + ASSERT_EQ(rgn->size, r2.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r2.size); + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r2.size); + + test_pass_pop(); return 0; } @@ -626,15 +702,19 @@ static int memblock_remove_absent_check(void) .size = SZ_1G }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_remove(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == r1.size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, r1.size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, r1.size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == r1.size); + test_pass_pop(); return 0; } @@ -674,6 +754,8 @@ static int memblock_remove_overlap_top_check(void) .size = SZ_32M }; + PREFIX_PUSH(); + r1_end = r1.base + r1.size; r2_end = r2.base + r2.size; total_size = r1_end - r2_end; @@ -682,11 +764,13 @@ static int memblock_remove_overlap_top_check(void) memblock_add(r1.base, r1.size); memblock_remove(r2.base, r2.size); - assert(rgn->base == r1.base + r2.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r1.base + r2.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, total_size); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == total_size); + test_pass_pop(); return 0; } @@ -724,17 +808,22 @@ static int memblock_remove_overlap_bottom_check(void) .size = SZ_256M }; + PREFIX_PUSH(); + total_size = r2.base - r1.base; reset_memblock_regions(); memblock_add(r1.base, r1.size); memblock_remove(r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, total_size); + + ASSERT_EQ(memblock.memory.cnt, 1); + ASSERT_EQ(memblock.memory.total_size, total_size); + + test_pass_pop(); - assert(memblock.memory.cnt == 1); - assert(memblock.memory.total_size == total_size); return 0; } @@ -774,6 +863,8 @@ static int memblock_remove_within_check(void) .size = SZ_1M }; + PREFIX_PUSH(); + r1_size = r2.base - r1.base; r2_size = (r1.base + r1.size) - (r2.base + r2.size); total_size = r1_size + r2_size; @@ -782,26 +873,34 @@ static int memblock_remove_within_check(void) memblock_add(r1.base, r1.size); memblock_remove(r2.base, r2.size); - assert(rgn1->base == r1.base); - assert(rgn1->size == r1_size); + ASSERT_EQ(rgn1->base, r1.base); + ASSERT_EQ(rgn1->size, r1_size); + + ASSERT_EQ(rgn2->base, r2.base + r2.size); + ASSERT_EQ(rgn2->size, r2_size); - assert(rgn2->base == r2.base + r2.size); - assert(rgn2->size == r2_size); + ASSERT_EQ(memblock.memory.cnt, 2); + ASSERT_EQ(memblock.memory.total_size, total_size); - assert(memblock.memory.cnt == 2); - assert(memblock.memory.total_size == total_size); + test_pass_pop(); return 0; } static int memblock_remove_checks(void) { + prefix_reset(); + prefix_push(FUNC_REMOVE); + test_print("Running %s tests...\n", FUNC_REMOVE); + memblock_remove_simple_check(); memblock_remove_absent_check(); memblock_remove_overlap_top_check(); memblock_remove_overlap_bottom_check(); memblock_remove_within_check(); + prefix_pop(); + return 0; } @@ -835,16 +934,20 @@ static int memblock_free_simple_check(void) .size = SZ_1M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_reserve(r2.base, r2.size); memblock_free((void *)r1.base, r1.size); - assert(rgn->base == r2.base); - assert(rgn->size == r2.size); + ASSERT_EQ(rgn->base, r2.base); + ASSERT_EQ(rgn->size, r2.size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, r2.size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == r2.size); + test_pass_pop(); return 0; } @@ -880,15 +983,19 @@ static int memblock_free_absent_check(void) .size = SZ_128M }; + PREFIX_PUSH(); + reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_free((void *)r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == r1.size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, r1.size); + + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, r1.size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == r1.size); + test_pass_pop(); return 0; } @@ -928,17 +1035,21 @@ static int memblock_free_overlap_top_check(void) .size = SZ_8M }; + PREFIX_PUSH(); + total_size = (r1.size + r1.base) - (r2.base + r2.size); reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_free((void *)r2.base, r2.size); - assert(rgn->base == r2.base + r2.size); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r2.base + r2.size); + ASSERT_EQ(rgn->size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -973,17 +1084,21 @@ static int memblock_free_overlap_bottom_check(void) .size = SZ_32M }; + PREFIX_PUSH(); + total_size = r2.base - r1.base; reset_memblock_regions(); memblock_reserve(r1.base, r1.size); memblock_free((void *)r2.base, r2.size); - assert(rgn->base == r1.base); - assert(rgn->size == total_size); + ASSERT_EQ(rgn->base, r1.base); + ASSERT_EQ(rgn->size, total_size); - assert(memblock.reserved.cnt == 1); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 1); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } @@ -1024,6 +1139,8 @@ static int memblock_free_within_check(void) .size = SZ_1M }; + PREFIX_PUSH(); + r1_size = r2.base - r1.base; r2_size = (r1.base + r1.size) - (r2.base + r2.size); total_size = r1_size + r2_size; @@ -1032,26 +1149,34 @@ static int memblock_free_within_check(void) memblock_reserve(r1.base, r1.size); memblock_free((void *)r2.base, r2.size); - assert(rgn1->base == r1.base); - assert(rgn1->size == r1_size); + ASSERT_EQ(rgn1->base, r1.base); + ASSERT_EQ(rgn1->size, r1_size); - assert(rgn2->base == r2.base + r2.size); - assert(rgn2->size == r2_size); + ASSERT_EQ(rgn2->base, r2.base + r2.size); + ASSERT_EQ(rgn2->size, r2_size); - assert(memblock.reserved.cnt == 2); - assert(memblock.reserved.total_size == total_size); + ASSERT_EQ(memblock.reserved.cnt, 2); + ASSERT_EQ(memblock.reserved.total_size, total_size); + + test_pass_pop(); return 0; } static int memblock_free_checks(void) { + prefix_reset(); + prefix_push(FUNC_FREE); + test_print("Running %s tests...\n", FUNC_FREE); + memblock_free_simple_check(); memblock_free_absent_check(); memblock_free_overlap_top_check(); memblock_free_overlap_bottom_check(); memblock_free_within_check(); + prefix_pop(); + return 0; } diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c index 62d3191f7c9a..e43b2676af81 100644 --- a/tools/testing/memblock/tests/common.c +++ b/tools/testing/memblock/tests/common.c @@ -1,11 +1,39 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "tests/common.h" #include <string.h> +#include <getopt.h> +#include <linux/memory_hotplug.h> +#include <linux/build_bug.h> #define INIT_MEMBLOCK_REGIONS 128 #define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS +#define PREFIXES_MAX 15 +#define DELIM ": " static struct test_memory memory_block; +static const char __maybe_unused *prefixes[PREFIXES_MAX]; +static int __maybe_unused nr_prefixes; + +static const char *short_opts = "mv"; +static const struct option long_opts[] = { + {"movable-node", 0, NULL, 'm'}, + {"verbose", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +static const char * const help_opts[] = { + "disallow allocations from regions marked as hotplugged\n\t\t\t" + "by simulating enabling the \"movable_node\" kernel\n\t\t\t" + "parameter", + "enable verbose output, which includes the name of the\n\t\t\t" + "memblock function being tested, the name of the test,\n\t\t\t" + "and whether the test passed or failed." +}; + +static int verbose; + +/* sets global variable returned by movable_node_is_enabled() stub */ +bool movable_node_enabled; void reset_memblock_regions(void) { @@ -46,3 +74,93 @@ void dummy_physical_memory_cleanup(void) { free(memory_block.base); } + +static void usage(const char *prog) +{ + BUILD_BUG_ON(ARRAY_SIZE(help_opts) != ARRAY_SIZE(long_opts) - 1); + + printf("Usage: %s [-%s]\n", prog, short_opts); + + for (int i = 0; long_opts[i].name; i++) { + printf(" -%c, --%-12s\t%s\n", long_opts[i].val, + long_opts[i].name, help_opts[i]); + } + + exit(1); +} + +void parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt_long_only(argc, argv, short_opts, long_opts, + NULL)) != -1) { + switch (c) { + case 'm': + movable_node_enabled = true; + break; + case 'v': + verbose = 1; + break; + default: + usage(argv[0]); + } + } +} + +void print_prefixes(const char *postfix) +{ + for (int i = 0; i < nr_prefixes; i++) + test_print("%s%s", prefixes[i], DELIM); + test_print(postfix); +} + +void test_fail(void) +{ + if (verbose) { + ksft_test_result_fail(": "); + print_prefixes("failed\n"); + } +} + +void test_pass(void) +{ + if (verbose) { + ksft_test_result_pass(": "); + print_prefixes("passed\n"); + } +} + +void test_print(const char *fmt, ...) +{ + if (verbose) { + int saved_errno = errno; + va_list args; + + va_start(args, fmt); + errno = saved_errno; + vprintf(fmt, args); + va_end(args); + } +} + +void prefix_reset(void) +{ + memset(prefixes, 0, PREFIXES_MAX * sizeof(char *)); + nr_prefixes = 0; +} + +void prefix_push(const char *prefix) +{ + assert(nr_prefixes < PREFIXES_MAX); + prefixes[nr_prefixes] = prefix; + nr_prefixes++; +} + +void prefix_pop(void) +{ + if (nr_prefixes > 0) { + prefixes[nr_prefixes - 1] = 0; + nr_prefixes--; + } +} diff --git a/tools/testing/memblock/tests/common.h b/tools/testing/memblock/tests/common.h index 619054d03219..3e7f23d341d7 100644 --- a/tools/testing/memblock/tests/common.h +++ b/tools/testing/memblock/tests/common.h @@ -7,9 +7,49 @@ #include <linux/types.h> #include <linux/memblock.h> #include <linux/sizes.h> +#include <linux/printk.h> +#include <../selftests/kselftest.h> #define MEM_SIZE SZ_16K +/** + * ASSERT_EQ(): + * Check the condition + * @_expected == @_seen + * If false, print failed test message (if in VERBOSE mode) and then assert + */ +#define ASSERT_EQ(_expected, _seen) do { \ + if ((_expected) != (_seen)) \ + test_fail(); \ + assert((_expected) == (_seen)); \ +} while (0) + +/** + * ASSERT_NE(): + * Check the condition + * @_expected != @_seen + * If false, print failed test message (if in VERBOSE mode) and then assert + */ +#define ASSERT_NE(_expected, _seen) do { \ + if ((_expected) == (_seen)) \ + test_fail(); \ + assert((_expected) != (_seen)); \ +} while (0) + +/** + * ASSERT_LT(): + * Check the condition + * @_expected < @_seen + * If false, print failed test message (if in VERBOSE mode) and then assert + */ +#define ASSERT_LT(_expected, _seen) do { \ + if ((_expected) >= (_seen)) \ + test_fail(); \ + assert((_expected) < (_seen)); \ +} while (0) + +#define PREFIX_PUSH() prefix_push(__func__) + /* * Available memory registered with memblock needs to be valid for allocs * test to run. This is a convenience wrapper for memory allocated in @@ -30,5 +70,19 @@ void reset_memblock_attributes(void); void setup_memblock(void); void dummy_physical_memory_init(void); void dummy_physical_memory_cleanup(void); +void parse_args(int argc, char **argv); + +void test_fail(void); +void test_pass(void); +void test_print(const char *fmt, ...); +void prefix_reset(void); +void prefix_push(const char *prefix); +void prefix_pop(void); + +static inline void test_pass_pop(void) +{ + test_pass(); + prefix_pop(); +} #endif diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index a33874b081b6..e89685bd587c 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -28,6 +28,7 @@ #include "bpf_iter_test_kern6.skel.h" #include "bpf_iter_bpf_link.skel.h" #include "bpf_iter_ksym.skel.h" +#include "bpf_iter_sockmap.skel.h" static int duration; @@ -67,6 +68,50 @@ free_link: bpf_link__destroy(link); } +static void do_read_map_iter_fd(struct bpf_object_skeleton **skel, struct bpf_program *prog, + struct bpf_map *map) +{ + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo; + struct bpf_link *link; + char buf[16] = {}; + int iter_fd, len; + + memset(&linfo, 0, sizeof(linfo)); + linfo.map.map_fd = bpf_map__fd(map); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + link = bpf_program__attach_iter(prog, &opts); + if (!ASSERT_OK_PTR(link, "attach_map_iter")) + return; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_GE(iter_fd, 0, "create_map_iter")) { + bpf_link__destroy(link); + return; + } + + /* Close link and map fd prematurely */ + bpf_link__destroy(link); + bpf_object__destroy_skeleton(*skel); + *skel = NULL; + + /* Try to let map free work to run first if map is freed */ + usleep(100); + /* Memory used by both sock map and sock local storage map are + * freed after two synchronize_rcu() calls, so wait for it + */ + kern_sync_rcu(); + kern_sync_rcu(); + + /* Read after both map fd and link fd are closed */ + while ((len = read(iter_fd, buf, sizeof(buf))) > 0) + ; + ASSERT_GE(len, 0, "read_iterator"); + + close(iter_fd); +} + static int read_fd_into_buffer(int fd, char *buf, int size) { int bufleft = size; @@ -634,6 +679,12 @@ static void test_bpf_hash_map(void) goto out; } + /* Sleepable program is prohibited for hash map iterator */ + linfo.map.map_fd = map_fd; + link = bpf_program__attach_iter(skel->progs.sleepable_dummy_dump, &opts); + if (!ASSERT_ERR_PTR(link, "attach_sleepable_prog_to_iter")) + goto out; + linfo.map.map_fd = map_fd; link = bpf_program__attach_iter(skel->progs.dump_bpf_hash_map, &opts); if (!ASSERT_OK_PTR(link, "attach_iter")) @@ -827,6 +878,20 @@ out: bpf_iter_bpf_array_map__destroy(skel); } +static void test_bpf_array_map_iter_fd(void) +{ + struct bpf_iter_bpf_array_map *skel; + + skel = bpf_iter_bpf_array_map__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_array_map__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.dump_bpf_array_map, + skel->maps.arraymap1); + + bpf_iter_bpf_array_map__destroy(skel); +} + static void test_bpf_percpu_array_map(void) { DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); @@ -1009,6 +1074,20 @@ out: bpf_iter_bpf_sk_storage_helpers__destroy(skel); } +static void test_bpf_sk_stoarge_map_iter_fd(void) +{ + struct bpf_iter_bpf_sk_storage_map *skel; + + skel = bpf_iter_bpf_sk_storage_map__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_sk_storage_map__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.rw_bpf_sk_storage_map, + skel->maps.sk_stg_map); + + bpf_iter_bpf_sk_storage_map__destroy(skel); +} + static void test_bpf_sk_storage_map(void) { DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); @@ -1044,7 +1123,15 @@ static void test_bpf_sk_storage_map(void) linfo.map.map_fd = map_fd; opts.link_info = &linfo; opts.link_info_len = sizeof(linfo); - link = bpf_program__attach_iter(skel->progs.dump_bpf_sk_storage_map, &opts); + link = bpf_program__attach_iter(skel->progs.oob_write_bpf_sk_storage_map, &opts); + err = libbpf_get_error(link); + if (!ASSERT_EQ(err, -EACCES, "attach_oob_write_iter")) { + if (!err) + bpf_link__destroy(link); + goto out; + } + + link = bpf_program__attach_iter(skel->progs.rw_bpf_sk_storage_map, &opts); if (!ASSERT_OK_PTR(link, "attach_iter")) goto out; @@ -1052,6 +1139,7 @@ static void test_bpf_sk_storage_map(void) if (!ASSERT_GE(iter_fd, 0, "create_iter")) goto free_link; + skel->bss->to_add_val = time(NULL); /* do some tests */ while ((len = read(iter_fd, buf, sizeof(buf))) > 0) ; @@ -1065,6 +1153,13 @@ static void test_bpf_sk_storage_map(void) if (!ASSERT_EQ(skel->bss->val_sum, expected_val, "val_sum")) goto close_iter; + for (i = 0; i < num_sockets; i++) { + err = bpf_map_lookup_elem(map_fd, &sock_fd[i], &val); + if (!ASSERT_OK(err, "map_lookup") || + !ASSERT_EQ(val, i + 1 + skel->bss->to_add_val, "check_map_value")) + break; + } + close_iter: close(iter_fd); free_link: @@ -1217,6 +1312,19 @@ out: bpf_iter_task_vma__destroy(skel); } +void test_bpf_sockmap_map_iter_fd(void) +{ + struct bpf_iter_sockmap *skel; + + skel = bpf_iter_sockmap__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_sockmap__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.copy, skel->maps.sockmap); + + bpf_iter_sockmap__destroy(skel); +} + void test_bpf_iter(void) { if (test__start_subtest("btf_id_or_null")) @@ -1267,10 +1375,14 @@ void test_bpf_iter(void) test_bpf_percpu_hash_map(); if (test__start_subtest("bpf_array_map")) test_bpf_array_map(); + if (test__start_subtest("bpf_array_map_iter_fd")) + test_bpf_array_map_iter_fd(); if (test__start_subtest("bpf_percpu_array_map")) test_bpf_percpu_array_map(); if (test__start_subtest("bpf_sk_storage_map")) test_bpf_sk_storage_map(); + if (test__start_subtest("bpf_sk_storage_map_iter_fd")) + test_bpf_sk_stoarge_map_iter_fd(); if (test__start_subtest("bpf_sk_storage_delete")) test_bpf_sk_storage_delete(); if (test__start_subtest("bpf_sk_storage_get")) @@ -1283,4 +1395,6 @@ void test_bpf_iter(void) test_link_iter(); if (test__start_subtest("ksym")) test_ksym_iter(); + if (test__start_subtest("bpf_sockmap_map_iter_fd")) + test_bpf_sockmap_map_iter_fd(); } diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c index 02bb8cbf9194..da860b07abb5 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c @@ -3,6 +3,7 @@ #include <test_progs.h> #include <network_helpers.h> #include <bpf/btf.h> +#include "bind4_prog.skel.h" typedef int (*test_cb)(struct bpf_object *obj); @@ -407,6 +408,98 @@ static void test_func_replace_global_func(void) prog_name, false, NULL); } +static int find_prog_btf_id(const char *name, __u32 attach_prog_fd) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + struct btf *btf; + int ret; + + ret = bpf_obj_get_info_by_fd(attach_prog_fd, &info, &info_len); + if (ret) + return ret; + + if (!info.btf_id) + return -EINVAL; + + btf = btf__load_from_kernel_by_id(info.btf_id); + ret = libbpf_get_error(btf); + if (ret) + return ret; + + ret = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); + btf__free(btf); + return ret; +} + +static int load_fentry(int attach_prog_fd, int attach_btf_id) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_FENTRY, + .attach_prog_fd = attach_prog_fd, + .attach_btf_id = attach_btf_id, + ); + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + + return bpf_prog_load(BPF_PROG_TYPE_TRACING, + "bind4_fentry", + "GPL", + insns, + ARRAY_SIZE(insns), + &opts); +} + +static void test_fentry_to_cgroup_bpf(void) +{ + struct bind4_prog *skel = NULL; + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + int cgroup_fd = -1; + int fentry_fd = -1; + int btf_id; + + cgroup_fd = test__join_cgroup("/fentry_to_cgroup_bpf"); + if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd")) + return; + + skel = bind4_prog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel")) + goto cleanup; + + skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd); + if (!ASSERT_OK_PTR(skel->links.bind_v4_prog, "bpf_program__attach_cgroup")) + goto cleanup; + + btf_id = find_prog_btf_id("bind_v4_prog", bpf_program__fd(skel->progs.bind_v4_prog)); + if (!ASSERT_GE(btf_id, 0, "find_prog_btf_id")) + goto cleanup; + + fentry_fd = load_fentry(bpf_program__fd(skel->progs.bind_v4_prog), btf_id); + if (!ASSERT_GE(fentry_fd, 0, "load_fentry")) + goto cleanup; + + /* Make sure bpf_obj_get_info_by_fd works correctly when attaching + * to another BPF program. + */ + + ASSERT_OK(bpf_obj_get_info_by_fd(fentry_fd, &info, &info_len), + "bpf_obj_get_info_by_fd"); + + ASSERT_EQ(info.btf_id, 0, "info.btf_id"); + ASSERT_EQ(info.attach_btf_id, btf_id, "info.attach_btf_id"); + ASSERT_GT(info.attach_btf_obj_id, 0, "info.attach_btf_obj_id"); + +cleanup: + if (cgroup_fd >= 0) + close(cgroup_fd); + if (fentry_fd >= 0) + close(fentry_fd); + bind4_prog__destroy(skel); +} + /* NOTE: affect other tests, must run in serial mode */ void serial_test_fexit_bpf2bpf(void) { @@ -430,4 +523,6 @@ void serial_test_fexit_bpf2bpf(void) test_fmod_ret_freplace(); if (test__start_subtest("func_replace_global_func")) test_func_replace_global_func(); + if (test__start_subtest("fentry_to_cgroup_bpf")) + test_fentry_to_cgroup_bpf(); } diff --git a/tools/testing/selftests/bpf/prog_tests/lru_bug.c b/tools/testing/selftests/bpf/prog_tests/lru_bug.c new file mode 100644 index 000000000000..3c7822390827 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/lru_bug.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> + +#include "lru_bug.skel.h" + +void test_lru_bug(void) +{ + struct lru_bug *skel; + int ret; + + skel = lru_bug__open_and_load(); + if (!ASSERT_OK_PTR(skel, "lru_bug__open_and_load")) + return; + ret = lru_bug__attach(skel); + if (!ASSERT_OK(ret, "lru_bug__attach")) + goto end; + usleep(1); + ASSERT_OK(skel->data->result, "prealloc_lru_pop doesn't call check_and_init_map_value"); +end: + lru_bug__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c index 0aa3cd34cbe3..d7a69217fb68 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c @@ -112,3 +112,12 @@ int dump_bpf_hash_map(struct bpf_iter__bpf_map_elem *ctx) return 0; } + +SEC("iter.s/bpf_map_elem") +int sleepable_dummy_dump(struct bpf_iter__bpf_map_elem *ctx) +{ + if (ctx->meta->seq_num == 0) + BPF_SEQ_PRINTF(ctx->meta->seq, "map dump starts\n"); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c index 6b70ccaba301..c7b8e006b171 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c @@ -16,19 +16,37 @@ struct { __u32 val_sum = 0; __u32 ipv6_sk_count = 0; +__u32 to_add_val = 0; SEC("iter/bpf_sk_storage_map") -int dump_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) +int rw_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) { struct sock *sk = ctx->sk; __u32 *val = ctx->value; - if (sk == (void *)0 || val == (void *)0) + if (sk == NULL || val == NULL) return 0; if (sk->sk_family == AF_INET6) ipv6_sk_count++; val_sum += *val; + + *val += to_add_val; + + return 0; +} + +SEC("iter/bpf_sk_storage_map") +int oob_write_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) +{ + struct sock *sk = ctx->sk; + __u32 *val = ctx->value; + + if (sk == NULL || val == NULL) + return 0; + + *(val + 1) = 0xdeadbeef; + return 0; } diff --git a/tools/testing/selftests/bpf/progs/lru_bug.c b/tools/testing/selftests/bpf/progs/lru_bug.c new file mode 100644 index 000000000000..687081a724b3 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lru_bug.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> + +struct map_value { + struct task_struct __kptr *ptr; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct map_value); +} lru_map SEC(".maps"); + +int pid = 0; +int result = 1; + +SEC("fentry/bpf_ktime_get_ns") +int printk(void *ctx) +{ + struct map_value v = {}; + + if (pid == bpf_get_current_task_btf()->pid) + bpf_map_update_elem(&lru_map, &(int){0}, &v, 0); + return 0; +} + +SEC("fentry/do_nanosleep") +int nanosleep(void *ctx) +{ + struct map_value val = {}, *v; + struct task_struct *current; + + bpf_map_update_elem(&lru_map, &(int){0}, &val, 0); + v = bpf_map_lookup_elem(&lru_map, &(int){0}); + if (!v) + return 0; + bpf_map_delete_elem(&lru_map, &(int){0}); + current = bpf_get_current_task_btf(); + v->ptr = current; + pid = current->pid; + bpf_ktime_get_ns(); + result = !v->ptr; + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index c7f47429d6cd..4c122f1b1737 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -4,6 +4,8 @@ include ../../../build/Build.include all: top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) # For cross-builds to work, UNAME_M has to map to ARCH and arch specific # directories and targets in this Makefile. "uname -m" doesn't map to @@ -197,7 +199,8 @@ endif CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \ -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \ - -I$(<D) -Iinclude/$(UNAME_M) -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES) + -I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \ + $(KHDR_INCLUDES) no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie) @@ -206,7 +209,7 @@ no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ pgste-option = $(call try-run, echo 'int main() { return 0; }' | \ $(CC) -Werror -Wl$(comma)--s390-pgste -x c - -o "$$TMP",-Wl$(comma)--s390-pgste) - +LDLIBS += -ldl LDFLAGS += -pthread $(no-pie-option) $(pgste-option) # After inclusion, $(OUTPUT) is defined and diff --git a/tools/testing/selftests/kvm/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c index a54d4d05a058..fac248a43666 100644 --- a/tools/testing/selftests/kvm/rseq_test.c +++ b/tools/testing/selftests/kvm/rseq_test.c @@ -20,15 +20,7 @@ #include "processor.h" #include "test_util.h" -static __thread volatile struct rseq __rseq = { - .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, -}; - -/* - * Use an arbitrary, bogus signature for configuring rseq, this test does not - * actually enter an rseq critical section. - */ -#define RSEQ_SIG 0xdeadbeef +#include "../rseq/rseq.c" /* * Any bug related to task migration is likely to be timing-dependent; perform @@ -49,12 +41,16 @@ static void guest_code(void) GUEST_SYNC(0); } -static void sys_rseq(int flags) +/* + * We have to perform direct system call for getcpu() because it's + * not available until glic 2.29. + */ +static void sys_getcpu(unsigned *cpu) { int r; - r = syscall(__NR_rseq, &__rseq, sizeof(__rseq), flags, RSEQ_SIG); - TEST_ASSERT(!r, "rseq failed, errno = %d (%s)", errno, strerror(errno)); + r = syscall(__NR_getcpu, cpu, NULL, NULL); + TEST_ASSERT(!r, "getcpu failed, errno = %d (%s)", errno, strerror(errno)); } static int next_cpu(int cpu) @@ -101,7 +97,7 @@ static void *migration_worker(void *__rseq_tid) atomic_inc(&seq_cnt); /* - * Ensure the odd count is visible while sched_getcpu() isn't + * Ensure the odd count is visible while getcpu() isn't * stable, i.e. while changing affinity is in-progress. */ smp_wmb(); @@ -142,10 +138,10 @@ static void *migration_worker(void *__rseq_tid) * check completes. * * 3. To ensure the read-side makes efficient forward progress, - * e.g. if sched_getcpu() involves a syscall. Stalling the - * read-side means the test will spend more time waiting for - * sched_getcpu() to stabilize and less time trying to hit - * the timing-dependent bug. + * e.g. if getcpu() involves a syscall. Stalling the read-side + * means the test will spend more time waiting for getcpu() + * to stabilize and less time trying to hit the timing-dependent + * bug. * * Because any bug in this area is likely to be timing-dependent, * run with a range of delays at 1us intervals from 1us to 10us @@ -218,7 +214,9 @@ int main(int argc, char *argv[]) calc_min_max_cpu(); - sys_rseq(0); + r = rseq_register_current_thread(); + TEST_ASSERT(!r, "rseq_register_current_thread failed, errno = %d (%s)", + errno, strerror(errno)); /* * Create and run a dummy VM that immediately exits to userspace via @@ -238,9 +236,9 @@ int main(int argc, char *argv[]) /* * Verify rseq's CPU matches sched's CPU. Ensure migration - * doesn't occur between sched_getcpu() and reading the rseq - * cpu_id by rereading both if the sequence count changes, or - * if the count is odd (migration in-progress). + * doesn't occur between getcpu() and reading the rseq cpu_id + * by rereading both if the sequence count changes, or if the + * count is odd (migration in-progress). */ do { /* @@ -250,13 +248,13 @@ int main(int argc, char *argv[]) snapshot = atomic_read(&seq_cnt) & ~1; /* - * Ensure reading sched_getcpu() and rseq.cpu_id - * complete in a single "no migration" window, i.e. are - * not reordered across the seq_cnt reads. + * Ensure calling getcpu() and reading rseq.cpu_id complete + * in a single "no migration" window, i.e. are not reordered + * across the seq_cnt reads. */ smp_rmb(); - cpu = sched_getcpu(); - rseq_cpu = READ_ONCE(__rseq.cpu_id); + sys_getcpu(&cpu); + rseq_cpu = rseq_current_cpu_raw(); smp_rmb(); } while (snapshot != atomic_read(&seq_cnt)); @@ -267,9 +265,9 @@ int main(int argc, char *argv[]) /* * Sanity check that the test was able to enter the guest a reasonable * number of times, e.g. didn't get stalled too often/long waiting for - * sched_getcpu() to stabilize. A 2:1 migration:KVM_RUN ratio is a - * fairly conservative ratio on x86-64, which can do _more_ KVM_RUNs - * than migrations given the 1us+ delay in the migration task. + * getcpu() to stabilize. A 2:1 migration:KVM_RUN ratio is a fairly + * conservative ratio on x86-64, which can do _more_ KVM_RUNs than + * migrations given the 1us+ delay in the migration task. */ TEST_ASSERT(i > (NR_TASK_MIGRATIONS / 2), "Only performed %d KVM_RUNs, task stalled too much?\n", i); @@ -278,7 +276,7 @@ int main(int argc, char *argv[]) kvm_vm_free(vm); - sys_rseq(RSEQ_FLAG_UNREGISTER); + rseq_unregister_current_thread(); return 0; } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c index 6ec901dab61e..069589c52f41 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c @@ -59,6 +59,7 @@ int main(int argc, char *argv[]) int ret; union cpuid10_eax eax; union perf_capabilities host_cap; + uint64_t val; host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES); host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT); @@ -91,11 +92,17 @@ int main(int argc, char *argv[]) vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.lbr_format); ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format); - /* testcase 3, check invalid LBR format is rejected */ - /* Note, on Arch LBR capable platforms, LBR_FMT in perf capability msr is 0x3f, - * to avoid the failure, use a true invalid format 0x30 for the test. */ - ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0x30); - TEST_ASSERT(ret == 0, "Bad PERF_CAPABILITIES didn't fail."); + /* + * Testcase 3, check that an "invalid" LBR format is rejected. Only an + * exact match of the host's format (and 0/disabled) is allowed. + */ + for (val = 1; val <= PMU_CAP_LBR_FMT; val++) { + if (val == (host_cap.capabilities & PMU_CAP_LBR_FMT)) + continue; + + ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val); + TEST_ASSERT(!ret, "Bad LBR FMT = 0x%lx didn't fail", val); + } printf("Completed perf capability tests.\n"); kvm_vm_free(vm); diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 892306bdb47d..0e5751af6247 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -38,4 +38,5 @@ ioam6_parser toeplitz tun cmsg_sender -unix_connect
\ No newline at end of file +unix_connect +tap
\ No newline at end of file diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index e2dfef8b78a7..c0ee2955fe54 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -57,7 +57,7 @@ TEST_GEN_FILES += ipsec TEST_GEN_FILES += ioam6_parser TEST_GEN_FILES += gro TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun +TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun tap TEST_GEN_FILES += toeplitz TEST_GEN_FILES += cmsg_sender TEST_GEN_FILES += stress_reuseport_listen diff --git a/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh index a15d21dc035a..56eb83d1a3bd 100755 --- a/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh @@ -181,37 +181,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:4::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:4::2-2001:db8:4::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:4::2-2001:db8:4::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -226,13 +232,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:4::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:4::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh index a73f52efcb6c..0446db9c6f74 100755 --- a/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh @@ -276,37 +276,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -321,13 +327,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh index 8fea2c2e0b25..d40183b4eccc 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh @@ -278,37 +278,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -323,13 +329,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index e2ea6c126c99..24d4e9cb617e 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -553,6 +553,18 @@ static void set_nonblock(int fd, bool nonblock) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } +static void shut_wr(int fd) +{ + /* Close our write side, ev. give some time + * for address notification and/or checking + * the current status + */ + if (cfg_wait) + usleep(cfg_wait); + + shutdown(fd, SHUT_WR); +} + static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after_out) { struct pollfd fds = { @@ -630,14 +642,7 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after /* ... and peer also closed already */ break; - /* ... but we still receive. - * Close our write side, ev. give some time - * for address notification and/or checking - * the current status - */ - if (cfg_wait) - usleep(cfg_wait); - shutdown(peerfd, SHUT_WR); + shut_wr(peerfd); } else { if (errno == EINTR) continue; @@ -767,7 +772,7 @@ static int copyfd_io_mmap(int infd, int peerfd, int outfd, if (err) return err; - shutdown(peerfd, SHUT_WR); + shut_wr(peerfd); err = do_recvfile(peerfd, outfd); *in_closed_after_out = true; @@ -791,6 +796,9 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd, err = do_sendfile(infd, peerfd, size); if (err) return err; + + shut_wr(peerfd); + err = do_recvfile(peerfd, outfd); *in_closed_after_out = true; } diff --git a/tools/testing/selftests/net/tap.c b/tools/testing/selftests/net/tap.c new file mode 100644 index 000000000000..247c3b3ac1c9 --- /dev/null +++ b/tools/testing/selftests/net/tap.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/if.h> +#include <linux/if_tun.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/virtio_net.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include "../kselftest_harness.h" + +static const char param_dev_tap_name[] = "xmacvtap0"; +static const char param_dev_dummy_name[] = "xdummy0"; +static unsigned char param_hwaddr_src[] = { 0x00, 0xfe, 0x98, 0x14, 0x22, 0x42 }; +static unsigned char param_hwaddr_dest[] = { + 0x00, 0xfe, 0x98, 0x94, 0xd2, 0x43 +}; + +#define MAX_RTNL_PAYLOAD (2048) +#define PKT_DATA 0xCB +#define TEST_PACKET_SZ (sizeof(struct virtio_net_hdr) + ETH_HLEN + ETH_MAX_MTU) + +static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type, + unsigned short len) +{ + struct rtattr *rta = + (struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len); + return rta; +} + +static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type) +{ + return rtattr_add(nh, type, 0); +} + +static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) +{ + uint8_t *end = (uint8_t *)nh + nh->nlmsg_len; + + attr->rta_len = end - (uint8_t *)attr; +} + +static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, + const char *s) +{ + struct rtattr *rta = rtattr_add(nh, type, strlen(s)); + + memcpy(RTA_DATA(rta), s, strlen(s)); + return rta; +} + +static struct rtattr *rtattr_add_strsz(struct nlmsghdr *nh, unsigned short type, + const char *s) +{ + struct rtattr *rta = rtattr_add(nh, type, strlen(s) + 1); + + strcpy(RTA_DATA(rta), s); + return rta; +} + +static struct rtattr *rtattr_add_any(struct nlmsghdr *nh, unsigned short type, + const void *arr, size_t len) +{ + struct rtattr *rta = rtattr_add(nh, type, len); + + memcpy(RTA_DATA(rta), arr, len); + return rta; +} + +static int dev_create(const char *dev, const char *link_type, + int (*fill_rtattr)(struct nlmsghdr *nh), + int (*fill_info_data)(struct nlmsghdr *nh)) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + unsigned char data[MAX_RTNL_PAYLOAD]; + } req; + struct rtattr *link_info, *info_data; + int ret, rtnl; + + rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (rtnl < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + return 1; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + req.nh.nlmsg_type = RTM_NEWLINK; + + req.info.ifi_family = AF_UNSPEC; + req.info.ifi_type = 1; + req.info.ifi_index = 0; + req.info.ifi_flags = IFF_BROADCAST | IFF_UP; + req.info.ifi_change = 0xffffffff; + + rtattr_add_str(&req.nh, IFLA_IFNAME, dev); + + if (fill_rtattr) { + ret = fill_rtattr(&req.nh); + if (ret) + return ret; + } + + link_info = rtattr_begin(&req.nh, IFLA_LINKINFO); + + rtattr_add_strsz(&req.nh, IFLA_INFO_KIND, link_type); + + if (fill_info_data) { + info_data = rtattr_begin(&req.nh, IFLA_INFO_DATA); + ret = fill_info_data(&req.nh); + if (ret) + return ret; + rtattr_end(&req.nh, info_data); + } + + rtattr_end(&req.nh, link_info); + + ret = send(rtnl, &req, req.nh.nlmsg_len, 0); + if (ret < 0) + fprintf(stderr, "%s: send %s\n", __func__, strerror(errno)); + ret = (unsigned int)ret != req.nh.nlmsg_len; + + close(rtnl); + return ret; +} + +static int dev_delete(const char *dev) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + unsigned char data[MAX_RTNL_PAYLOAD]; + } req; + int ret, rtnl; + + rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (rtnl < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + return 1; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_DELLINK; + + req.info.ifi_family = AF_UNSPEC; + + rtattr_add_str(&req.nh, IFLA_IFNAME, dev); + + ret = send(rtnl, &req, req.nh.nlmsg_len, 0); + if (ret < 0) + fprintf(stderr, "%s: send %s\n", __func__, strerror(errno)); + + ret = (unsigned int)ret != req.nh.nlmsg_len; + + close(rtnl); + return ret; +} + +static int macvtap_fill_rtattr(struct nlmsghdr *nh) +{ + int ifindex; + + ifindex = if_nametoindex(param_dev_dummy_name); + if (ifindex == 0) { + fprintf(stderr, "%s: ifindex %s\n", __func__, strerror(errno)); + return -errno; + } + + rtattr_add_any(nh, IFLA_LINK, &ifindex, sizeof(ifindex)); + rtattr_add_any(nh, IFLA_ADDRESS, param_hwaddr_src, ETH_ALEN); + + return 0; +} + +static int opentap(const char *devname) +{ + int ifindex; + char buf[256]; + int fd; + struct ifreq ifr; + + ifindex = if_nametoindex(devname); + if (ifindex == 0) { + fprintf(stderr, "%s: ifindex %s\n", __func__, strerror(errno)); + return -errno; + } + + sprintf(buf, "/dev/tap%d", ifindex); + fd = open(buf, O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "%s: open %s\n", __func__, strerror(errno)); + return -errno; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR | IFF_MULTI_QUEUE; + if (ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr)) < 0) + return -errno; + return fd; +} + +size_t build_eth(uint8_t *buf, uint16_t proto) +{ + struct ethhdr *eth = (struct ethhdr *)buf; + + eth->h_proto = htons(proto); + memcpy(eth->h_source, param_hwaddr_src, ETH_ALEN); + memcpy(eth->h_dest, param_hwaddr_dest, ETH_ALEN); + + return ETH_HLEN; +} + +static uint32_t add_csum(const uint8_t *buf, int len) +{ + uint32_t sum = 0; + uint16_t *sbuf = (uint16_t *)buf; + + while (len > 1) { + sum += *sbuf++; + len -= 2; + } + + if (len) + sum += *(uint8_t *)sbuf; + + return sum; +} + +static uint16_t finish_ip_csum(uint32_t sum) +{ + uint16_t lo = sum & 0xffff; + uint16_t hi = sum >> 16; + + return ~(lo + hi); + +} + +static uint16_t build_ip_csum(const uint8_t *buf, int len, + uint32_t sum) +{ + sum += add_csum(buf, len); + return finish_ip_csum(sum); +} + +static int build_ipv4_header(uint8_t *buf, int payload_len) +{ + struct iphdr *iph = (struct iphdr *)buf; + + iph->ihl = 5; + iph->version = 4; + iph->ttl = 8; + iph->tot_len = + htons(sizeof(*iph) + sizeof(struct udphdr) + payload_len); + iph->id = htons(1337); + iph->protocol = IPPROTO_UDP; + iph->saddr = htonl((172 << 24) | (17 << 16) | 2); + iph->daddr = htonl((172 << 24) | (17 << 16) | 1); + iph->check = build_ip_csum(buf, iph->ihl << 2, 0); + + return iph->ihl << 2; +} + +static int build_udp_packet(uint8_t *buf, int payload_len, bool csum_off) +{ + const int ip4alen = sizeof(uint32_t); + struct udphdr *udph = (struct udphdr *)buf; + int len = sizeof(*udph) + payload_len; + uint32_t sum = 0; + + udph->source = htons(22); + udph->dest = htons(58822); + udph->len = htons(len); + + memset(buf + sizeof(struct udphdr), PKT_DATA, payload_len); + + sum = add_csum(buf - 2 * ip4alen, 2 * ip4alen); + sum += htons(IPPROTO_UDP) + udph->len; + + if (!csum_off) + sum += add_csum(buf, len); + + udph->check = finish_ip_csum(sum); + + return sizeof(*udph) + payload_len; +} + +size_t build_test_packet_valid_udp_gso(uint8_t *buf, size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->hdr_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr); + vh->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + vh->csum_start = ETH_HLEN + sizeof(struct iphdr); + vh->csum_offset = __builtin_offsetof(struct udphdr, check); + vh->gso_type = VIRTIO_NET_HDR_GSO_UDP; + vh->gso_size = ETH_DATA_LEN - sizeof(struct iphdr); + cur += sizeof(*vh); + + cur += build_eth(cur, ETH_P_IP); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, true); + + return cur - buf; +} + +size_t build_test_packet_valid_udp_csum(uint8_t *buf, size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->flags = VIRTIO_NET_HDR_F_DATA_VALID; + vh->gso_type = VIRTIO_NET_HDR_GSO_NONE; + cur += sizeof(*vh); + + cur += build_eth(cur, ETH_P_IP); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, false); + + return cur - buf; +} + +size_t build_test_packet_crash_tap_invalid_eth_proto(uint8_t *buf, + size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->hdr_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr); + vh->flags = 0; + vh->gso_type = VIRTIO_NET_HDR_GSO_UDP; + vh->gso_size = ETH_DATA_LEN - sizeof(struct iphdr); + cur += sizeof(*vh); + + cur += build_eth(cur, 0); + cur += sizeof(struct iphdr) + sizeof(struct udphdr); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, true); + cur += payload_len; + + return cur - buf; +} + +FIXTURE(tap) +{ + int fd; +}; + +FIXTURE_SETUP(tap) +{ + int ret; + + ret = dev_create(param_dev_dummy_name, "dummy", NULL, NULL); + EXPECT_EQ(ret, 0); + + ret = dev_create(param_dev_tap_name, "macvtap", macvtap_fill_rtattr, + NULL); + EXPECT_EQ(ret, 0); + + self->fd = opentap(param_dev_tap_name); + ASSERT_GE(self->fd, 0); +} + +FIXTURE_TEARDOWN(tap) +{ + int ret; + + if (self->fd != -1) + close(self->fd); + + ret = dev_delete(param_dev_tap_name); + EXPECT_EQ(ret, 0); + + ret = dev_delete(param_dev_dummy_name); + EXPECT_EQ(ret, 0); +} + +TEST_F(tap, test_packet_valid_udp_gso) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_valid_udp_gso(pkt, 1021); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, off); +} + +TEST_F(tap, test_packet_valid_udp_csum) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_valid_udp_csum(pkt, 1024); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, off); +} + +TEST_F(tap, test_packet_crash_tap_invalid_eth_proto) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_crash_tap_invalid_eth_proto(pkt, 1024); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/netfilter/nft_trans_stress.sh b/tools/testing/selftests/netfilter/nft_trans_stress.sh index f1affd12c4b1..a7f62ad4f661 100755 --- a/tools/testing/selftests/netfilter/nft_trans_stress.sh +++ b/tools/testing/selftests/netfilter/nft_trans_stress.sh @@ -9,8 +9,27 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -testns=testns1 +testns=testns-$(mktemp -u "XXXXXXXX") + tables="foo bar baz quux" +global_ret=0 +eret=0 +lret=0 + +check_result() +{ + local r=$1 + local OK="PASS" + + if [ $r -ne 0 ] ;then + OK="FAIL" + global_ret=$r + fi + + echo "$OK: nft $2 test returned $r" + + eret=0 +} nft --version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -59,16 +78,66 @@ done) sleep 1 +ip netns exec "$testns" nft -f "$tmp" for i in $(seq 1 10) ; do ip netns exec "$testns" nft -f "$tmp" & done for table in $tables;do - randsleep=$((RANDOM%10)) + randsleep=$((RANDOM%2)) sleep $randsleep - ip netns exec "$testns" nft delete table inet $table 2>/dev/null + ip netns exec "$testns" nft delete table inet $table + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi done -randsleep=$((RANDOM%10)) -sleep $randsleep +check_result $eret "add/delete" + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp") | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi +done + +check_result $eret "reload" + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp" + echo "insert rule inet foo INPUT meta nftrace set 1" + echo "insert rule inet foo OUTPUT meta nftrace set 1" + ) | ip netns exec "$testns" nft -f /dev/stdin + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi + + (echo "flush ruleset"; cat "$tmp" + ) | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi +done + +check_result $eret "add/delete with nftrace enabled" + +echo "insert rule inet foo INPUT meta nftrace set 1" >> $tmp +echo "insert rule inet foo OUTPUT meta nftrace set 1" >> $tmp + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp") | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=1 + fi +done + +check_result $lret "add/delete with nftrace enabled" pkill -9 ping @@ -76,3 +145,5 @@ wait rm -f "$tmp" ip netns del "$testns" + +exit $global_ret diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c index b00b1bfd9d8e..cb1108bc9249 100644 --- a/tools/thermal/tmon/sysfs.c +++ b/tools/thermal/tmon/sysfs.c @@ -13,6 +13,7 @@ #include <stdint.h> #include <dirent.h> #include <libintl.h> +#include <limits.h> #include <ctype.h> #include <time.h> #include <syslog.h> @@ -33,9 +34,9 @@ int sysfs_set_ulong(char *path, char *filename, unsigned long val) { FILE *fd; int ret = -1; - char filepath[256]; + char filepath[PATH_MAX + 2]; /* NUL and '/' */ - snprintf(filepath, 256, "%s/%s", path, filename); + snprintf(filepath, sizeof(filepath), "%s/%s", path, filename); fd = fopen(filepath, "w"); if (!fd) { @@ -57,9 +58,9 @@ static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong) { FILE *fd; int ret = -1; - char filepath[256]; + char filepath[PATH_MAX + 2]; /* NUL and '/' */ - snprintf(filepath, 256, "%s/%s", path, filename); + snprintf(filepath, sizeof(filepath), "%s/%s", path, filename); fd = fopen(filepath, "r"); if (!fd) { @@ -76,9 +77,9 @@ static int sysfs_get_string(char *path, char *filename, char *str) { FILE *fd; int ret = -1; - char filepath[256]; + char filepath[PATH_MAX + 2]; /* NUL and '/' */ - snprintf(filepath, 256, "%s/%s", path, filename); + snprintf(filepath, sizeof(filepath), "%s/%s", path, filename); fd = fopen(filepath, "r"); if (!fd) { @@ -199,8 +200,8 @@ static int find_tzone_cdev(struct dirent *nl, char *tz_name, { unsigned long trip_instance = 0; char cdev_name_linked[256]; - char cdev_name[256]; - char cdev_trip_name[256]; + char cdev_name[PATH_MAX]; + char cdev_trip_name[PATH_MAX]; int cdev_id; if (nl->d_type == DT_LNK) { @@ -213,7 +214,8 @@ static int find_tzone_cdev(struct dirent *nl, char *tz_name, return -EINVAL; } /* find the link to real cooling device record binding */ - snprintf(cdev_name, 256, "%s/%s", tz_name, nl->d_name); + snprintf(cdev_name, sizeof(cdev_name) - 2, "%s/%s", + tz_name, nl->d_name); memset(cdev_name_linked, 0, sizeof(cdev_name_linked)); if (readlink(cdev_name, cdev_name_linked, sizeof(cdev_name_linked) - 1) != -1) { @@ -226,8 +228,8 @@ static int find_tzone_cdev(struct dirent *nl, char *tz_name, /* find the trip point in which the cdev is binded to * in this tzone */ - snprintf(cdev_trip_name, 256, "%s%s", nl->d_name, - "_trip_point"); + snprintf(cdev_trip_name, sizeof(cdev_trip_name) - 1, + "%s%s", nl->d_name, "_trip_point"); sysfs_get_ulong(tz_name, cdev_trip_name, &trip_instance); /* validate trip point range, e.g. trip could return -1 diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 32896c845ffe..515dfe9d3bcf 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -484,6 +484,10 @@ static void kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) vcpu->ready = false; preempt_notifier_init(&vcpu->preempt_notifier, &kvm_preempt_ops); vcpu->last_used_slot = NULL; + + /* Fill the stats id string for the vcpu */ + snprintf(vcpu->stats_id, sizeof(vcpu->stats_id), "kvm-%d/vcpu-%d", + task_pid_nr(current), id); } static void kvm_vcpu_destroy(struct kvm_vcpu *vcpu) @@ -1017,21 +1021,21 @@ static void kvm_destroy_vm_debugfs(struct kvm *kvm) } } -static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) +static int kvm_create_vm_debugfs(struct kvm *kvm, const char *fdname) { static DEFINE_MUTEX(kvm_debugfs_lock); struct dentry *dent; char dir_name[ITOA_MAX_LEN * 2]; struct kvm_stat_data *stat_data; const struct _kvm_stats_desc *pdesc; - int i, ret; + int i, ret = -ENOMEM; int kvm_debugfs_num_entries = kvm_vm_stats_header.num_desc + kvm_vcpu_stats_header.num_desc; if (!debugfs_initialized()) return 0; - snprintf(dir_name, sizeof(dir_name), "%d-%d", task_pid_nr(current), fd); + snprintf(dir_name, sizeof(dir_name), "%d-%s", task_pid_nr(current), fdname); mutex_lock(&kvm_debugfs_lock); dent = debugfs_lookup(dir_name, kvm_debugfs_dir); if (dent) { @@ -1050,13 +1054,13 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) sizeof(*kvm->debugfs_stat_data), GFP_KERNEL_ACCOUNT); if (!kvm->debugfs_stat_data) - return -ENOMEM; + goto out_err; for (i = 0; i < kvm_vm_stats_header.num_desc; ++i) { pdesc = &kvm_vm_stats_desc[i]; stat_data = kzalloc(sizeof(*stat_data), GFP_KERNEL_ACCOUNT); if (!stat_data) - return -ENOMEM; + goto out_err; stat_data->kvm = kvm; stat_data->desc = pdesc; @@ -1071,7 +1075,7 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) pdesc = &kvm_vcpu_stats_desc[i]; stat_data = kzalloc(sizeof(*stat_data), GFP_KERNEL_ACCOUNT); if (!stat_data) - return -ENOMEM; + goto out_err; stat_data->kvm = kvm; stat_data->desc = pdesc; @@ -1083,12 +1087,13 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) } ret = kvm_arch_create_vm_debugfs(kvm); - if (ret) { - kvm_destroy_vm_debugfs(kvm); - return i; - } + if (ret) + goto out_err; return 0; +out_err: + kvm_destroy_vm_debugfs(kvm); + return ret; } /* @@ -1119,7 +1124,7 @@ int __weak kvm_arch_create_vm_debugfs(struct kvm *kvm) return 0; } -static struct kvm *kvm_create_vm(unsigned long type) +static struct kvm *kvm_create_vm(unsigned long type, const char *fdname) { struct kvm *kvm = kvm_arch_alloc_vm(); struct kvm_memslots *slots; @@ -1155,6 +1160,9 @@ static struct kvm *kvm_create_vm(unsigned long type) */ kvm->debugfs_dentry = ERR_PTR(-ENOENT); + snprintf(kvm->stats_id, sizeof(kvm->stats_id), "kvm-%d", + task_pid_nr(current)); + if (init_srcu_struct(&kvm->srcu)) goto out_err_no_srcu; if (init_srcu_struct(&kvm->irq_srcu)) @@ -1205,7 +1213,7 @@ static struct kvm *kvm_create_vm(unsigned long type) r = kvm_arch_post_init_vm(kvm); if (r) - goto out_err; + goto out_err_mmu_notifier; mutex_lock(&kvm_lock); list_add(&kvm->vm_list, &vm_list); @@ -1221,12 +1229,18 @@ static struct kvm *kvm_create_vm(unsigned long type) */ if (!try_module_get(kvm_chardev_ops.owner)) { r = -ENODEV; - goto out_err; + goto out_err_mmu_notifier; } + r = kvm_create_vm_debugfs(kvm, fdname); + if (r) + goto out_err; + return kvm; out_err: + module_put(kvm_chardev_ops.owner); +out_err_mmu_notifier: #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) if (kvm->mmu_notifier.ops) mmu_notifier_unregister(&kvm->mmu_notifier, current->mm); @@ -3916,10 +3930,6 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) if (r) goto unlock_vcpu_destroy; - /* Fill the stats id string for the vcpu */ - snprintf(vcpu->stats_id, sizeof(vcpu->stats_id), "kvm-%d/vcpu-%d", - task_pid_nr(current), id); - /* Now it's all set up, let userspace reach it */ kvm_get_kvm(kvm); r = create_vcpu_fd(vcpu); @@ -4886,28 +4896,30 @@ EXPORT_SYMBOL_GPL(file_is_kvm); static int kvm_dev_ioctl_create_vm(unsigned long type) { - int r; + char fdname[ITOA_MAX_LEN + 1]; + int r, fd; struct kvm *kvm; struct file *file; - kvm = kvm_create_vm(type); - if (IS_ERR(kvm)) - return PTR_ERR(kvm); + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + snprintf(fdname, sizeof(fdname), "%d", fd); + + kvm = kvm_create_vm(type, fdname); + if (IS_ERR(kvm)) { + r = PTR_ERR(kvm); + goto put_fd; + } + #ifdef CONFIG_KVM_MMIO r = kvm_coalesced_mmio_init(kvm); if (r < 0) goto put_kvm; #endif - r = get_unused_fd_flags(O_CLOEXEC); - if (r < 0) - goto put_kvm; - - snprintf(kvm->stats_id, sizeof(kvm->stats_id), - "kvm-%d", task_pid_nr(current)); - file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR); if (IS_ERR(file)) { - put_unused_fd(r); r = PTR_ERR(file); goto put_kvm; } @@ -4918,18 +4930,15 @@ static int kvm_dev_ioctl_create_vm(unsigned long type) * cases it will be called by the final fput(file) and will take * care of doing kvm_put_kvm(kvm). */ - if (kvm_create_vm_debugfs(kvm, r) < 0) { - put_unused_fd(r); - fput(file); - return -ENOMEM; - } kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm); - fd_install(r, file); - return r; + fd_install(fd, file); + return fd; put_kvm: kvm_put_kvm(kvm); +put_fd: + put_unused_fd(fd); return r; } |